[DRE-commits] r2142 - in packages: . libdb4.3-ruby libdb4.3-ruby/branches libdb4.3-ruby/branches/upstream libdb4.3-ruby/branches/upstream/current libdb4.3-ruby/branches/upstream/current/src

lucas at alioth.debian.org lucas at alioth.debian.org
Sat Dec 8 12:00:49 UTC 2007


Author: lucas
Date: 2007-12-08 12:00:45 +0000 (Sat, 08 Dec 2007)
New Revision: 2142

Added:
   packages/libdb4.3-ruby/
   packages/libdb4.3-ruby/branches/
   packages/libdb4.3-ruby/branches/upstream/
   packages/libdb4.3-ruby/branches/upstream/current/
   packages/libdb4.3-ruby/branches/upstream/current/src/
   packages/libdb4.3-ruby/branches/upstream/current/src/common.c
   packages/libdb4.3-ruby/branches/upstream/current/src/env.c
   packages/libdb4.3-ruby/branches/upstream/current/src/lock.c
   packages/libdb4.3-ruby/branches/upstream/current/src/log.c
   packages/libdb4.3-ruby/branches/upstream/current/src/recnum.c
   packages/libdb4.3-ruby/branches/upstream/current/src/sequence.c
Log:
[svn-inject] Installing original source of libdb4.3-ruby

Added: packages/libdb4.3-ruby/branches/upstream/current/src/common.c
===================================================================
--- packages/libdb4.3-ruby/branches/upstream/current/src/common.c	                        (rev 0)
+++ packages/libdb4.3-ruby/branches/upstream/current/src/common.c	2007-12-08 12:00:45 UTC (rev 2142)
@@ -0,0 +1,4230 @@
+#include "bdb.h"
+
+static ID id_bt_compare, id_bt_prefix, id_dup_compare, id_h_hash;
+#if BDB_VERSION >= 40100
+static ID id_append_recno;
+#endif
+
+#if BDB_VERSION >= 30000
+static ID id_feedback;
+#endif
+
+static void bdb_mark _((bdb_DB *));
+
+#define GetIdDb(obj, dbst) do {						  \
+    (obj) = rb_thread_local_aref(rb_thread_current(), bdb_id_current_db); \
+    if (TYPE(obj) != T_DATA ||						  \
+	RDATA(obj)->dmark != (RUBY_DATA_FUNC)bdb_mark) {		  \
+	rb_raise(bdb_eFatal, "BUG : current_db not set");		  \
+    }									  \
+    Data_Get_Struct(obj, bdb_DB, dbst);					  \
+} while (0)
+
+void
+bdb_ary_push(struct ary_st *db_ary, VALUE obj)
+{
+    if (db_ary->mark) {
+        rb_warning("db_ary in mark phase");
+        return;
+    }
+    if (db_ary->len == db_ary->total) {
+	if (db_ary->total) {
+	    REALLOC_N(db_ary->ptr, VALUE, db_ary->total + 5);
+	}
+	else {
+	    db_ary->ptr = ALLOC_N(VALUE, 5);
+	}
+	db_ary->total += 5;
+    }
+    db_ary->ptr[db_ary->len] = obj;
+    db_ary->len++;
+}
+
+void
+bdb_ary_unshift(struct ary_st *db_ary, VALUE obj)
+{
+    if (db_ary->mark) {
+        rb_warning("db_ary in mark phase");
+        return;
+    }
+    if (db_ary->len == db_ary->total) {
+	if (db_ary->total) {
+	    REALLOC_N(db_ary->ptr, VALUE, db_ary->total + 5);
+	}
+	else { 
+	    db_ary->ptr = ALLOC_N(VALUE, 5);
+	}
+	db_ary->total += 5;
+    }
+    if (db_ary->len) {
+        MEMMOVE(db_ary->ptr + 1, db_ary->ptr, VALUE, db_ary->len);
+    }
+    db_ary->len++;
+    db_ary->ptr[0] = obj;
+}
+
+VALUE
+bdb_ary_delete(struct ary_st *db_ary, VALUE val)
+{
+    int i, pos;
+
+    if (!db_ary->ptr || db_ary->mark) return Qfalse;
+    for (pos = 0; pos < db_ary->len; pos++) {
+	if (db_ary->ptr[pos] == val) {
+	    for (i = pos + 1; i < db_ary->len; i++, pos++) {
+		db_ary->ptr[pos] = db_ary->ptr[i];
+	    }
+	    db_ary->len = pos;
+	    return Qtrue;
+	}
+    }
+    return Qfalse;
+}
+
+void
+bdb_ary_mark(struct ary_st *db_ary)
+{
+    int i;
+
+    for (i = 0; i < db_ary->len; i++) {
+        rb_gc_mark(db_ary->ptr[i]);
+    }
+}
+
+VALUE
+bdb_test_dump(VALUE obj, DBT *key, VALUE a, int type_kv)
+{
+    bdb_DB *dbst;
+    int is_nil = 0;
+    VALUE tmp = a;
+
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    if (dbst->filter[type_kv]) {
+	if (FIXNUM_P(dbst->filter[type_kv])) {
+	    tmp = rb_funcall(obj, NUM2INT(dbst->filter[type_kv]), 1, a);
+	}
+	else {
+	    tmp = rb_funcall(dbst->filter[type_kv], bdb_id_call, 1, a);
+	}
+    }
+    if (dbst->marshal) {
+        if (rb_obj_is_kind_of(tmp, bdb_cDelegate)) {
+	    tmp = bdb_deleg_to_orig(tmp);
+        }
+        tmp = rb_funcall(dbst->marshal, bdb_id_dump, 1, tmp);
+        if (TYPE(tmp) != T_STRING) {
+	    rb_raise(rb_eTypeError, "dump() must return String");
+	}
+    }
+    else {
+        tmp = rb_obj_as_string(tmp);
+        if ((dbst->options & BDB_NIL) && a == Qnil) {
+            is_nil = 1;
+        }
+    }
+    key->data = StringValuePtr(tmp);
+    key->flags &= ~DB_DBT_MALLOC;
+    key->size = RSTRING(tmp)->len + is_nil;
+    return tmp;
+}
+
+VALUE
+bdb_test_ret(VALUE obj, VALUE tmp, VALUE a, int type_kv)
+{
+    bdb_DB *dbst;
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    if (dbst->marshal || a == Qnil) {
+	return a;
+    }
+    else {
+        if (dbst->filter[type_kv]) {
+            return rb_obj_as_string(a);
+        }
+        else {
+	    return tmp;
+	}
+    }
+}
+
+VALUE
+bdb_test_recno(VALUE obj, DBT *key, db_recno_t *recno, VALUE a)
+{
+    bdb_DB *dbst;
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    if (RECNUM_TYPE(dbst)) {
+        *recno = NUM2INT(a) + dbst->array_base;
+        key->data = recno;
+        key->size = sizeof(db_recno_t);
+	return a;
+    }
+    else {
+        return bdb_test_dump(obj, key, a, FILTER_KEY);
+    }
+}
+
+VALUE
+bdb_test_load(VALUE obj, DBT *a, int type_kv)
+{
+    VALUE res;
+    int i, posi;
+    bdb_DB *dbst;
+
+    posi = type_kv & ~FILTER_FREE;
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    if (dbst->marshal) {
+	res = rb_str_new(a->data, a->size);
+	if (dbst->filter[2 + posi]) {
+	    if (FIXNUM_P(dbst->filter[2 + posi])) {
+		res = rb_funcall(obj,
+                                 NUM2INT(dbst->filter[2 + posi]), 1, res);
+	    }
+	    else {
+		res = rb_funcall(dbst->filter[2 + posi], bdb_id_call, 1, res);
+	    }
+	}
+	res = rb_funcall(dbst->marshal, bdb_id_load, 1, res);
+    }
+    else {
+#if BDB_VERSION >= 30000
+	if (dbst->type == DB_QUEUE) {
+	    for (i = a->size - 1; i >= 0; i--) {
+		if (((char *)a->data)[i] != dbst->re_pad)
+		    break;
+	    }
+	    a->size = i + 1;
+	}
+#endif
+	if ((dbst->options & BDB_NIL) &&
+            a->size == 1 && ((char *)a->data)[0] == '\000') {
+	    res = Qnil;
+	}
+	else {
+            if (a->size == 0 && !(dbst->options & BDB_NIL)) {
+                res = Qnil;
+            }
+            else {
+                res = rb_tainted_str_new(a->data, a->size);
+                if (dbst->filter[2 + posi]) {
+                    if (FIXNUM_P(dbst->filter[2 + posi])) {
+                        res = rb_funcall(obj, NUM2INT(dbst->filter[2 + posi]),
+                                         1, res);
+                    }
+                    else {
+                        res = rb_funcall(dbst->filter[2 + posi],
+                                         bdb_id_call, 1, res);
+                    }
+                }
+            }
+	}
+    }
+    if ((a->flags & DB_DBT_MALLOC) && !(type_kv & FILTER_FREE)) {
+	free(a->data);
+        a->flags &= ~DB_DBT_MALLOC;
+    }
+    return res;
+}
+
+static VALUE
+test_load_dyna1(VALUE obj, DBT *key, DBT *val)
+{
+    bdb_DB *dbst;
+    VALUE del, res, tmp;
+    struct deleg_class *delegst;
+    
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    res = bdb_test_load(obj, val, FILTER_VALUE);
+    if (dbst->marshal && !SPECIAL_CONST_P(res)) {
+	del = Data_Make_Struct(bdb_cDelegate, struct deleg_class, 
+			       bdb_deleg_mark, free, delegst);
+	delegst->db = obj;
+	if (RECNUM_TYPE(dbst)) {
+	    tmp = INT2NUM((*(db_recno_t *)key->data) - dbst->array_base);
+	}
+	else {
+	    tmp = rb_str_new(key->data, key->size);
+	    if (dbst->filter[2 + FILTER_VALUE]) {
+		if (FIXNUM_P(dbst->filter[2 + FILTER_VALUE])) {
+		    tmp = rb_funcall(obj, 
+				     NUM2INT(dbst->filter[2 + FILTER_VALUE]),
+				     1, tmp);
+		}
+		else {
+		    tmp = rb_funcall(dbst->filter[2 + FILTER_VALUE],
+				     bdb_id_call, 1, tmp);
+		}
+	    }
+	    tmp = rb_funcall(dbst->marshal, bdb_id_load, 1, tmp);
+	}
+	delegst->key = tmp;
+	delegst->obj = res;
+	res = del;
+    }
+    return res;
+}
+
+static VALUE
+test_load_dyna(VALUE obj, DBT *key, DBT *val)
+{
+    VALUE res = test_load_dyna1(obj, key, val);
+    if (key->flags & DB_DBT_MALLOC) {
+	free(key->data);
+        key->flags &= ~DB_DBT_MALLOC;
+    }
+    return res;
+}
+
+static int
+#if BDB_VERSION >= 30200
+bdb_bt_compare(DB *dbbd, const DBT *a, const DBT *b)
+#else
+bdb_bt_compare(const DBT *a, const DBT *b)
+#endif
+{
+    VALUE obj, av, bv, res;
+    bdb_DB *dbst;
+
+    GetIdDb(obj, dbst);
+    av = bdb_test_load(obj, (DBT *)a, FILTER_VALUE|FILTER_FREE);
+    bv = bdb_test_load(obj, (DBT *)b, FILTER_VALUE|FILTER_FREE);
+    if (dbst->bt_compare == 0)
+	res = rb_funcall(obj, id_bt_compare, 2, av, bv);
+    else
+	res = rb_funcall(dbst->bt_compare, bdb_id_call, 2, av, bv);
+    return NUM2INT(res);
+} 
+
+static size_t
+#if BDB_VERSION >= 30200
+bdb_bt_prefix(DB *dbbd, const DBT *a, const DBT *b)
+#else
+bdb_bt_prefix(const DBT *a, const DBT *b)
+#endif
+{
+    VALUE obj, av, bv, res;
+    bdb_DB *dbst;
+
+    GetIdDb(obj, dbst);
+    av = bdb_test_load(obj, (DBT *)a, FILTER_VALUE|FILTER_FREE);
+    bv = bdb_test_load(obj, (DBT *)b, FILTER_VALUE|FILTER_FREE);
+    if (dbst->bt_prefix == 0)
+	res = rb_funcall(obj, id_bt_prefix, 2, av, bv);
+    else
+	res = rb_funcall(dbst->bt_prefix, bdb_id_call, 2, av, bv);
+    return NUM2INT(res);
+} 
+
+static int
+#if BDB_VERSION >= 30200
+bdb_dup_compare(DB *dbbd, const DBT *a, const DBT *b)
+#else
+bdb_dup_compare(const DBT *a, const DBT *b)
+#endif
+{
+    VALUE obj, av, bv, res;
+    bdb_DB *dbst;
+
+    GetIdDb(obj, dbst);
+    av = bdb_test_load(obj, (DBT *)a, FILTER_VALUE|FILTER_FREE);
+    bv = bdb_test_load(obj, (DBT *)b, FILTER_VALUE|FILTER_FREE);
+    if (dbst->dup_compare == 0)
+	res = rb_funcall(obj, id_dup_compare, 2, av, bv);
+    else
+	res = rb_funcall(dbst->dup_compare, bdb_id_call, 2, av, bv);
+    return NUM2INT(res);
+}
+
+static u_int32_t
+#if BDB_VERSION >= 30200
+bdb_h_hash(DB *dbbd, const void *bytes, u_int32_t length)
+#else
+bdb_h_hash(const void *bytes, u_int32_t length)
+#endif
+{
+    VALUE obj, st, res;
+    bdb_DB *dbst;
+
+    GetIdDb(obj, dbst);
+    st = rb_tainted_str_new((char *)bytes, length);
+    if (dbst->h_hash == 0)
+	res = rb_funcall(obj, id_h_hash, 1, st);
+    else
+	res = rb_funcall(dbst->h_hash, bdb_id_call, 1, st);
+    return NUM2UINT(res);
+}
+
+#if BDB_VERSION >= 40100
+
+static int
+bdb_append_recno(DB *dbp, DBT *data, db_recno_t recno)
+{
+    VALUE res, obj, av, rec;
+    bdb_DB *dbst;
+
+    GetIdDb(obj, dbst);
+    av = bdb_test_load(obj, data, FILTER_VALUE|FILTER_FREE);
+    rec = INT2NUM(recno - dbst->array_base);
+    if (dbst->append_recno == 0)
+	res = rb_funcall(obj, id_append_recno, 2, rec, av);
+    else
+	res = rb_funcall(dbst->append_recno, bdb_id_call, 2, rec, av);
+    if (!NIL_P(res)) {
+	bdb_test_dump(obj, data, res, FILTER_VALUE);
+    }
+    return 0;
+}
+
+#endif
+
+#if BDB_VERSION >= 30000
+
+static void
+bdb_feedback(DB *dbp, int opcode, int pct)
+{
+    VALUE obj;
+    bdb_DB *dbst;
+
+    GetIdDb(obj, dbst);
+    if (NIL_P(dbst->feedback)) {
+	return;
+    }
+    if (dbst->feedback == 0) {
+	rb_funcall(obj, id_feedback, 2, INT2NUM(opcode), INT2NUM(pct));
+    }
+    else {
+	rb_funcall(dbst->feedback, bdb_id_call, 2, INT2NUM(opcode), INT2NUM(pct));
+    }
+}
+
+#endif
+
+static VALUE
+bdb_i_options(VALUE obj, VALUE dbstobj)
+{
+    VALUE key, value;
+    char *options, *str;
+    DB *dbp;
+    bdb_DB *dbst;
+
+    Data_Get_Struct(dbstobj, bdb_DB, dbst);
+    key = rb_ary_entry(obj, 0);
+    value = rb_ary_entry(obj, 1);
+    dbp = dbst->dbp;
+    key = rb_obj_as_string(key);
+    options = StringValuePtr(key);
+    if (strcmp(options, "set_bt_minkey") == 0) {
+#if BDB_VERSION < 30000
+	dbst->dbinfo->bt_minkey = NUM2INT(value);
+#else
+        bdb_test_error(dbp->set_bt_minkey(dbp, NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_bt_compare") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->options |= BDB_BT_COMPARE;
+	dbst->bt_compare = value;
+#if BDB_VERSION < 30000
+	dbst->dbinfo->bt_compare = bdb_bt_compare;
+#else
+	bdb_test_error(dbp->set_bt_compare(dbp, bdb_bt_compare));
+#endif
+    }
+    else if (strcmp(options, "set_bt_prefix") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->options |= BDB_BT_PREFIX;
+	dbst->bt_prefix = value;
+#if BDB_VERSION < 30000
+	dbst->dbinfo->bt_prefix = bdb_bt_prefix;
+#else
+	bdb_test_error(dbp->set_bt_prefix(dbp, bdb_bt_prefix));
+#endif
+    }
+    else if (strcmp(options, "set_dup_compare") == 0) {
+#ifdef DB_DUPSORT
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->options |= BDB_DUP_COMPARE;
+	dbst->dup_compare = value;
+#if BDB_VERSION < 30000
+	dbst->dbinfo->dup_compare = bdb_dup_compare;
+#else
+	bdb_test_error(dbp->set_dup_compare(dbp, bdb_dup_compare));
+#endif
+#else
+	rb_warning("dup_compare need db >= 2.5.9");
+#endif
+    }
+    else if (strcmp(options, "set_h_hash") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->options |= BDB_H_HASH;
+	dbst->h_hash = value;
+#if BDB_VERSION < 30000
+	dbst->dbinfo->h_hash = bdb_h_hash;
+#else
+	bdb_test_error(dbp->set_h_hash(dbp, bdb_h_hash));
+#endif
+    }
+#if BDB_VERSION >= 40100
+    else if (strcmp(options, "set_append_recno") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->options |= BDB_APPEND_RECNO;
+	dbst->append_recno = value;
+	bdb_test_error(dbp->set_append_recno(dbp, bdb_append_recno));
+    }
+#endif
+    else if (strcmp(options, "set_cachesize") == 0) {
+	switch (TYPE(value)) {
+	case T_FIXNUM:
+	case T_FLOAT:
+	case T_BIGNUM:
+#if BDB_VERSION < 30000
+	    dbst->dbinfo->db_cachesize = NUM2INT(value);
+#else
+	    bdb_test_error(dbp->set_cachesize(dbp, 0, NUM2UINT(value), 0));
+#endif
+	    break;
+	default:
+	    Check_Type(value, T_ARRAY);
+	    if (RARRAY(value)->len < 3) { 
+		rb_raise(bdb_eFatal, "expected 3 values for cachesize");
+	    }
+#if BDB_VERSION < 30000
+	    dbst->dbinfo->db_cachesize = NUM2INT(RARRAY(value)->ptr[1]);
+#else
+	    bdb_test_error(dbp->set_cachesize(dbp, 
+					      NUM2INT(RARRAY(value)->ptr[0]),
+					      NUM2INT(RARRAY(value)->ptr[1]),
+					      NUM2INT(RARRAY(value)->ptr[2])));
+#endif
+	    break;
+	}
+    }
+    else if (strcmp(options, "set_flags") == 0) {
+#if BDB_VERSION < 30000
+	dbst->dbinfo->flags = NUM2UINT(value);
+#else
+        bdb_test_error(dbp->set_flags(dbp, NUM2UINT(value)));
+#endif
+	dbst->flags |= NUM2UINT(value);
+    }
+    else if (strcmp(options, "set_h_ffactor") == 0) {
+#if BDB_VERSION < 30000
+	dbst->dbinfo->h_ffactor = NUM2INT(value);
+#else
+        bdb_test_error(dbp->set_h_ffactor(dbp, NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_h_nelem") == 0) {
+#if BDB_VERSION < 30000
+	dbst->dbinfo->h_nelem = NUM2INT(value);
+#else
+        bdb_test_error(dbp->set_h_nelem(dbp, NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_lorder") == 0) {
+#if BDB_VERSION < 30000
+	dbst->dbinfo->db_lorder = NUM2INT(value);
+#else
+        bdb_test_error(dbp->set_lorder(dbp, NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_pagesize") == 0) {
+#if BDB_VERSION < 30000
+	dbst->dbinfo->db_pagesize = NUM2INT(value);
+#else
+        bdb_test_error(dbp->set_pagesize(dbp, NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_re_delim") == 0) {
+	int ch;
+	if (TYPE(value) == T_STRING) {
+	    str = StringValuePtr(value);
+	    ch = str[0];
+	}
+	else {
+	    ch = NUM2INT(value);
+	}
+#if BDB_VERSION < 30000
+	dbst->dbinfo->re_delim = ch;
+	dbst->dbinfo->flags |= DB_DELIMITER;
+#else
+        bdb_test_error(dbp->set_re_delim(dbp, ch));
+#endif
+    }
+    else if (strcmp(options, "set_re_len") == 0) {
+#if BDB_VERSION < 30000
+	dbst->dbinfo->re_len = NUM2INT(value);
+	dbst->dbinfo->flags |= DB_FIXEDLEN;
+#else
+        bdb_test_error(dbp->set_re_len(dbp, NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_re_pad") == 0) {
+	int ch;
+	if (TYPE(value) == T_STRING) {
+	    str = StringValuePtr(value);
+	    ch = str[0];
+	}
+	else {
+	    ch = NUM2INT(value);
+	}
+#if BDB_VERSION < 30000
+	dbst->dbinfo->re_pad = ch;
+	dbst->dbinfo->flags |= DB_PAD;
+#else
+        bdb_test_error(dbp->set_re_pad(dbp, ch));
+#endif
+    }
+    else if (strcmp(options, "set_re_source") == 0) {
+        if (TYPE(value) != T_STRING)
+            rb_raise(bdb_eFatal, "re_source must be a filename");
+#if BDB_VERSION < 30000
+	dbst->dbinfo->re_source = StringValuePtr(value);
+#else
+        bdb_test_error(dbp->set_re_source(dbp, StringValuePtr(value)));
+#endif
+	dbst->options |= BDB_RE_SOURCE;
+    }
+#if BDB_VERSION >= 30200
+    else if (strcmp(options, "set_q_extentsize") == 0) {
+	bdb_test_error(dbp->set_q_extentsize(dbp, NUM2INT(value)));
+    }
+#endif
+    else if (strcmp(options, "marshal") == 0) {
+        switch (value) {
+        case Qtrue: 
+	    dbst->marshal = bdb_mMarshal; 
+	    dbst->options |= BDB_MARSHAL;
+	    break;
+        case Qfalse: 
+	    dbst->marshal = Qfalse; 
+	    dbst->options &= ~BDB_MARSHAL;
+	    break;
+        default: 
+	    if (!rb_respond_to(value, bdb_id_load) ||
+		!rb_respond_to(value, bdb_id_dump)) {
+		rb_raise(bdb_eFatal, "marshal value must be true or false");
+	    }
+	    dbst->marshal = value;
+	    dbst->options |= BDB_MARSHAL;
+	    break;
+        }
+    }
+    else if (strcmp(options, "set_array_base") == 0 ||
+	     strcmp(options, "array_base") == 0) {
+	int ary_base = NUM2INT(value);
+	switch (ary_base) {
+	case 0: dbst->array_base = 1; break;
+	case 1: dbst->array_base = 0; break;
+	default: rb_raise(bdb_eFatal, "array base must be 0 or 1");
+	}
+    }
+    else if (strcmp(options, "thread") == 0) {
+	if (RTEST(value)) {
+	    dbst->options &= ~BDB_NO_THREAD;
+	}
+	else {
+	    dbst->options |= BDB_NO_THREAD;
+	}
+    }
+    else if (strcmp(options, "set_store_key") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->filter[FILTER_KEY] = value;
+    }
+    else if (strcmp(options, "set_fetch_key") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->filter[2 + FILTER_KEY] = value;
+    }
+    else if (strcmp(options, "set_store_value") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->filter[FILTER_VALUE] = value;
+    }
+    else if (strcmp(options, "set_fetch_value") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->filter[2 + FILTER_VALUE] = value;
+    }
+#if BDB_VERSION >= 40100
+    else if (strcmp(options, "set_encrypt") == 0) {
+	char *passwd;
+	int flags = DB_ENCRYPT_AES;
+	if (TYPE(value) == T_ARRAY) {
+	    if (RARRAY(value)->len != 2) {
+		rb_raise(bdb_eFatal, "Expected an Array with 2 values");
+	    }
+	    passwd = StringValuePtr(RARRAY(value)->ptr[0]);
+	    flags = NUM2INT(RARRAY(value)->ptr[1]);
+	}
+	else {
+	    passwd = StringValuePtr(value);
+	}
+	bdb_test_error(dbp->set_encrypt(dbp, passwd, flags));
+    }
+#endif
+#if BDB_VERSION >= 30000
+    else if (strcmp(options, "set_feedback") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->options |= BDB_FEEDBACK;
+	dbst->feedback = value;
+	dbp->set_feedback(dbp, bdb_feedback);
+    }
+#endif
+    else if (strcmp(options, "store_nil_as_null") == 0) {
+        if (RTEST(value)) {
+            dbst->options |= BDB_NIL;
+        }
+        else {
+            dbst->options &= ~BDB_NIL;
+        }
+    }
+    return Qnil;
+}
+
+struct bdb_eiv {
+    VALUE bdb;
+    bdb_DB *dbst;
+};
+
+static void
+bdb_i_close(bdb_DB *dbst, int flags)
+{
+    bdb_ENV *envst;
+
+    if (dbst->dbp) {
+	if (RTEST(dbst->txn)) {
+	    bdb_TXN *txnst;
+	    int opened = 0;
+
+	    Data_Get_Struct(dbst->txn, bdb_TXN, txnst);
+	    opened = bdb_ary_delete(&txnst->db_ary, dbst->ori_val);
+	    if (!opened) {
+		opened = bdb_ary_delete(&txnst->db_assoc, dbst->ori_val);
+	    }
+	    if (opened) {
+		if (txnst->options & BDB_TXN_COMMIT) {
+		    rb_funcall2(dbst->txn, rb_intern("commit"), 0, 0);
+		}
+		else {
+		    rb_funcall2(dbst->txn, rb_intern("abort"), 0, 0);
+		}
+	    }
+	}
+	else {
+	    if (dbst->env) {
+		Data_Get_Struct(dbst->env, bdb_ENV, envst);
+		bdb_ary_delete(&envst->db_ary, dbst->ori_val);
+	    }
+	    if (!(dbst->options & BDB_NOT_OPEN)) {
+		bdb_test_error(dbst->dbp->close(dbst->dbp, flags));
+	    }
+	}
+    }
+    dbst->dbp = NULL;
+}
+
+VALUE
+bdb_local_aref()
+{
+    bdb_DB *dbst;
+    VALUE obj;
+
+    GetIdDb(obj, dbst);
+    return obj;
+}
+
+static VALUE
+i_close(bdb_DB *dbst)
+{
+    bdb_i_close(dbst, 0);
+    return Qnil;
+}
+
+static VALUE
+bdb_final_aref(bdb_DB *dbst)
+{
+    VALUE obj;
+
+    obj = rb_thread_local_aref(rb_thread_current(), bdb_id_current_db);
+    if (!NIL_P(obj) && RDATA(obj)->dmark == (RUBY_DATA_FUNC)bdb_mark &&
+        DATA_PTR(obj) == dbst) {
+        rb_thread_local_aset(rb_thread_current(), bdb_id_current_db, Qnil);
+    }
+    return Qnil;
+}
+
+static void
+bdb_free(bdb_DB *dbst)
+{
+    if (dbst->dbp && !(dbst->options & BDB_NOT_OPEN)) {
+        rb_protect((VALUE (*)(ANYARGS))i_close, (VALUE)dbst, 0);
+        rb_protect((VALUE (*)(ANYARGS))bdb_final_aref, (VALUE)dbst, 0);
+    }
+    free(dbst);
+}
+
+static void
+bdb_mark(bdb_DB *dbst)
+{
+    int i;
+    rb_gc_mark(dbst->marshal);
+    rb_gc_mark(dbst->env);
+    rb_gc_mark(dbst->txn);
+    rb_gc_mark(dbst->orig);
+    rb_gc_mark(dbst->secondary);
+    rb_gc_mark(dbst->bt_compare);
+    rb_gc_mark(dbst->bt_prefix);
+    rb_gc_mark(dbst->dup_compare);
+    for (i = 0; i < 4; i++) {
+	rb_gc_mark(dbst->filter[i]);
+    }
+    rb_gc_mark(dbst->h_hash);
+    rb_gc_mark(dbst->filename);
+    rb_gc_mark(dbst->database);
+#if BDB_VERSION >= 40100
+    rb_gc_mark(dbst->append_recno);
+#endif
+#if BDB_VERSION >= 30000
+    rb_gc_mark(dbst->feedback);
+#endif
+}
+
+static VALUE
+bdb_env(VALUE obj)
+{
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (RTEST(dbst->env)) {
+	return dbst->env;
+    }
+    return Qnil;
+}
+
+VALUE
+bdb_env_p(VALUE obj)
+{
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    return (RTEST(dbst->env)?Qtrue:Qfalse);
+}
+
+static VALUE
+bdb_txn(VALUE obj)
+{
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (RTEST(dbst->txn)) {
+	return dbst->txn;
+    }
+    return Qnil;
+}
+
+VALUE
+bdb_txn_p(VALUE obj)
+{
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    return (RTEST(dbst->txn)?Qtrue:Qfalse);
+}
+
+static VALUE
+bdb_close(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE opt;
+    bdb_DB *dbst;
+    int flags = 0;
+
+    if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4) {
+	rb_raise(rb_eSecurityError, "Insecure: can't close the database");
+    }
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    if (dbst->dbp != NULL) {
+	if (rb_scan_args(argc, argv, "01", &opt)) {
+	    flags = NUM2INT(opt);
+	}
+	bdb_i_close(dbst, flags);
+    }
+    dbst->options |= BDB_NOT_OPEN;
+    rb_protect((VALUE (*)(ANYARGS))bdb_final_aref, (VALUE)dbst, 0);
+    RDATA(obj)->dfree = free;
+    return Qnil;
+}
+
+#if BDB_VERSION < 30100
+static long
+bdb_hard_count(dbp)
+    DB *dbp;
+{
+    DBC *dbcp;
+    DBT key, data;
+    db_recno_t recno;
+    long count = 0;
+    int ret;
+
+    MEMZERO(&key, DBT, 1);
+    key.data = &recno;
+    key.size = sizeof(db_recno_t);
+    MEMZERO(&data, DBT, 1);
+    data.flags = DB_DBT_MALLOC;
+#if BDB_VERSION < 20600
+    key.flags |= DB_DBT_MALLOC;
+    bdb_test_error(dbp->cursor(dbp, 0, &dbcp));
+#else
+    bdb_test_error(dbp->cursor(dbp, 0, &dbcp, 0));
+#endif
+    do {
+        bdb_cache_error(dbcp->c_get(dbcp, &key, &data, DB_NEXT),
+			dbcp->c_close(dbcp), ret);
+        if (ret == DB_NOTFOUND) {
+            dbcp->c_close(dbcp);
+            return count;
+        }
+	if (ret == DB_KEYEMPTY)  {
+            dbcp->c_close(dbcp);
+	    return -1;
+	}
+	free(data.data);
+        count++;
+    } while (1);
+    return count;
+}
+#endif
+
+static long
+bdb_is_recnum(dbp)
+    DB *dbp;
+{
+    DB_BTREE_STAT *bdb_stat;
+    long count;
+#if BDB_VERSION < 30100
+    long hard;
+#endif
+
+#if BDB_VERSION >= 30100
+#if BDB_VERSION >= 30300
+#if BDB_VERSION >= 40300
+    bdb_test_error(dbp->stat(dbp, NULL, &bdb_stat, 0));
+#else
+    bdb_test_error(dbp->stat(dbp, &bdb_stat, 0));
+#endif
+#else
+    bdb_test_error(dbp->stat(dbp, &bdb_stat, 0, 0));
+#endif
+    count = (bdb_stat->bt_nkeys == bdb_stat->bt_ndata)?bdb_stat->bt_nkeys:-1;
+    free(bdb_stat);
+#else
+    bdb_test_error(dbp->stat(dbp, &bdb_stat, 0, DB_RECORDCOUNT));
+    count = bdb_stat->bt_nrecs;
+    free(bdb_stat);
+    hard = bdb_hard_count(dbp);
+    count = (count == hard)?count:-1;
+#endif
+    return count;
+}
+
+static VALUE
+bdb_recno_length(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+    DB_BTREE_STAT *bdb_stat;
+    VALUE hash;
+#if BDB_VERSION >= 40300
+    DB_TXN *txnid = NULL;
+#endif
+
+    GetDB(obj, dbst);
+#if BDB_VERSION >= 40000
+#if BDB_VERSION >= 40300
+    if (RTEST(dbst->txn)) {
+        bdb_TXN *txnst;
+
+        GetTxnDB(dbst->txn, txnst);
+        txnid = txnst->txnid;
+    }
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, txnid, &bdb_stat, DB_FAST_STAT));
+#else
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, DB_FAST_STAT));
+#endif
+#else
+#if BDB_VERSION >= 30300
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, DB_RECORDCOUNT));
+#else
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, 0, DB_RECORDCOUNT));
+#endif
+#endif
+#if BDB_VERSION >= 30100
+    hash = INT2NUM(bdb_stat->bt_nkeys);
+#else
+    hash = INT2NUM(bdb_stat->bt_nrecs);
+#endif
+    free(bdb_stat);
+    return hash;
+}
+
+static VALUE
+bdb_s_new(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE res;
+    bdb_TXN *txnst = NULL;
+    bdb_ENV *envst = NULL;
+    bdb_DB *dbst;
+    DB_ENV *envp = 0;
+
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+    res = rb_obj_alloc(obj);
+#else
+    res = rb_funcall2(obj, rb_intern("allocate"), 0, 0);
+#endif
+    Data_Get_Struct(res, bdb_DB, dbst);
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+	VALUE v, f = argv[argc - 1];
+
+	if ((v = rb_hash_aref(f, rb_str_new2("txn"))) != RHASH(f)->ifnone) {
+	    if (!rb_obj_is_kind_of(v, bdb_cTxn)) {
+		rb_raise(bdb_eFatal, "argument of txn must be a transaction");
+	    }
+	    Data_Get_Struct(v, bdb_TXN, txnst);
+	    dbst->txn = v;
+	    dbst->env = txnst->env;
+	    Data_Get_Struct(txnst->env, bdb_ENV, envst);
+	    envp = envst->envp;
+	    dbst->options |= envst->options & BDB_NO_THREAD;
+	    dbst->marshal = txnst->marshal;
+	}
+	else if ((v = rb_hash_aref(f, rb_str_new2("env"))) != RHASH(f)->ifnone) {
+	    if (!rb_obj_is_kind_of(v, bdb_cEnv)) {
+		rb_raise(bdb_eFatal, "argument of env must be an environnement");
+	    }
+	    Data_Get_Struct(v, bdb_ENV, envst);
+	    dbst->env = v;
+	    envp = envst->envp;
+	    dbst->options |= envst->options & BDB_NO_THREAD;
+	    dbst->marshal = envst->marshal;
+	}
+#ifdef DB_ENCRYPT 
+	if (envst && (envst->options & BDB_ENV_ENCRYPT)) {
+	    VALUE tmp = rb_str_new2("set_flags");
+	    if ((v = rb_hash_aref(f, rb_intern("set_flags"))) != RHASH(f)->ifnone) {
+		rb_hash_aset(f, rb_intern("set_flags"), 
+			     INT2NUM(NUM2INT(v) | DB_ENCRYPT));
+	    }
+	    else if ((v = rb_hash_aref(f, tmp)) != RHASH(f)->ifnone) {
+		rb_hash_aset(f, tmp, INT2NUM(NUM2INT(v) | DB_ENCRYPT));
+	    }
+	    else {
+		rb_hash_aset(f, tmp, INT2NUM(DB_ENCRYPT));
+	    }
+	}
+#endif
+    }
+#if BDB_VERSION >= 30000
+    bdb_test_error(db_create(&(dbst->dbp), envp, 0));
+    dbst->dbp->set_errpfx(dbst->dbp, "BDB::");
+    dbst->dbp->set_errcall(dbst->dbp, bdb_env_errcall);
+#endif
+    if (rb_respond_to(obj, bdb_id_load) == Qtrue &&
+	rb_respond_to(obj, bdb_id_dump) == Qtrue) {
+	dbst->marshal = obj;
+	dbst->options |= BDB_MARSHAL;
+    }
+    if (rb_method_boundp(obj, rb_intern("bdb_store_key"), 0) == Qtrue) {
+	dbst->filter[FILTER_KEY] = INT2FIX(rb_intern("bdb_store_key"));
+    }
+    if (rb_method_boundp(obj, rb_intern("bdb_fetch_key"), 0) == Qtrue) {
+	dbst->filter[2 + FILTER_KEY] = INT2FIX(rb_intern("bdb_fetch_key"));
+    }
+    if (rb_method_boundp(obj, rb_intern("bdb_store_value"), 0) == Qtrue) {
+	dbst->filter[FILTER_VALUE] = INT2FIX(rb_intern("bdb_store_value"));
+    }
+    if (rb_method_boundp(obj, rb_intern("bdb_fetch_value"), 0) == Qtrue) {
+	dbst->filter[2 + FILTER_VALUE] = INT2FIX(rb_intern("bdb_fetch_value"));
+    }
+    rb_obj_call_init(res, argc, argv);
+    if (txnst) {
+	bdb_ary_push(&txnst->db_ary, res);
+    }
+    else if (envst) {
+	bdb_ary_push(&envst->db_ary, res);
+    }
+    return res;
+}
+
+VALUE
+bdb_init(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    DB *dbp;
+    int flags, mode, ret, nb;
+    char *name, *subname;
+    VALUE a, b, d, e;
+    VALUE hash_arg = Qnil;
+#if BDB_VERSION < 30000
+    DB_INFO dbinfo;
+
+    MEMZERO(&dbinfo, DB_INFO, 1);
+#endif
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    dbp = dbst->dbp;
+#if BDB_VERSION < 30000
+    dbst->dbinfo = &dbinfo;
+#endif
+#if BDB_VERSION >= 40100
+    if (rb_const_defined(CLASS_OF(obj), rb_intern("BDB_ENCRYPT"))) {
+	char *passwd;
+	int flags = DB_ENCRYPT_AES;
+	VALUE value = rb_const_get(CLASS_OF(obj), rb_intern("BDB_ENCRYPT"));
+	if (TYPE(value) == T_ARRAY) {
+	    if (RARRAY(value)->len != 2) {
+		rb_raise(bdb_eFatal, "Expected an Array with 2 values");
+	    }
+	    passwd = StringValuePtr(RARRAY(value)->ptr[0]);
+	    flags = NUM2INT(RARRAY(value)->ptr[1]);
+	}
+	else {
+	    passwd = StringValuePtr(value);
+	}
+	bdb_test_error(dbp->set_encrypt(dbp, passwd, flags));
+    }
+#endif
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+	hash_arg = argv[argc - 1];
+	rb_iterate(rb_each, argv[argc - 1], bdb_i_options, obj);
+        argc--;
+    }
+    mode = flags = 0;
+    if (argc) {
+	flags = DB_RDONLY;
+    }
+    a = b = d = e = Qnil;
+    switch(nb = rb_scan_args(argc, argv, "04", &a, &b, &d, &e)) {
+    case 4:
+	mode = NUM2INT(e);
+    case 3:
+	if (TYPE(d) == T_STRING) {
+	    if (strcmp(StringValuePtr(d), "r") == 0)
+		flags = DB_RDONLY;
+	    else if (strcmp(StringValuePtr(d), "r+") == 0)
+		flags = 0;
+	    else if (strcmp(StringValuePtr(d), "w") == 0 ||
+		     strcmp(StringValuePtr(d), "w+") == 0)
+		flags = DB_CREATE | DB_TRUNCATE;
+	    else if (strcmp(StringValuePtr(d), "a") == 0 ||
+		     strcmp(StringValuePtr(d), "a+") == 0)
+		flags = DB_CREATE;
+	    else {
+		rb_raise(bdb_eFatal, "flags must be r, r+, w, w+, a or a+");
+	    }
+	}
+	else if (d == Qnil)
+	    flags = DB_RDONLY;
+	else
+	    flags = NUM2INT(d);
+    }
+    name = subname = NULL;
+    if (!NIL_P(a)) {
+	SafeStringValue(a);
+        name = StringValuePtr(a);
+    }
+    if (!NIL_P(b)) {
+	SafeStringValue(b);
+        subname = StringValuePtr(b);
+    }
+    if (dbst->bt_compare == 0 && rb_respond_to(obj, id_bt_compare) == Qtrue) {
+	dbst->options |= BDB_BT_COMPARE;
+#if BDB_VERSION < 30000
+	dbst->dbinfo->bt_compare = bdb_bt_compare;
+#else
+	bdb_test_error(dbp->set_bt_compare(dbp, bdb_bt_compare));
+#endif
+    }
+    if (dbst->bt_prefix == 0 && rb_respond_to(obj, id_bt_prefix) == Qtrue) {
+	dbst->options |= BDB_BT_PREFIX;
+#if BDB_VERSION < 30000
+	dbst->dbinfo->bt_prefix = bdb_bt_prefix;
+#else
+	bdb_test_error(dbp->set_bt_prefix(dbp, bdb_bt_prefix));
+#endif
+    }
+#ifdef DB_DUPSORT
+    if (dbst->dup_compare == 0 && rb_respond_to(obj, id_dup_compare) == Qtrue) {
+	dbst->options |= BDB_DUP_COMPARE;
+#if BDB_VERSION < 30000
+	dbst->dbinfo->dup_compare = bdb_dup_compare;
+#else
+	bdb_test_error(dbp->set_dup_compare(dbp, bdb_dup_compare));
+#endif
+    }
+#endif
+    if (dbst->h_hash == 0 && rb_respond_to(obj, id_h_hash) == Qtrue) {
+	dbst->options |= BDB_H_HASH;
+#if BDB_VERSION < 30000
+	dbst->dbinfo->h_hash = bdb_h_hash;
+#else
+	bdb_test_error(dbp->set_h_hash(dbp, bdb_h_hash));
+#endif
+    }
+#if BDB_VERSION >= 40100
+    if (dbst->append_recno == 0 && rb_respond_to(obj, id_append_recno) == Qtrue) {
+	dbst->options |= BDB_APPEND_RECNO;
+	bdb_test_error(dbp->set_append_recno(dbp, bdb_append_recno));
+    }
+#endif
+#if BDB_VERSION >= 30000
+    if (dbst->feedback == 0 && rb_respond_to(obj, id_feedback) == Qtrue) {
+	dbp->set_feedback(dbp, bdb_feedback);
+	dbst->options |= BDB_FEEDBACK;
+    }
+#endif
+    if (flags & DB_TRUNCATE) {
+	rb_secure(2);
+    }
+    if (flags & DB_CREATE) {
+	rb_secure(4);
+    }
+    if (rb_safe_level() >= 4) {
+	flags |= DB_RDONLY;
+    }
+#ifdef DB_DUPSORT
+    if (dbst->options & BDB_DUP_COMPARE) {
+#if BDB_VERSION < 30000
+	dbst->dbinfo->flags = DB_DUP | DB_DUPSORT;
+#else
+        bdb_test_error(dbp->set_flags(dbp, DB_DUP | DB_DUPSORT));
+#endif
+    }
+#endif
+#ifndef BDB_NO_THREAD_COMPILE
+    if (!(dbst->options & (BDB_RE_SOURCE | BDB_NO_THREAD))) {
+	flags |= DB_THREAD;
+    }
+#endif
+    if (dbst->options & BDB_NEED_CURRENT) {
+	rb_thread_local_aset(rb_thread_current(), bdb_id_current_db, obj);
+    }
+
+#if BDB_VERSION < 30000
+    {
+	bdb_ENV *envst;
+	DB_ENV *envp = NULL;
+	if (dbst->env) {
+	    Data_Get_Struct(dbst->env, bdb_ENV, envst);
+	    envp = envst->envp;
+	}
+
+	if (name == NULL && (flags & DB_RDONLY)) {
+	    flags &= ~DB_RDONLY;
+	}
+	    
+	if ((ret = db_open(name, dbst->type, flags, mode, envp,
+			   dbst->dbinfo, &dbp)) != 0) {
+	    if (bdb_errcall) {
+		bdb_errcall = 0;
+		rb_raise(bdb_eFatal, "%s -- %s", StringValuePtr(bdb_errstr), db_strerror(ret));
+	    }
+	    else
+		rb_raise(bdb_eFatal, "%s", db_strerror(ret));
+	}
+	dbst->dbp = dbp;
+    }
+#else
+    {
+	DB_TXN *txnid = NULL;
+
+	if (name == NULL && subname == NULL) {
+	    if (flags & DB_RDONLY) {
+		flags &= ~DB_RDONLY;
+	    }
+#if BDB_VERSION >= 40416
+	    flags |= DB_CREATE;
+#endif
+	}
+#if BDB_VERSION >= 40100
+	if (RTEST(dbst->txn)) {
+	    bdb_TXN *txnst;
+
+	    GetTxnDB(dbst->txn, txnst);
+	    txnid = txnst->txnid;
+	}
+	else if (RTEST(dbst->env)) {
+	    bdb_ENV *envst;
+
+	    GetEnvDB(dbst->env, envst);
+	    if (envst->options & BDB_AUTO_COMMIT) {
+		flags |= DB_AUTO_COMMIT;
+		dbst->options |= BDB_AUTO_COMMIT;
+	    }
+	}
+	ret = dbp->open(dbp, txnid, name, subname, dbst->type, flags, mode);
+#else
+	ret = dbp->open(dbp, name, subname, dbst->type, flags, mode);
+#endif
+	if (ret != 0) {
+	    dbp->close(dbp, 0);
+	    if (bdb_errcall) {
+		bdb_errcall = 0;
+		rb_raise(bdb_eFatal, "%s -- %s", StringValuePtr(bdb_errstr), db_strerror(ret));
+	    }
+	    else {
+		rb_raise(bdb_eFatal, "%s", db_strerror(ret));
+	    }
+	}
+    }
+#endif
+    dbst->options &= ~BDB_NOT_OPEN;
+    if (dbst->env) {
+	bdb_ENV *envst;
+	Data_Get_Struct(dbst->env, bdb_ENV, envst);
+	dbst->options |= envst->options & BDB_INIT_LOCK;
+    }
+    dbst->filename = dbst->database = Qnil;
+    if (name) {
+	dbst->filename = rb_tainted_str_new2(name);
+	OBJ_FREEZE(dbst->filename);
+    }
+#if BDB_VERSION >= 30000
+    if (subname) {
+	dbst->database = rb_tainted_str_new2(subname);
+	OBJ_FREEZE(dbst->database);
+    }
+#endif
+    dbst->len = -2;
+    if (dbst->type == DB_UNKNOWN) {
+#if BDB_VERSION < 30000
+	dbst->type = dbst->dbp->type;
+#else
+#if BDB_VERSION >= 30300
+	{
+	    DBTYPE new_type;
+	    bdb_test_error(dbst->dbp->get_type(dbst->dbp, &new_type));
+	    dbst->type = (int)new_type;
+	}
+#else
+	dbst->type = dbst->dbp->get_type(dbst->dbp);
+#endif
+#endif
+	switch(dbst->type) {
+	case DB_BTREE:
+	    RBASIC(obj)->klass = bdb_cBtree;
+	    break;
+	case DB_HASH:
+	    RBASIC(obj)->klass = bdb_cHash;
+	    break;
+	case DB_RECNO:
+	{
+	    long count;
+
+	    rb_warning("It's hard to distinguish Recnum with Recno for all versions of Berkeley DB");
+	    if ((count = bdb_is_recnum(dbst->dbp)) != -1) {
+		RBASIC(obj)->klass = bdb_cRecnum;
+		dbst->len = count;
+	    }
+	    else {
+		RBASIC(obj)->klass = bdb_cRecno;
+	    }
+	    break;
+	}
+#if BDB_VERSION >= 30000
+	case DB_QUEUE:
+	    RBASIC(obj)->klass = bdb_cQueue;
+	    break;
+#endif
+	default:
+	    dbst->dbp->close(dbst->dbp, 0);
+            dbst->dbp = NULL;
+	    rb_raise(bdb_eFatal, "Unknown DB type");
+	}
+    }
+    if (dbst->len == -2 && rb_obj_is_kind_of(obj, bdb_cRecnum)) {
+	long count;
+
+	if ((count = bdb_is_recnum(dbst->dbp)) != -1) {
+	    VALUE len = bdb_recno_length(obj);
+	    dbst->len = NUM2LONG(len);
+	}
+	else {
+	    if (flags & DB_TRUNCATE) {
+		dbst->len = 0;
+	    }
+	    else {
+                dbst->dbp->close(dbst->dbp, 0);
+                dbst->dbp = NULL;
+		rb_raise(bdb_eFatal, "database is not a Recnum");
+	    }
+	}
+    }
+    return obj;
+}
+
+static VALUE
+bdb_s_alloc(obj)
+    VALUE obj;
+{
+    VALUE res, cl;
+    bdb_DB *dbst;
+
+    res = Data_Make_Struct(obj, bdb_DB, bdb_mark, bdb_free, dbst);
+    dbst->options = BDB_NOT_OPEN;
+    cl = obj;
+    while (cl) {
+	if (cl == bdb_cBtree || RCLASS(cl)->m_tbl == RCLASS(bdb_cBtree)->m_tbl) {
+	    dbst->type = DB_BTREE; 
+	    break;
+	}
+	if (cl == bdb_cRecnum || RCLASS(cl)->m_tbl == RCLASS(bdb_cRecnum)->m_tbl) {
+	    dbst->type = DB_RECNO; 
+	    break;
+	}
+	else if (cl == bdb_cHash || RCLASS(cl)->m_tbl == RCLASS(bdb_cHash)->m_tbl) {
+	    dbst->type = DB_HASH; 
+	    break;
+	}
+	else if (cl == bdb_cRecno || RCLASS(cl)->m_tbl == RCLASS(bdb_cRecno)->m_tbl) {
+	    dbst->type = DB_RECNO;
+	    break;
+    }
+#if BDB_VERSION >= 30000
+	else if (cl == bdb_cQueue || RCLASS(cl)->m_tbl == RCLASS(bdb_cQueue)->m_tbl) {
+	    dbst->type = DB_QUEUE;
+	    break;
+	}
+#endif
+	else if (cl == bdb_cUnknown || RCLASS(cl)->m_tbl == RCLASS(bdb_cUnknown)->m_tbl) {
+	    dbst->type = DB_UNKNOWN;
+	    break;
+	}
+	cl = RCLASS(cl)->super;
+    }
+    if (!cl) {
+	rb_raise(bdb_eFatal, "unknown database type");
+    }
+    dbst->ori_val = res;
+    return res;
+}
+
+static VALUE
+bdb_i_s_create(obj, db)
+    VALUE obj, db;
+{
+    VALUE tmp[2];
+    tmp[0] = rb_ary_entry(obj, 0);
+    tmp[1] = rb_ary_entry(obj, 1);
+    bdb_put(2, tmp, db);
+    return Qnil;
+} 
+
+static VALUE
+bdb_s_create(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE res;
+    int i;
+
+    res = rb_funcall2(obj, rb_intern("new"), 0, 0);
+    if (argc == 1 && TYPE(argv[0]) == T_HASH) {
+	rb_iterate(rb_each, argv[0], bdb_i_s_create, res);
+	return res;
+    }
+    if (argc % 2 != 0) {
+        rb_raise(rb_eArgError, "odd number args for %s", rb_class2name(obj));
+    }
+    for (i = 0; i < argc; i += 2) {
+        bdb_put(2, argv + i, res);
+    }
+    return res;
+}
+
+static VALUE
+bdb_s_open(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE res = rb_funcall2(obj, rb_intern("new"), argc, argv);
+    if (rb_block_given_p()) {
+	return rb_ensure(rb_yield, res, bdb_protect_close, res);
+    }
+    return res;
+}
+
+#if BDB_VERSION >= 30000
+
+struct re {
+    int re_len, re_pad;
+};
+
+static VALUE
+bdb_queue_i_search_re_len(obj, restobj)
+    VALUE obj, restobj;
+{
+    VALUE key, value;
+    char *str;
+    struct re *rest;
+
+    Data_Get_Struct(restobj, struct re, rest);
+    key = rb_ary_entry(obj, 0);
+    value = rb_ary_entry(obj, 1);
+    key = rb_obj_as_string(key);
+    if (strcmp(StringValuePtr(key), "set_re_len") == 0) {
+	rest->re_len = NUM2INT(value);
+    }
+    else if (strcmp(StringValuePtr(key), "set_re_pad") == 0) {
+	int ch;
+	if (TYPE(value) == T_STRING) {
+	    str = StringValuePtr(value);
+	    ch = str[0];
+	}
+	else {
+	    ch = NUM2INT(value);
+	}
+	rest->re_pad = ch;
+    }
+    return Qnil;
+}
+
+#define DEFAULT_RECORD_LENGTH 132
+#define DEFAULT_RECORD_PAD 0x20
+
+static VALUE
+bdb_queue_s_new(int argc, VALUE *argv, VALUE obj) 
+{
+    VALUE *nargv, ret, restobj;
+    struct re *rest;
+    bdb_DB *dbst;
+
+    restobj = Data_Make_Struct(obj, struct re, 0, free, rest);
+    rest->re_len = -1;
+    rest->re_pad = -1;
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+	rb_iterate(rb_each, argv[argc - 1], bdb_queue_i_search_re_len, restobj);
+	if (rest->re_len <= 0) {
+	    rest->re_len = DEFAULT_RECORD_LENGTH;
+	    rb_hash_aset(argv[argc - 1], rb_tainted_str_new2("set_re_len"), INT2NUM(rest->re_len));
+	}
+	if (rest->re_pad < 0) {
+	    rest->re_pad = DEFAULT_RECORD_PAD;
+	    rb_hash_aset(argv[argc - 1], rb_tainted_str_new2("set_re_pad"), INT2NUM(rest->re_pad));
+	}
+	nargv = argv;
+    }
+    else {
+	nargv = ALLOCA_N(VALUE, argc + 1);
+	MEMCPY(nargv, argv, VALUE, argc);
+	nargv[argc] = rb_hash_new();
+	rest->re_len = DEFAULT_RECORD_LENGTH;
+	rest->re_pad = DEFAULT_RECORD_PAD;
+	rb_hash_aset(nargv[argc], rb_tainted_str_new2("set_re_len"), INT2NUM(DEFAULT_RECORD_LENGTH));
+	rb_hash_aset(nargv[argc], rb_tainted_str_new2("set_re_pad"), INT2NUM(DEFAULT_RECORD_PAD));
+	argc += 1;
+    }
+    ret = bdb_s_new(argc, nargv, obj);
+    Data_Get_Struct(ret, bdb_DB, dbst);
+    dbst->re_len = rest->re_len;
+    dbst->re_pad = rest->re_pad;
+    return ret;
+}
+#endif
+
+static VALUE
+bdb_append_internal(argc, argv, obj, flag, retval)
+    int argc, flag;
+    VALUE *argv, obj;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT key, data;
+    db_recno_t recno;
+    int i;
+    VALUE *a, ary = Qnil;
+    volatile VALUE res = Qnil;
+
+    rb_secure(4);
+    if (argc < 1)
+	return obj;
+    INIT_TXN(txnid, obj, dbst);
+    if (txnid == NULL && (dbst->options & BDB_AUTO_COMMIT)) {
+      flag |= DB_AUTO_COMMIT;
+    }
+    MEMZERO(&key, DBT, 1);
+    recno = 1;
+    key.data = &recno;
+    key.size = sizeof(db_recno_t);
+#if BDB_VERSION >= 40100
+    if (flag & DB_APPEND) {
+	key.flags |= DB_DBT_MALLOC;
+    }
+#endif
+    if (retval) {
+	ary = rb_ary_new();
+    }
+    for (i = 0, a = argv; i < argc; i++, a++) {
+	MEMZERO(&data, DBT, 1);
+	res = bdb_test_dump(obj, &data, *a, FILTER_VALUE);
+	SET_PARTIAL(dbst, data);
+#if BDB_VERSION >= 30000
+	if (dbst->type == DB_QUEUE && dbst->re_len < data.size) {
+	    rb_raise(bdb_eFatal, "size > re_len for Queue");
+	}
+#endif
+	bdb_test_error(dbst->dbp->put(dbst->dbp, txnid, &key, &data, flag));
+	if (retval) {
+	    rb_ary_push(ary, INT2NUM(*(db_recno_t *)key.data));
+	}
+    }
+    if (retval) {
+	return ary;
+    }
+    return obj;
+}
+
+static VALUE
+bdb_append_m(int argc, VALUE *argv, VALUE obj)
+{
+    return bdb_append_internal(argc, argv, obj, DB_APPEND, Qtrue);
+}
+
+static VALUE
+bdb_append(obj, val)
+    VALUE val, obj;
+{
+    return bdb_append_internal(1, &val, obj, DB_APPEND, Qfalse);
+}
+
+static VALUE
+bdb_unshift(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    int flag;
+
+    INIT_TXN(txnid, obj, dbst);
+    if (dbst->flags & DB_RENUMBER) {
+	flag = 0;
+    }
+    else {
+	flag = DB_NOOVERWRITE;
+    }
+    return bdb_append_internal(argc, argv, obj, flag, Qtrue);
+}
+
+VALUE
+bdb_put(int argc, VALUE *argv, VALUE obj)
+{
+    volatile VALUE a0 = Qnil;
+    volatile VALUE b0 = Qnil;
+    VALUE a, b, c;
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT key, data;
+    int ret, flags;
+    db_recno_t recno;
+
+    rb_secure(4);
+    INIT_TXN(txnid, obj, dbst);
+    flags = 0;
+    a = b = c = Qnil;
+    MEMZERO(&key, DBT, 1);
+    MEMZERO(&data, DBT, 1);
+    if (rb_scan_args(argc, argv, "21", &a, &b, &c) == 3) {
+        flags = NUM2INT(c);
+    }
+    a0 = bdb_test_recno(obj, &key, &recno, a);
+    b0 = bdb_test_dump(obj, &data, b, FILTER_VALUE);
+    SET_PARTIAL(dbst, data);
+#if BDB_VERSION >= 30000
+    if (dbst->type == DB_QUEUE && dbst->re_len < data.size) {
+	rb_raise(bdb_eFatal, "size > re_len for Queue");
+    }
+#endif
+    if (txnid == NULL && (dbst->options & BDB_AUTO_COMMIT)) {
+      flags |= DB_AUTO_COMMIT;
+    }
+    ret = bdb_test_error(dbst->dbp->put(dbst->dbp, txnid, &key, &data, flags));
+    if (ret == DB_KEYEXIST)
+	return Qfalse;
+    else {
+	if (dbst->partial) {
+	    if (flags & DB_APPEND) {
+		a = INT2NUM((long)key.data);
+	    }
+	    return bdb_get(1, &a, obj);
+	}
+	else {
+	    return bdb_test_ret(obj, b0, b, FILTER_VALUE);
+	}
+    }
+}
+
+static VALUE
+bdb_aset(obj, a, b)
+    VALUE obj, a, b;
+{
+    VALUE tmp[2];
+    tmp[0] = a;
+    tmp[1] = b;
+    bdb_put(2, tmp, obj);
+    return b;
+}
+
+VALUE
+bdb_test_load_key(obj, key)
+    VALUE obj;
+    DBT *key;
+{
+    bdb_DB *dbst;
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    if (RECNUM_TYPE(dbst))
+        return INT2NUM((*(db_recno_t *)key->data) - dbst->array_base);
+    return bdb_test_load(obj, key, FILTER_KEY);
+}
+
+VALUE
+bdb_assoc(obj, key, data)
+    VALUE obj;
+    DBT *key, *data;
+{
+    return rb_assoc_new(bdb_test_load_key(obj, key), 
+			bdb_test_load(obj, data, FILTER_VALUE));
+}
+
+VALUE
+bdb_assoc_dyna(obj, key, data)
+    VALUE obj;
+    DBT *key, *data;
+{
+    VALUE k, v;
+    int to_free = key->flags & DB_DBT_MALLOC;
+
+    key->flags &= ~DB_DBT_MALLOC;
+    k = bdb_test_load_key(obj, key);
+    v = test_load_dyna1(obj, key, data);
+    if (to_free) {
+        free(key->data);
+    }
+    return rb_assoc_new(k, v);
+}
+
+static VALUE
+bdb_assoc2(obj, skey, pkey, data)
+    VALUE obj;
+    DBT *skey, *pkey, *data;
+{
+    return rb_assoc_new(
+	rb_assoc_new(bdb_test_load_key(obj, skey), bdb_test_load_key(obj, pkey)),
+	bdb_test_load(obj, data, FILTER_VALUE));
+}
+
+VALUE
+bdb_assoc3(obj, skey, pkey, data)
+    VALUE obj;
+    DBT *skey, *pkey, *data;
+{
+    return rb_ary_new3(3, bdb_test_load_key(obj, skey), 
+		       bdb_test_load_key(obj, pkey),
+		       bdb_test_load(obj, data, FILTER_VALUE));
+}
+
+static VALUE bdb_has_both _((VALUE, VALUE, VALUE));
+static VALUE bdb_has_both_internal _((VALUE, VALUE, VALUE, VALUE));
+
+static VALUE
+bdb_get_internal(argc, argv, obj, notfound, dyna)
+    int argc;
+    VALUE *argv;
+    VALUE obj;
+    VALUE notfound;
+    int dyna;
+{
+    VALUE a = Qnil;
+    VALUE b = Qnil;
+    VALUE c;
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT key, data;
+#if BDB_VERSION < 20600
+#define DB_GET_BOTH 9
+#endif
+    int flagss;
+    int ret, flags;
+    db_recno_t recno;
+
+    INIT_TXN(txnid, obj, dbst);
+    flagss = flags = 0;
+    MEMZERO(&key, DBT, 1);
+    MEMZERO(&data, DBT, 1);
+    data.flags |= DB_DBT_MALLOC;
+    switch(rb_scan_args(argc, argv, "12", &a, &b, &c)) {
+    case 3:
+        flags = NUM2INT(c);
+        if ((flags & ~DB_RMW) == DB_GET_BOTH) {
+#if BDB_VERSION < 20600
+	    return bdb_has_both_internal(obj, a, b, Qtrue);
+#else
+            b = bdb_test_dump(obj, &data, b, FILTER_VALUE);
+	    data.flags |= DB_DBT_MALLOC;
+#endif
+        }
+	break;
+    case 2:
+	flagss = flags = NUM2INT(b);
+	break;
+    }
+    a = bdb_test_recno(obj, &key, &recno, a);
+    SET_PARTIAL(dbst, data);
+    flags |= TEST_INIT_LOCK(dbst);
+    ret = bdb_test_error(dbst->dbp->get(dbst->dbp, txnid, &key, &data, flags));
+    if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
+        return notfound;
+    if (((flags & ~DB_RMW) == DB_GET_BOTH) ||
+	((flags & ~DB_RMW) == DB_SET_RECNO)) {
+        return bdb_assoc(obj, &key, &data);
+    }
+    if (dyna) {
+	return test_load_dyna(obj, &key, &data);
+    }
+    else {
+	return bdb_test_load(obj, &data, FILTER_VALUE);
+    }
+}
+
+VALUE
+bdb_get(int argc, VALUE *argv, VALUE obj)
+{
+    return bdb_get_internal(argc, argv, obj, Qnil, 0);
+}
+
+static VALUE
+bdb_get_dyna(int argc, VALUE *argv, VALUE obj)
+{
+    return bdb_get_internal(argc, argv, obj, Qnil, 1);
+}
+
+static VALUE
+bdb_fetch(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE key, if_none;
+    VALUE val;
+
+    rb_scan_args(argc, argv, "11", &key, &if_none);
+    val = bdb_get_internal(1, argv, obj, Qundef, 1);
+    if (val == Qundef) {
+	if (rb_block_given_p()) {
+	    if (argc > 1) {
+		rb_raise(rb_eArgError, "wrong # of arguments", argc);
+	    }
+	    return rb_yield(key);
+	}
+	if (argc == 1) {
+	    rb_raise(rb_eIndexError, "key not found");
+	}
+	return if_none;
+    }
+    return val;
+}
+
+#if BDB_VERSION >= 30300
+
+static VALUE
+bdb_pget(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE a = Qnil;
+    VALUE b = Qnil;
+    VALUE c;
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT pkey, data, skey;
+    int flagss;
+    int ret, flags;
+    db_recno_t srecno;
+
+    INIT_TXN(txnid, obj, dbst);
+    flagss = flags = 0;
+    MEMZERO(&skey, DBT, 1);
+    MEMZERO(&pkey, DBT, 1);
+    MEMZERO(&data, DBT, 1);
+    data.flags |= DB_DBT_MALLOC;
+    pkey.flags |= DB_DBT_MALLOC;
+    switch(rb_scan_args(argc, argv, "12", &a, &b, &c)) {
+    case 3:
+        flags = NUM2INT(c);
+        if ((flags & ~DB_RMW) == DB_GET_BOTH) {
+            b = bdb_test_dump(obj, &data, b, FILTER_VALUE);
+	    data.flags |= DB_DBT_MALLOC;
+        }
+	break;
+    case 2:
+	flagss = flags = NUM2INT(b);
+	break;
+    }
+    a = bdb_test_recno(obj, &skey, &srecno, a);
+    SET_PARTIAL(dbst, data);
+    flags |= TEST_INIT_LOCK(dbst);
+    ret = bdb_test_error(dbst->dbp->pget(dbst->dbp, txnid, &skey, &pkey, &data, flags));
+    if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
+        return Qnil;
+    if (((flags & ~DB_RMW) == DB_GET_BOTH) ||
+	((flags & ~DB_RMW) == DB_SET_RECNO)) {
+        return bdb_assoc2(obj, &skey, &pkey, &data);
+    }
+    return bdb_assoc(obj, &pkey, &data);
+}
+
+#endif
+
+#if BDB_VERSION >= 30100
+
+static VALUE
+bdb_btree_key_range(obj, a)
+    VALUE obj, a;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT key;
+    db_recno_t recno;
+    DB_KEY_RANGE key_range;
+    volatile VALUE b = Qnil;
+
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    b = bdb_test_recno(obj, &key, &recno, a);
+    bdb_test_error(dbst->dbp->key_range(dbst->dbp, txnid, &key, &key_range, 0));
+    return rb_struct_new(bdb_sKeyrange, 
+			 rb_float_new(key_range.less),
+			 rb_float_new(key_range.equal), 
+			 rb_float_new(key_range.greater));
+}
+#endif
+
+#if BDB_VERSION >= 40416
+
+
+struct data_flags {
+    DB_COMPACT *cdata;
+    int flags;
+};
+
+static VALUE
+bdb_compact_i(obj, dataobj)
+    VALUE obj, dataobj;
+{
+    VALUE key, value;
+    char *str;
+    struct data_flags *dtf;
+
+    Data_Get_Struct(dataobj, struct data_flags, dtf);
+    key = rb_ary_entry(obj, 0);
+    value = rb_ary_entry(obj, 1);
+    key = rb_obj_as_string(key);
+    str = StringValuePtr(key);
+    if (strcmp(str, "compact_timeout") == 0) {
+	dtf->cdata->compact_timeout = NUM2LONG(value);
+    }
+    else if (strcmp(str, "compact_fillpercent") == 0) {
+	dtf->cdata->compact_fillpercent = NUM2INT(value);
+    }
+    else if (strcmp(str, "flags") == 0) {
+	dtf->flags = NUM2INT(value);
+    }
+    else {
+	rb_warning("Unknown option %s", str);
+    }
+    return Qnil;
+}
+    
+static VALUE
+bdb_treerec_compact(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT start, stop, end;
+    DBT *pstart, *pstop;
+    DB_COMPACT cdata;
+    db_recno_t recno_start, recno_stop;
+    int flags;
+    VALUE a, b, c, result;
+
+    pstop = pstart = NULL;
+    MEMZERO(&cdata, DB_COMPACT, 1);
+    flags = 0;
+    INIT_TXN(txnid, obj, dbst);
+    switch (rb_scan_args(argc, argv, "03", &a, &b, &c)) {
+    case 3:
+	if (FIXNUM_P(c)) {
+	    flags = NUM2INT(c);
+	}
+	else {
+	    struct data_flags *dtf;
+	    VALUE dtobj;
+		
+	    dtobj = Data_Make_Struct(rb_cData, struct data_flags, 0, free, dtf);
+	    dtf->cdata = &cdata;
+	    dtf->flags = 0;
+	    rb_iterate(rb_each, c, bdb_compact_i, dtobj);
+	    flags = dtf->flags;
+	}
+	/* ... */
+    case 2:
+	if (!NIL_P(b)) {
+	    MEMZERO(&stop, DBT, 1);
+	    b = bdb_test_recno(obj, &start, &recno_stop, b);
+	    pstop = &stop;
+	}
+	/* ... */
+    case 1:
+	if (!NIL_P(a)) {
+	    MEMZERO(&start, DBT, 1);
+	    a = bdb_test_recno(obj, &start, &recno_start, a);
+	    pstart = &start;
+	}
+    }
+    MEMZERO(&end, DBT, 1);
+    bdb_test_error(dbst->dbp->compact(dbst->dbp, txnid, pstart, pstop, &cdata,
+				      flags, &end));
+    result = rb_hash_new();
+    rb_hash_aset(result, rb_tainted_str_new2("end"), bdb_test_load_key(obj, &end));
+    rb_hash_aset(result, rb_tainted_str_new2("compact_deadlock"),
+		 INT2NUM(cdata.compact_deadlock));
+    rb_hash_aset(result, rb_tainted_str_new2("compact_levels"),
+		 INT2NUM(cdata.compact_levels));
+    rb_hash_aset(result, rb_tainted_str_new2("compact_pages_free"),
+		 INT2NUM(cdata.compact_pages_free));
+    rb_hash_aset(result, rb_tainted_str_new2("compact_pages_examine"),
+		 INT2NUM(cdata.compact_pages_examine));
+    rb_hash_aset(result, rb_tainted_str_new2("compact_pages_truncated"),
+		 INT2NUM(cdata.compact_pages_truncated));
+    return result;
+}
+
+#endif
+
+
+#if BDB_VERSION >= 20600
+
+static VALUE
+bdb_count(obj, a)
+    VALUE obj, a;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    DBT key, data;
+    int ret, flags27;
+    db_recno_t recno;
+    db_recno_t count;
+    volatile VALUE b = Qnil;
+
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    b = bdb_test_recno(obj, &key, &recno, a);
+    MEMZERO(&data, DBT, 1);
+    data.flags |= DB_DBT_MALLOC;
+    SET_PARTIAL(dbst, data);
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+    flags27 = TEST_INIT_LOCK(dbst);
+    bdb_cache_error(dbcp->c_get(dbcp, &key, &data, DB_SET | flags27),
+		    dbcp->c_close(dbcp), ret);
+    if (ret == DB_NOTFOUND) {
+        dbcp->c_close(dbcp);
+        return INT2NUM(0);
+    }
+#if BDB_VERSION >= 30100
+    bdb_cache_error(dbcp->c_count(dbcp, &count, 0), dbcp->c_close(dbcp), ret);
+    dbcp->c_close(dbcp);
+    return INT2NUM(count);
+#else
+    count = 1;
+    while (1) {
+        bdb_cache_error(dbcp->c_get(dbcp, &key, &data, DB_NEXT_DUP | flags27),
+			dbcp->c_close(dbcp), ret);
+        if (ret == DB_NOTFOUND) {
+            dbcp->c_close(dbcp);
+            return INT2NUM(count);
+        }
+        if (ret == DB_KEYEMPTY) continue;
+        FREE_KEY(dbst, key);
+        if (data.flags & DB_DBT_MALLOC)
+            free(data.data);
+        count++;
+    }
+    return INT2NUM(-1);
+#endif
+}
+
+#endif
+
+#if BDB_VERSION >= 30000
+
+static VALUE
+bdb_consume(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT key, data;
+    DBC *dbcp;
+    int ret;
+    db_recno_t recno;
+
+    rb_secure(4);
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    MEMZERO(&data, DBT, 1);
+    recno = 1;
+    key.data = &recno;
+    key.size = sizeof(db_recno_t);
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+    bdb_cache_error(dbcp->c_get(dbcp, &key, &data, DB_CONSUME),
+		    dbcp->c_close(dbcp), ret);
+    dbcp->c_close(dbcp);
+    if (ret == DB_NOTFOUND) {
+	return Qnil;
+    }
+    return bdb_assoc(obj, &key, &data);
+}
+
+#endif
+
+static VALUE
+bdb_has_key(obj, key)
+    VALUE obj, key;
+{
+    return (bdb_get_internal(1, &key, obj, Qundef, 0) == Qundef)?Qfalse:Qtrue;
+}
+
+static VALUE
+bdb_has_both_internal(obj, a, b, flag)
+    VALUE obj, a, b, flag;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    DBT key, data;
+    DBT datas;
+    int flagss;
+    int ret, flags;
+    db_recno_t recno;
+    volatile VALUE c = Qnil;
+    volatile VALUE d = Qnil;
+
+    INIT_TXN(txnid, obj, dbst);
+    flagss = flags = 0;
+    MEMZERO(&key, DBT, 1);
+    MEMZERO(&data, DBT, 1);
+    MEMZERO(&datas, DBT, 1);
+    c = bdb_test_recno(obj, &key, &recno, a);
+    d = bdb_test_dump(obj, &datas, b, FILTER_VALUE);
+    data.flags |= DB_DBT_MALLOC;
+    SET_PARTIAL(dbst, data);
+    flags |= TEST_INIT_LOCK(dbst);
+#if BDB_VERSION < 20600
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp));
+#else
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+#endif
+    bdb_cache_error(dbcp->c_get(dbcp, &key, &data, DB_SET),
+		    dbcp->c_close(dbcp), ret);
+    if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY) {
+	dbcp->c_close(dbcp);
+        return (flag == Qtrue)?Qnil:Qfalse;
+    }
+    if (datas.size == data.size &&
+	memcmp(datas.data, data.data, data.size) == 0) {
+	dbcp->c_close(dbcp);
+	if (flag == Qtrue) {
+	    return bdb_assoc(obj, &key, &data);
+	}
+	else {
+	    FREE_KEY(dbst, key);
+	    free(data.data);
+	    return Qtrue;
+	}
+    }
+#if BDB_VERSION < 20600
+#define DB_NEXT_DUP 0
+    FREE_KEY(dbst, key);
+    free(data.data);
+    dbcp->c_close(dbcp);
+    return (flag == Qtrue)?Qnil:Qfalse;
+#endif
+#if BDB_VERSION < 30100
+    if (RECNUM_TYPE(dbst)) {
+	free(data.data);
+	dbcp->c_close(dbcp);
+	return Qfalse;
+    }
+#endif
+    while (1) {
+	FREE_KEY(dbst, key);
+	free(data.data);
+	MEMZERO(&data, DBT, 1);
+	data.flags |= DB_DBT_MALLOC;
+	bdb_cache_error(dbcp->c_get(dbcp, &key, &data, DB_NEXT_DUP),
+			dbcp->c_close(dbcp), ret);
+	if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY) {
+	    dbcp->c_close(dbcp);
+	    return Qfalse;
+	}
+	if (datas.size == data.size &&
+	    memcmp(datas.data, data.data, data.size) == 0) {
+	    FREE_KEY(dbst, key);
+	    free(data.data);
+	    dbcp->c_close(dbcp);
+	    return Qtrue;
+	}
+    }
+    return Qfalse;
+}
+
+static VALUE
+bdb_has_both(obj, a, b)
+    VALUE obj, a, b;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT key, data;
+    int ret, flags;
+    db_recno_t recno;
+    volatile VALUE c = Qnil;
+    volatile VALUE d = Qnil;
+
+#if BDB_VERSION < 20600
+    return bdb_has_both_internal(obj, a, b, Qfalse);
+#else
+    INIT_TXN(txnid, obj, dbst);
+#if BDB_VERSION < 30100
+    if (RECNUM_TYPE(dbst)) {
+	return bdb_has_both_internal(obj, a, b, Qfalse);
+    }
+#endif
+    MEMZERO(&key, DBT, 1);
+    MEMZERO(&data, DBT, 1);
+    c = bdb_test_recno(obj, &key, &recno, a);
+    d = bdb_test_dump(obj, &data, b, FILTER_VALUE);
+    data.flags |= DB_DBT_MALLOC;
+    SET_PARTIAL(dbst, data);
+    flags = DB_GET_BOTH | TEST_INIT_LOCK(dbst);
+    ret = bdb_test_error(dbst->dbp->get(dbst->dbp, txnid, &key, &data, flags));
+    if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
+        return Qfalse;
+    free(data.data);
+    return Qtrue;
+#endif
+}
+
+VALUE
+bdb_del(obj, a)
+    VALUE a, obj;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    int flag = 0;
+    DBT key;
+    int ret;
+    db_recno_t recno;
+    volatile VALUE b = Qnil;
+
+    rb_secure(4);
+    INIT_TXN(txnid, obj, dbst);
+    if (txnid == NULL && (dbst->options & BDB_AUTO_COMMIT)) {
+        flag |= DB_AUTO_COMMIT;
+    }
+    MEMZERO(&key, DBT, 1);
+    b = bdb_test_recno(obj, &key, &recno, a);
+    ret = bdb_test_error(dbst->dbp->del(dbst->dbp, txnid, &key, flag));
+    if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
+        return Qnil;
+    else
+        return obj;
+}
+
+static VALUE
+bdb_empty(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    DBT key, data;
+    int ret, flags;
+    db_recno_t recno;
+
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&data, DBT, 1);
+    data.flags = DB_DBT_MALLOC;
+#if BDB_VERSION < 20600
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp));
+#else
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+#endif
+    SET_PARTIAL(dbst, data);
+    flags = TEST_INIT_LOCK(dbst);
+    bdb_cache_error(dbcp->c_get(dbcp, &key, &data, DB_FIRST | flags),
+		    dbcp->c_close(dbcp), ret);
+    if (ret == DB_NOTFOUND) {
+        dbcp->c_close(dbcp);
+        return Qtrue;
+    }
+    FREE_KEY(dbst, key);
+    free(data.data);
+    dbcp->c_close(dbcp);
+    return Qfalse;
+}
+
+static VALUE
+bdb_lgth_intern(obj, delete)
+    VALUE obj, delete;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    DBT key, data;
+    int ret, value, flags;
+    db_recno_t recno;
+
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&data, DBT, 1);
+    data.flags = DB_DBT_MALLOC;
+    value = 0;
+#if BDB_VERSION < 20600
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp));
+#else
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+#endif
+    SET_PARTIAL(dbst, data);
+    flags = TEST_INIT_LOCK(dbst);
+    do {
+        bdb_cache_error(dbcp->c_get(dbcp, &key, &data, DB_NEXT | flags),
+			dbcp->c_close(dbcp), ret);
+        if (ret == DB_NOTFOUND) {
+            dbcp->c_close(dbcp);
+            return INT2NUM(value);
+        }
+	if (ret == DB_KEYEMPTY) continue;
+	FREE_KEY(dbst, key);
+	free(data.data);
+        value++;
+	if (delete == Qtrue) {
+	    bdb_test_error(dbcp->c_del(dbcp, 0));
+	}
+    } while (1);
+    return INT2NUM(-1);
+}
+
+static VALUE
+bdb_length(obj)
+    VALUE obj;
+{
+    return bdb_lgth_intern(obj, Qfalse);
+}
+
+typedef struct {
+    int sens;
+    VALUE replace;
+    VALUE db;
+    VALUE set;
+    DBC *dbcp;
+#if BDB_VERSION >= 30300
+    void *data;
+    int len;
+#endif
+    int primary;
+    int type;
+} eachst;
+
+static VALUE
+bdb_each_ensure(st)
+    eachst *st;
+{
+#if BDB_VERSION >= 30300
+    if (st->len && st->data) {
+	free(st->data);
+    }
+#endif
+    st->dbcp->c_close(st->dbcp);
+    return Qnil;
+}
+
+static void
+bdb_treat(st, pkey, key, data)
+    eachst *st;
+    DBT *pkey, *key, *data;
+{
+    bdb_DB *dbst;
+    DBC *dbcp;
+    VALUE res = Qnil;
+
+    GetDB(st->db, dbst);
+    dbcp = st->dbcp;
+    switch (st->type) {
+    case BDB_ST_DUPU:
+	FREE_KEY(dbst, *key);
+	res = bdb_test_load(st->db, data, FILTER_VALUE);
+	if (TYPE(st->replace) == T_ARRAY) {
+	    rb_ary_push(st->replace, res);
+	}
+	else {
+	    rb_yield(res);
+	}
+	break;
+    case BDB_ST_DUPKV:
+	rb_yield(bdb_assoc_dyna(st->db, key, data));
+	break;
+    case BDB_ST_DUPVAL:
+	res = test_load_dyna(st->db, key, data);
+	if (TYPE(st->replace) == T_ARRAY) {
+	    rb_ary_push(st->replace, res);
+	}
+	else {
+	    rb_yield(res);
+	}
+	break;
+    case BDB_ST_KEY:
+	if (data->flags & DB_DBT_MALLOC) {
+	    free(data->data);
+            data->flags &= ~DB_DBT_MALLOC;
+	}
+	rb_yield(bdb_test_load_key(st->db, key));
+	break;
+
+    case BDB_ST_VALUE:
+	FREE_KEY(dbst, *key);
+	res = rb_yield(bdb_test_load(st->db, data, FILTER_VALUE));
+	if (st->replace == Qtrue) {
+	    MEMZERO(data, DBT, 1);
+	    res = bdb_test_dump(st->db, data, res, FILTER_VALUE);
+	    SET_PARTIAL(dbst, *data);
+	    bdb_test_error(dbcp->c_put(dbcp, key, data, DB_CURRENT));
+	}
+	else if (st->replace != Qfalse) {
+	    rb_ary_push(st->replace, res);
+	}
+	break;
+     case BDB_ST_SELECT:
+	res = bdb_assoc(st->db, key, data);
+	if (RTEST(rb_yield(res))) {
+	    rb_ary_push(st->replace, res);
+	}
+	break;
+    case BDB_ST_KV:
+	if (st->primary) {
+	    rb_yield(bdb_assoc3(st->db, key, pkey, data));
+	}
+	else {
+	    rb_yield(bdb_assoc_dyna(st->db, key, data));
+	}
+	break;
+    case BDB_ST_REJECT:
+    {
+	VALUE ary = bdb_assoc(st->db, key, data);
+	if (!RTEST(rb_yield(ary))) {
+	    rb_hash_aset(st->replace, RARRAY(ary)->ptr[0], RARRAY(ary)->ptr[1]);
+	}
+	break;
+    }
+    case BDB_ST_DELETE:
+	if (RTEST(rb_yield(bdb_assoc(st->db, key, data)))) {
+	    bdb_test_error(dbcp->c_del(dbcp, 0));
+	}
+	break;
+    }
+}
+
+static VALUE
+bdb_i_each_kv(st)
+    eachst *st;
+{
+    bdb_DB *dbst;
+    DBC *dbcp;
+    DBT pkey, key, data, orig;
+    int ret, init = Qfalse, prefix = Qfalse;
+    db_recno_t recno;
+    volatile VALUE res = Qnil;
+    
+    prefix = st->type & BDB_ST_PREFIX;
+    st->type &= ~BDB_ST_PREFIX;
+    GetDB(st->db, dbst);
+    dbcp = st->dbcp;
+    MEMZERO(&key, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&orig, DBT, 1);
+    MEMZERO(&data, DBT, 1);
+    data.flags = DB_DBT_MALLOC;
+    SET_PARTIAL(dbst, data);
+    MEMZERO(&pkey, DBT, 1);
+    pkey.flags = DB_DBT_MALLOC;
+    if (!NIL_P(st->set)) {
+	res = bdb_test_recno(st->db, &key, &recno, st->set);
+        if (prefix) {
+            init = Qtrue;
+            orig.size = key.size;
+            orig.data = ALLOCA_N(char, key.size);
+            MEMCPY(orig.data, key.data, char, key.size);
+        }
+#if BDB_VERSION >= 30300
+	if (st->type == BDB_ST_KV && st->primary) {
+	    ret = bdb_test_error(dbcp->c_pget(dbcp, &key, &pkey, &data, 
+                                              (st->type & BDB_ST_DUP)?DB_SET:
+                                              DB_SET_RANGE));
+	}
+	else
+#endif
+	{
+#if BDB_VERSION < 20600
+	    key.flags |= DB_DBT_MALLOC;
+#endif
+	    ret = bdb_test_error(dbcp->c_get(dbcp, &key, &data, 
+                                             (st->type & BDB_ST_DUP)?DB_SET:
+                                             DB_SET_RANGE));
+#if BDB_VERSION < 20600
+	    key.flags &= ~DB_DBT_MALLOC;
+#endif
+	}
+	if (ret == DB_NOTFOUND) {
+	    return Qfalse;
+	}
+        bdb_treat(st, &pkey, &key, &data);
+    }
+    do {
+#if BDB_VERSION >= 30300
+	if (st->type == BDB_ST_KV && st->primary) {
+	    ret = bdb_test_error(dbcp->c_pget(dbcp, &key, &pkey, &data, st->sens));
+	}
+	else
+#endif
+	{
+#if BDB_VERSION < 20600
+	    key.flags |= DB_DBT_MALLOC;
+#endif
+	    ret = bdb_test_error(dbcp->c_get(dbcp, &key, &data, st->sens));
+#if BDB_VERSION < 20600
+	    key.flags &= ~DB_DBT_MALLOC;
+#endif
+	}
+        if (ret == DB_NOTFOUND) {
+            return Qnil;
+        }
+	if (ret == DB_KEYEMPTY) continue;
+        if (prefix) {
+            if (!init) {
+                init = Qtrue;
+                orig.size = key.size;
+                orig.data = ALLOCA_N(char, key.size);
+                MEMCPY(orig.data, key.data, char, key.size);
+            }
+            if (key.size >= orig.size &&
+                !memcmp(key.data, orig.data, orig.size)) {
+                bdb_treat(st, &pkey, &key, &data);
+            }
+        }
+        else {
+            bdb_treat(st, &pkey, &key, &data);
+        }
+    } while (1);
+    return Qnil;
+}
+
+#if BDB_VERSION >= 30300
+
+static VALUE
+bdb_i_each_kv_bulk(st)
+    eachst *st;
+{
+    bdb_DB *dbst;
+    DBC *dbcp;
+    DBT key, data;
+    DBT rkey, rdata;
+    DBT pkey;
+    int ret, init;
+    db_recno_t recno;
+    void *p;
+    volatile VALUE res = Qnil;
+    
+    GetDB(st->db, dbst);
+    dbcp = st->dbcp;
+    MEMZERO(&key, DBT, 1);
+    MEMZERO(&pkey, DBT, 1);
+    MEMZERO(&rkey, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&data, DBT, 1);
+    MEMZERO(&rdata, DBT, 1);
+    st->data = data.data = (void *)ALLOC_N(char, st->len);
+    data.ulen = st->len;
+    data.flags = DB_DBT_USERMEM;
+    SET_PARTIAL(dbst, data);
+    SET_PARTIAL(dbst, rdata);
+    init = 1;
+    do {
+	if (init && !NIL_P(st->set)) {
+	    res = bdb_test_recno(st->db, &key, &recno, st->set);
+	    ret = bdb_test_error(dbcp->c_get(dbcp, &key, &data, 
+                                             ((st->type & BDB_ST_DUP)?DB_SET:
+                                              DB_SET_RANGE)|DB_MULTIPLE_KEY));
+	    init = 0;
+	}
+	else {
+	    ret = bdb_test_error(dbcp->c_get(dbcp, &key, &data, st->sens | DB_MULTIPLE_KEY));
+	}
+        if (ret == DB_NOTFOUND) {
+            return Qnil;
+        }
+	if (ret == DB_KEYEMPTY) continue;
+	for (DB_MULTIPLE_INIT(p, &data);;) {
+	    if (RECNUM_TYPE(dbst)) {
+		DB_MULTIPLE_RECNO_NEXT(p, &data, recno, 
+				       rdata.data, rdata.size);
+	    }
+	    else {
+		DB_MULTIPLE_KEY_NEXT(p, &data, rkey.data, rkey.size,
+				     rdata.data, rdata.size);
+	    }
+	    if (p == NULL) break;
+	    bdb_treat(st, 0, &rkey, &rdata);
+	}
+    } while (1);
+    return Qnil;
+}
+
+#endif
+
+VALUE
+bdb_each_kvc(argc, argv, obj, sens, replace, type)
+    VALUE obj, *argv;
+    int argc, sens;
+    VALUE replace;
+    int type;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    eachst st;
+    int flags = 0;
+
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+	VALUE g, f = argv[argc - 1];
+	if ((g = rb_hash_aref(f, rb_intern("flags"))) != RHASH(f)->ifnone ||
+	    (g = rb_hash_aref(f, rb_str_new2("flags"))) != RHASH(f)->ifnone) {
+	    flags = NUM2INT(g);
+	}
+	argc--;
+    }
+	
+    MEMZERO(&st, eachst, 1);
+
+#if BDB_VERSION >= 30300
+    {
+	VALUE bulkv = Qnil;
+
+	st.set = Qnil;
+	if (type & BDB_ST_ONE) {
+	    rb_scan_args(argc, argv, "01", &st.set);
+	}
+	else {
+	    if (type & BDB_ST_DUP) {
+		rb_scan_args(argc, argv, "11", &st.set, &bulkv);
+	    }
+	    else {
+		if (rb_scan_args(argc, argv, "02", &st.set, &bulkv) == 2) {
+		    if (bulkv == Qtrue || bulkv == Qfalse) {
+			st.primary = RTEST(bulkv);
+			bulkv = Qnil;
+		    }
+		}
+	    }
+	}
+	if (!NIL_P(bulkv)) {
+	    st.len = 1024 * NUM2INT(bulkv);
+	    if (st.len < 0) {
+		rb_raise(bdb_eFatal, "negative value for bulk retrieval");
+	    }
+	}
+    }
+#else
+    if (type & BDB_ST_DUP) {
+	if (argc != 1) {
+	    rb_raise(bdb_eFatal, "invalid number of arguments (%d for 1)", argc);
+	}
+	st.set = argv[0];
+    }
+    else {
+	rb_scan_args(argc, argv, "01", &st.set);
+    }
+#endif
+    type &= ~BDB_ST_ONE;
+    if ((type & ~BDB_ST_PREFIX) == BDB_ST_DELETE) {
+	rb_secure(4);
+    }
+    INIT_TXN(txnid, obj, dbst);
+#if BDB_VERSION < 20600
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp));
+#else
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, flags));
+#endif
+    st.db = obj;
+    st.dbcp = dbcp;
+    st.sens = sens | TEST_INIT_LOCK(dbst);
+    st.replace = replace;
+    st.type = type & ~BDB_ST_ONE;
+#if BDB_VERSION >= 30300
+    if (st.len) {
+	rb_ensure(bdb_i_each_kv_bulk, (VALUE)&st, bdb_each_ensure, (VALUE)&st);
+    }
+    else
+#endif
+    {
+	rb_ensure(bdb_i_each_kv, (VALUE)&st, bdb_each_ensure, (VALUE)&st);
+    }
+    if (replace == Qtrue || replace == Qfalse) {
+	return obj;
+    }
+    else {
+	return st.replace;
+    }
+}
+
+#if BDB_VERSION >= 20600
+
+static VALUE
+bdb_get_dup(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE result = Qfalse;
+    if (!rb_block_given_p()) {
+	result = rb_ary_new();
+    }
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT_DUP, result, BDB_ST_DUPU);
+}
+
+static VALUE
+bdb_common_each_dup(int argc, VALUE *argv, VALUE obj)
+{
+    if (!rb_block_given_p()) {
+	rb_raise(bdb_eFatal, "each_dup called out of an iterator");
+    }
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT_DUP, Qfalse, BDB_ST_DUPKV);
+}
+
+static VALUE
+bdb_common_each_dup_val(int argc, VALUE *argv, VALUE obj)
+{
+    if (!rb_block_given_p()) {
+	rb_raise(bdb_eFatal, "each_dup called out of an iterator");
+    }
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT_DUP, Qfalse, BDB_ST_DUPVAL);
+}
+
+static VALUE
+bdb_common_dups(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE result = rb_ary_new();
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT_DUP, result, BDB_ST_DUPVAL);
+}
+
+#endif
+
+static VALUE
+bdb_delete_if(int argc, VALUE *argv, VALUE obj)
+{
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT, Qfalse, BDB_ST_DELETE | BDB_ST_ONE);
+}
+
+static VALUE
+bdb_reject(int argc, VALUE *argv, VALUE obj)
+{
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT, rb_hash_new(), BDB_ST_REJECT);
+}
+
+VALUE
+bdb_each_value(int argc, VALUE *argv, VALUE obj)
+{ 
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT, Qfalse, BDB_ST_VALUE);
+}
+
+VALUE
+bdb_each_eulav(int argc, VALUE *argv, VALUE obj)
+{
+    return bdb_each_kvc(argc, argv, obj, DB_PREV, Qfalse, BDB_ST_VALUE | BDB_ST_ONE);
+}
+
+VALUE
+bdb_each_key(int argc, VALUE *argv, VALUE obj)
+{ 
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT, Qfalse, BDB_ST_KEY); 
+}
+
+static VALUE
+bdb_each_yek(int argc, VALUE *argv, VALUE obj) 
+{ 
+    return bdb_each_kvc(argc, argv, obj, DB_PREV, Qfalse, BDB_ST_KEY | BDB_ST_ONE);
+}
+
+static VALUE
+bdb_each_pair(int argc, VALUE *argv, VALUE obj) 
+{
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT, Qfalse, BDB_ST_KV);
+}
+
+static VALUE
+bdb_each_prefix(int argc, VALUE *argv, VALUE obj) 
+{
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT, Qfalse, BDB_ST_KV | BDB_ST_PREFIX);
+}
+
+static VALUE
+bdb_each_riap(int argc, VALUE *argv, VALUE obj) 
+{
+    return bdb_each_kvc(argc, argv, obj, DB_PREV, Qfalse, BDB_ST_KV | BDB_ST_ONE);
+}
+
+static VALUE
+bdb_each_xiferp(int argc, VALUE *argv, VALUE obj) 
+{
+    return bdb_each_kvc(argc, argv, obj, DB_PREV, Qfalse, 
+                        BDB_ST_KV | BDB_ST_ONE | BDB_ST_PREFIX);
+}
+
+static VALUE
+bdb_each_pair_prim(int argc, VALUE *argv, VALUE obj) 
+{
+    VALUE tmp[2] = {Qnil, Qtrue};
+    rb_scan_args(argc, argv, "01", tmp);
+    return bdb_each_kvc(2, tmp, obj, DB_NEXT, Qfalse, BDB_ST_KV);
+}
+
+static VALUE
+bdb_each_riap_prim(int argc, VALUE *argv, VALUE obj) 
+{
+    VALUE tmp[2] = {Qnil, Qtrue};
+    rb_scan_args(argc, argv, "01", tmp);
+    return bdb_each_kvc(2, tmp, obj, DB_PREV, Qfalse, BDB_ST_KV);
+}
+
+VALUE
+bdb_to_type(obj, result, flag)
+    VALUE obj, result, flag;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    DBT key, data;
+    int ret, flags;
+    db_recno_t recno;
+
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&data, DBT, 1);
+    data.flags = DB_DBT_MALLOC;
+#if BDB_VERSION < 20600
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp));
+#else
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+#endif
+    SET_PARTIAL(dbst, data);
+    flags = ((flag == Qnil)?DB_PREV:DB_NEXT) | TEST_INIT_LOCK(dbst);
+    do {
+        bdb_cache_error(dbcp->c_get(dbcp, &key, &data, flags),
+			dbcp->c_close(dbcp), ret);
+        if (ret == DB_NOTFOUND) {
+            dbcp->c_close(dbcp);
+            return result;
+        }
+	if (ret == DB_KEYEMPTY) continue;
+	switch (TYPE(result)) {
+	case T_ARRAY:
+	    if (flag == Qtrue) {
+		rb_ary_push(result, bdb_assoc(obj, &key, &data));
+	    }
+	    else {
+		rb_ary_push(result, bdb_test_load(obj, &data, FILTER_VALUE));
+	    }
+	    break;
+	case T_HASH:
+	    if (flag == Qtrue) {
+		rb_hash_aset(result, bdb_test_load_key(obj, &key), 
+			     bdb_test_load(obj, &data, FILTER_VALUE));
+	    }
+	    else {
+		rb_hash_aset(result, bdb_test_load(obj, &data, FILTER_VALUE), 
+			     bdb_test_load_key(obj, &key));
+	    }
+	}
+
+    } while (1);
+    return result;
+}
+
+static VALUE
+bdb_to_a(obj)
+    VALUE obj;
+{
+    return bdb_to_type(obj, rb_ary_new(), Qtrue);
+}
+
+static VALUE
+each_pair(obj)
+    VALUE obj;
+{
+    return rb_funcall(obj, rb_intern("each_pair"), 0, 0);
+}
+
+static VALUE
+bdb_update_i(pair, obj)
+    VALUE pair, obj;
+{
+    Check_Type(pair, T_ARRAY);
+    if (RARRAY(pair)->len < 2) {
+	rb_raise(rb_eArgError, "pair must be [key, value]");
+    }
+    bdb_put(2, RARRAY(pair)->ptr, obj);
+    return Qnil;
+}
+
+static VALUE
+bdb_update(obj, other)
+    VALUE obj, other;
+{
+    rb_iterate(each_pair, other, bdb_update_i, obj);
+    return obj;
+}
+
+VALUE
+bdb_clear(int argc, VALUE *argv, VALUE obj)
+{
+#if BDB_VERSION >= 30300
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    unsigned int count = 0;
+#endif
+    int flags = 0;
+
+    rb_secure(4);
+#if BDB_VERSION >= 30300
+    INIT_TXN(txnid, obj, dbst);
+    if (txnid == NULL && (dbst->options & BDB_AUTO_COMMIT)) {
+      flags |= DB_AUTO_COMMIT;
+    }
+    bdb_test_error(dbst->dbp->truncate(dbst->dbp, txnid, &count, flags));
+    return INT2NUM(count);
+#else
+    flags = 0;
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+	VALUE g, f = argv[argc - 1];
+	if ((g = rb_hash_aref(f, rb_intern("flags"))) != RHASH(f)->ifnone ||
+	    (g = rb_hash_aref(f, rb_str_new2("flags"))) != RHASH(f)->ifnone) {
+	    flags = NUM2INT(g);
+	}
+	argc--;
+    }
+    if (argc) {
+	flags = NUM2INT(argv[0]);
+    }
+    return bdb_lgth_intern(obj, Qtrue, flags);
+#endif
+}
+
+static VALUE
+bdb_replace(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE g;
+    int flags;
+
+    if (argc == 0 || argc > 2) {
+	rb_raise(rb_eArgError, "invalid number of arguments (0 for 1)");
+    }
+    flags = 0;
+    if (TYPE(argv[argc - 1]) == T_HASH) {
+	VALUE f = argv[argc - 1];
+	if ((g = rb_hash_aref(f, rb_intern("flags"))) != RHASH(f)->ifnone ||
+	    (g = rb_hash_aref(f, rb_str_new2("flags"))) != RHASH(f)->ifnone) {
+	    flags = NUM2INT(g);
+	}
+	argc--;
+    }
+    if (argc == 2) {
+	flags = NUM2INT(argv[1]);
+    }
+    g = INT2FIX(flags);
+    bdb_clear(1, &g, obj);
+    rb_iterate(each_pair, argv[0], bdb_update_i, obj);
+    return obj;
+}
+
+static VALUE
+bdb_invert(obj)
+    VALUE obj;
+{
+    return bdb_to_type(obj, rb_hash_new(), Qfalse);
+}
+
+static VALUE
+bdb_to_hash(obj)
+    VALUE obj;
+{
+    return bdb_to_type(obj, rb_hash_new(), Qtrue);
+}
+ 
+static VALUE
+bdb_kv(obj, type)
+    VALUE obj;
+    int type;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    DBT key, data;
+    int ret, flags;
+    db_recno_t recno;
+    VALUE ary;
+
+    ary = rb_ary_new();
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&data, DBT, 1);
+    data.flags = DB_DBT_MALLOC;
+#if BDB_VERSION < 20600
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp));
+#else
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+#endif
+    SET_PARTIAL(dbst, data);
+    flags = DB_NEXT | TEST_INIT_LOCK(dbst);
+    do {
+        bdb_cache_error(dbcp->c_get(dbcp, &key, &data, flags),
+			dbcp->c_close(dbcp), ret);
+        if (ret == DB_NOTFOUND) {
+            dbcp->c_close(dbcp);
+            return ary;
+        }
+	if (ret == DB_KEYEMPTY) continue;
+	switch (type) {
+	case BDB_ST_VALUE:
+	    FREE_KEY(dbst, key);
+	    rb_ary_push(ary, bdb_test_load(obj, &data, FILTER_VALUE));
+	    break;
+	case BDB_ST_KEY:
+	    free(data.data);
+	    rb_ary_push(ary, bdb_test_load_key(obj, &key));
+	    break;
+	}
+    } while (1);
+    return ary;
+}
+
+static VALUE
+bdb_values(obj)
+    VALUE obj;
+{
+    return bdb_kv(obj, BDB_ST_VALUE);
+}
+
+static VALUE
+bdb_keys(obj)
+    VALUE obj;
+{
+    return bdb_kv(obj, BDB_ST_KEY);
+}
+
+VALUE
+bdb_internal_value(obj, a, b, sens)
+    VALUE obj, a, b;
+    int sens;
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    DBT key, data;
+    int ret, flags;
+    db_recno_t recno;
+
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&data, DBT, 1);
+    data.flags = DB_DBT_MALLOC;
+#if BDB_VERSION < 20600
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp));
+#else
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+#endif
+    SET_PARTIAL(dbst, data);
+    flags = sens | TEST_INIT_LOCK(dbst);
+    do {
+        bdb_cache_error(dbcp->c_get(dbcp, &key, &data, flags),
+			dbcp->c_close(dbcp), ret);
+        if (ret == DB_NOTFOUND) {
+            dbcp->c_close(dbcp);
+            return (b == Qfalse)?Qfalse:Qnil;
+        }
+	if (ret == DB_KEYEMPTY) continue;
+	if (rb_equal(a, bdb_test_load(obj, &data, FILTER_VALUE)) == Qtrue) {
+	    VALUE d;
+	    
+	    dbcp->c_close(dbcp);
+	    if (b == Qfalse) {
+		d = Qtrue;
+		FREE_KEY(dbst, key);
+	    }
+	    else {
+		d = bdb_test_load_key(obj, &key);
+	    }
+	    return  d;
+	}
+	FREE_KEY(dbst, key);
+    } while (1);
+    return (b == Qfalse)?Qfalse:Qnil;
+}
+
+VALUE
+bdb_index(obj, a)
+    VALUE obj, a;
+{
+    return bdb_internal_value(obj, a, Qtrue, DB_NEXT);
+}
+
+static VALUE
+bdb_indexes(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE indexes;
+    int i;
+
+#if RUBY_VERSION_CODE >= 172
+    rb_warn("Common#%s is deprecated; use Common#values_at",
+	    rb_id2name(rb_frame_last_func()));
+#endif
+    indexes = rb_ary_new2(argc);
+    for (i = 0; i < argc; i++) {
+	RARRAY(indexes)->ptr[i] = bdb_get(1, &argv[i], obj);
+    }
+    RARRAY(indexes)->len = i;
+    return indexes;
+}
+
+static VALUE
+bdb_values_at(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE result = rb_ary_new2(argc);
+    long i;
+
+    for (i = 0; i < argc; i++) {
+	rb_ary_push(result, bdb_get(1, &argv[i], obj));
+    }
+    return result;
+}
+
+static VALUE
+bdb_select(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE result = rb_ary_new();
+
+    if (rb_block_given_p()) {
+	if (argc > 0) {
+	    rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
+	}
+	return bdb_each_kvc(argc, argv, obj, DB_NEXT, result, BDB_ST_SELECT);
+    }
+    rb_warn("Common#select(index..) is deprecated; use Common#values_at");
+    return bdb_values_at(argc, argv, obj);
+}
+
+VALUE
+bdb_has_value(obj, a)
+    VALUE obj, a;
+{
+    return bdb_internal_value(obj, a, Qfalse, DB_NEXT);
+}
+
+static VALUE
+bdb_sync(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+
+    if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
+	rb_raise(rb_eSecurityError, "Insecure: can't sync the database");
+    GetDB(obj, dbst);
+    bdb_test_error(dbst->dbp->sync(dbst->dbp, 0));
+    return Qtrue;
+}
+
+#if BDB_VERSION >= 30000
+static VALUE
+bdb_hash_stat(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    DB_HASH_STAT *bdb_stat;
+    VALUE hash, flagv;
+    int flags = 0;
+#if BDB_VERSION >= 4030
+    DB_TXN *txnid = NULL;
+#endif
+
+    if (rb_scan_args(argc, argv, "01", &flagv) == 1) {
+	flags = NUM2INT(flagv);
+    }
+    GetDB(obj, dbst);
+#if BDB_VERSION < 30300
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, 0, flags));
+#else
+#if BDB_VERSION >= 40300
+    if (RTEST(dbst->txn)) {
+        bdb_TXN *txnst;
+
+        GetTxnDB(dbst->txn, txnst);
+        txnid = txnst->txnid;
+    }
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, txnid, &bdb_stat, flags));
+#else
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, flags));
+#endif
+#endif
+    hash = rb_hash_new();
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_magic"), INT2NUM(bdb_stat->hash_magic));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_version"), INT2NUM(bdb_stat->hash_version));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_pagesize"), INT2NUM(bdb_stat->hash_pagesize));
+#if BDB_VERSION >= 30114
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_nkeys"), INT2NUM(bdb_stat->hash_nkeys));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_nrecs"), INT2NUM(bdb_stat->hash_nkeys));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_ndata"), INT2NUM(bdb_stat->hash_ndata));
+#else
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_nrecs"), INT2NUM(bdb_stat->hash_nrecs));
+#endif
+#if BDB_VERSION < 40100
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_nelem"), INT2NUM(bdb_stat->hash_nelem));
+#endif
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_ffactor"), INT2NUM(bdb_stat->hash_ffactor));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_buckets"), INT2NUM(bdb_stat->hash_buckets));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_free"), INT2NUM(bdb_stat->hash_free));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_bfree"), INT2NUM(bdb_stat->hash_bfree));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_bigpages"), INT2NUM(bdb_stat->hash_bigpages));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_big_bfree"), INT2NUM(bdb_stat->hash_big_bfree));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_overflows"), INT2NUM(bdb_stat->hash_overflows));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_ovfl_free"), INT2NUM(bdb_stat->hash_ovfl_free));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_dup"), INT2NUM(bdb_stat->hash_dup));
+    rb_hash_aset(hash, rb_tainted_str_new2("hash_dup_free"), INT2NUM(bdb_stat->hash_dup_free));
+    free(bdb_stat);
+    return hash;
+}
+#endif
+
+VALUE
+bdb_tree_stat(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    DB_BTREE_STAT *bdb_stat;
+    VALUE hash, flagv;
+    char pad;
+    int flags = 0;
+#if BDB_VERSION >= 40300
+    DB_TXN *txnid = NULL;
+#endif
+
+    if (rb_scan_args(argc, argv, "01", &flagv) == 1) {
+	flags = NUM2INT(flagv);
+    }
+    GetDB(obj, dbst);
+#if BDB_VERSION >= 30300
+#if BDB_VERSION >= 40300
+    if (RTEST(dbst->txn)) {
+        bdb_TXN *txnst;
+
+        GetTxnDB(dbst->txn, txnst);
+        txnid = txnst->txnid;
+    }
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, txnid, &bdb_stat, flags));
+#else
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, flags));
+#endif
+#else
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, 0, flags));
+#endif
+    hash = rb_hash_new();
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_magic"), INT2NUM(bdb_stat->bt_magic));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_version"), INT2NUM(bdb_stat->bt_version));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_dup_pg"), INT2NUM(bdb_stat->bt_dup_pg));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_dup_pgfree"), INT2NUM(bdb_stat->bt_dup_pgfree));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_free"), INT2NUM(bdb_stat->bt_free));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_int_pg"), INT2NUM(bdb_stat->bt_int_pg));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_int_pgfree"), INT2NUM(bdb_stat->bt_int_pgfree));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_leaf_pg"), INT2NUM(bdb_stat->bt_leaf_pg));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_leaf_pgfree"), INT2NUM(bdb_stat->bt_leaf_pgfree));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_levels"), INT2NUM(bdb_stat->bt_levels));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_minkey"), INT2NUM(bdb_stat->bt_minkey));
+#if BDB_VERSION >= 30100
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_nrecs"), INT2NUM(bdb_stat->bt_nkeys));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_nkeys"), INT2NUM(bdb_stat->bt_nkeys));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_ndata"), INT2NUM(bdb_stat->bt_ndata));
+#else
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_nrecs"), INT2NUM(bdb_stat->bt_nrecs));
+#endif
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_over_pg"), INT2NUM(bdb_stat->bt_over_pg));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_over_pgfree"), INT2NUM(bdb_stat->bt_over_pgfree));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_pagesize"), INT2NUM(bdb_stat->bt_pagesize));
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_re_len"), INT2NUM(bdb_stat->bt_re_len));
+    pad = (char)bdb_stat->bt_re_pad;
+    rb_hash_aset(hash, rb_tainted_str_new2("bt_re_pad"), rb_tainted_str_new(&pad, 1));
+    free(bdb_stat);
+    return hash;
+}
+
+#if BDB_VERSION >= 30000
+static VALUE
+bdb_queue_stat(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    DB_QUEUE_STAT *bdb_stat;
+    VALUE hash, flagv;
+    char pad;
+    int flags = 0;
+#if BDB_VERSION >= 40300
+    DB_TXN *txnid = NULL;
+#endif
+
+    if (rb_scan_args(argc, argv, "01", &flagv) == 1) {
+	flags = NUM2INT(flagv);
+    }
+    GetDB(obj, dbst);
+#if BDB_VERSION < 30300
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, 0, flags));
+#else
+#if BDB_VERSION >= 40300
+    if (RTEST(dbst->txn)) {
+        bdb_TXN *txnst;
+
+        GetTxnDB(dbst->txn, txnst);
+        txnid = txnst->txnid;
+    }
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, txnid, &bdb_stat, flags));
+#else
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, flags));
+#endif
+#endif
+    hash = rb_hash_new();
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_magic"), INT2NUM(bdb_stat->qs_magic));
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_version"), INT2NUM(bdb_stat->qs_version));
+#if BDB_VERSION >= 30114
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_nrecs"), INT2NUM(bdb_stat->qs_nkeys));
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_nkeys"), INT2NUM(bdb_stat->qs_nkeys));
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_ndata"), INT2NUM(bdb_stat->qs_ndata));
+#else
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_nrecs"), INT2NUM(bdb_stat->qs_nrecs));
+#endif
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_pages"), INT2NUM(bdb_stat->qs_pages));
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_pagesize"), INT2NUM(bdb_stat->qs_pagesize));
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_pgfree"), INT2NUM(bdb_stat->qs_pgfree));
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_re_len"), INT2NUM(bdb_stat->qs_re_len));
+    pad = (char)bdb_stat->qs_re_pad;
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_re_pad"), rb_tainted_str_new(&pad, 1));
+#if BDB_VERSION < 30200
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_start"), INT2NUM(bdb_stat->qs_start));
+#endif
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_first_recno"), INT2NUM(bdb_stat->qs_first_recno));
+    rb_hash_aset(hash, rb_tainted_str_new2("qs_cur_recno"), INT2NUM(bdb_stat->qs_cur_recno));
+    free(bdb_stat);
+    return hash;
+}
+
+static VALUE
+bdb_queue_padlen(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+    DB_QUEUE_STAT *bdb_stat;
+    VALUE hash;
+    char pad;
+#if BDB_VERSION >= 40300
+    DB_TXN *txnid = NULL;
+#endif
+
+    GetDB(obj, dbst);
+#if BDB_VERSION < 30300
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, 0, 0));
+#else
+#if BDB_VERSION >= 40300
+    if (RTEST(dbst->txn)) {
+        bdb_TXN *txnst;
+
+        GetTxnDB(dbst->txn, txnst);
+        txnid = txnst->txnid;
+    }
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, txnid, &bdb_stat, 0));
+#else
+    bdb_test_error(dbst->dbp->stat(dbst->dbp, &bdb_stat, 0));
+#endif
+#endif
+    pad = (char)bdb_stat->qs_re_pad;
+    hash = rb_assoc_new(rb_tainted_str_new(&pad, 1), INT2NUM(bdb_stat->qs_re_len));
+    free(bdb_stat);
+    return hash;
+}
+#endif
+
+static VALUE
+bdb_set_partial(obj, a, b)
+    VALUE obj, a, b;
+{
+    bdb_DB *dbst;
+    VALUE ret;
+
+    GetDB(obj, dbst);
+    if (dbst->marshal) {
+	rb_raise(bdb_eFatal, "set_partial is not implemented with Marshal");
+    }
+    ret = rb_ary_new2(3);
+    rb_ary_push(ret, (dbst->partial == DB_DBT_PARTIAL)?Qtrue:Qfalse);
+    rb_ary_push(ret, INT2NUM(dbst->doff));
+    rb_ary_push(ret, INT2NUM(dbst->dlen));
+    dbst->doff = NUM2UINT(a);
+    dbst->dlen = NUM2UINT(b);
+    dbst->partial = DB_DBT_PARTIAL;
+    return ret;
+}
+
+static VALUE
+bdb_clear_partial(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+    VALUE ret;
+
+    GetDB(obj, dbst);
+    if (dbst->marshal) {
+	rb_raise(bdb_eFatal, "set_partial is not implemented with Marshal");
+    }
+    ret = rb_ary_new2(3);
+    rb_ary_push(ret, (dbst->partial == DB_DBT_PARTIAL)?Qtrue:Qfalse);
+    rb_ary_push(ret, INT2NUM(dbst->doff));
+    rb_ary_push(ret, INT2NUM(dbst->dlen));
+    dbst->doff = dbst->dlen = dbst->partial = 0;
+    return ret;
+}
+
+#if BDB_VERSION >= 30000
+static VALUE
+bdb_i_create(obj)
+    VALUE obj;
+{
+    DB *dbp;
+    bdb_ENV *envst = 0;
+    DB_ENV *envp;
+    bdb_DB *dbst;
+    VALUE ret, env;
+
+    envp = NULL;
+    env = 0;
+    if (rb_obj_is_kind_of(obj, bdb_cEnv)) {
+        GetEnvDB(obj, envst);
+        envp = envst->envp;
+        env = obj;
+    }
+    bdb_test_error(db_create(&dbp, envp, 0));
+    dbp->set_errpfx(dbp, "BDB::");
+    dbp->set_errcall(dbp, bdb_env_errcall);
+    ret = Data_Make_Struct(bdb_cCommon, bdb_DB, bdb_mark, bdb_free, dbst);
+    rb_obj_call_init(ret, 0, 0);
+    dbst->env = env;
+    dbst->dbp = dbp;
+    if (envp) {
+	dbst->options |= envst->options & BDB_INIT_LOCK;
+    }
+    return ret;
+}
+#endif
+
+static VALUE
+bdb_s_upgrade(int argc, VALUE *argv, VALUE obj)
+{
+#if BDB_VERSION >= 30000
+    bdb_DB *dbst;
+    VALUE a, b;
+    int flags;
+    VALUE val;
+
+    rb_secure(4);
+    flags = 0;
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+	flags = NUM2INT(b);
+    }
+    SafeStringValue(a);
+    val = bdb_i_create(obj);
+    GetDB(val, dbst);
+    bdb_test_error(dbst->dbp->upgrade(dbst->dbp, StringValuePtr(a), flags));
+    return val;
+#else
+    rb_raise(bdb_eFatal, "You can't upgrade a database with this version of DB");
+#endif
+}
+
+static VALUE
+bdb_s_remove(int argc, VALUE *argv, VALUE obj)
+{
+#if BDB_VERSION >= 30000
+    bdb_DB *dbst;
+    VALUE a, b, c;
+    char *name, *subname;
+
+    rb_secure(2);
+    c = bdb_i_create(obj);
+    GetDB(c, dbst);
+    name = subname = NULL;
+    a = b = Qnil;
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+        if (!NIL_P(b)) {
+	    SafeStringValue(b);
+            subname = StringValuePtr(b);
+        }
+    }
+    SafeStringValue(a);
+    name = StringValuePtr(a);
+    bdb_test_error(dbst->dbp->remove(dbst->dbp, name, subname, 0));
+#else
+    rb_raise(bdb_eFatal, "You can't remove a database with this version of DB");
+#endif
+    return Qtrue;
+}
+
+static VALUE
+bdb_s_rename(int argc, VALUE *argv, VALUE obj)
+{
+#if BDB_VERSION >= 30114
+    bdb_DB *dbst;
+    VALUE a, b, c;
+    char *name, *subname, *newname;
+
+    rb_secure(2);
+    c = bdb_i_create(obj);
+    GetDB(c, dbst);
+    name = subname = NULL;
+    a = b = c = Qnil;
+    rb_scan_args(argc, argv, "30", &a, &b, &c);
+    if (!NIL_P(b)) {
+	SafeStringValue(b);
+	subname = StringValuePtr(b);
+    }
+    SafeStringValue(a);
+    SafeStringValue(c);
+    name = StringValuePtr(a);
+    newname = StringValuePtr(c);
+    bdb_test_error(dbst->dbp->rename(dbst->dbp, name, subname, newname, 0));
+#else
+    rb_raise(bdb_eFatal, "You can't rename a database with this version of DB");
+#endif
+    return Qtrue;
+}
+
+#if BDB_VERSION >= 20600
+
+static VALUE
+bdb_i_joinclose(st)
+    eachst *st;
+{
+    bdb_DB *dbst;
+
+    GetDB(st->db, dbst);
+    if (st->dbcp && dbst && dbst->dbp) {
+	st->dbcp->c_close(st->dbcp);
+    }
+    return Qnil;
+}
+
+ 
+static VALUE
+bdb_i_join(st)
+    eachst *st;
+{
+    int ret;
+    DBT key, data;
+    db_recno_t recno;
+    bdb_DB *dbst;
+
+    GetDB(st->db, dbst);
+    MEMZERO(&key, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&data, DBT, 1);
+    data.flags |= DB_DBT_MALLOC;
+    SET_PARTIAL(dbst, data);
+    do {
+	ret = bdb_test_error(st->dbcp->c_get(st->dbcp, &key, &data, st->sens));
+	if (ret  == DB_NOTFOUND || ret == DB_KEYEMPTY)
+	    return Qnil;
+	rb_yield(bdb_assoc(st->db, &key, &data));
+    } while (1);
+    return Qnil;
+}
+ 
+static VALUE
+bdb_join(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    bdb_DBC *dbcst;
+    DBC *dbc, **dbcarr;
+    int flags, i;
+    eachst st;
+    VALUE a, b, c;
+
+    c = 0;
+    flags = 0;
+    GetDB(obj, dbst);
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+	flags = NUM2INT(b);
+    }
+    if (TYPE(a) != T_ARRAY) {
+        rb_raise(bdb_eFatal, "first argument must an array of cursors");
+    }
+    if (RARRAY(a)->len == 0) {
+        rb_raise(bdb_eFatal, "empty array");
+    }
+    dbcarr = ALLOCA_N(DBC *, RARRAY(a)->len + 1);
+    {
+	DBC **dbs;
+	bdb_DB *tmp;
+
+	for (dbs = dbcarr, i = 0; i < RARRAY(a)->len; i++, dbs++) {
+	    if (!rb_obj_is_kind_of(RARRAY(a)->ptr[i], bdb_cCursor)) {
+		rb_raise(bdb_eFatal, "element %d is not a cursor");
+	    }
+	    GetCursorDB(RARRAY(a)->ptr[i], dbcst, tmp);
+	    *dbs = dbcst->dbc;
+	}
+	*dbs = 0;
+    }
+    dbc = 0;
+#if BDB_VERSION < 30000
+    bdb_test_error(dbst->dbp->join(dbst->dbp, dbcarr, 0, &dbc));
+#else
+    bdb_test_error(dbst->dbp->join(dbst->dbp, dbcarr, &dbc, 0));
+#endif
+    st.db = obj;
+    st.dbcp = dbc;
+    st.sens = flags | TEST_INIT_LOCK(dbst);
+    rb_ensure(bdb_i_join, (VALUE)&st, bdb_i_joinclose, (VALUE)&st);
+    return obj;
+}
+
+static VALUE
+bdb_byteswapp(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+    int byteswap = 0;
+#if BDB_VERSION < 20500
+    rb_raise(bdb_eFatal, "byteswapped needs Berkeley DB 2.5 or later");
+#endif
+    GetDB(obj, dbst);
+#if BDB_VERSION < 30000
+    return dbst->dbp->byteswapped?Qtrue:Qfalse;
+#else
+#if BDB_VERSION < 30300
+    return dbst->dbp->get_byteswapped(dbst->dbp)?Qtrue:Qfalse;
+#else
+    dbst->dbp->get_byteswapped(dbst->dbp, &byteswap);
+    return byteswap?Qtrue:Qfalse;
+#endif
+#endif
+}
+
+#endif
+
+#if BDB_VERSION >= 30300
+
+static VALUE
+bdb_internal_second_call(tmp)
+    VALUE *tmp;
+{
+    return rb_funcall2(tmp[0], bdb_id_call, 3, tmp + 1);
+}
+
+static int
+bdb_call_secondary(secst, pkey, pdata, skey)
+    DB *secst;
+    DBT *pkey;
+    DBT *pdata;
+    DBT *skey;
+{
+    VALUE obj, ary, second;
+    bdb_DB *dbst, *secondst;
+    VALUE result = Qnil;
+    int i, inter;
+
+    GetIdDb(obj, dbst);
+    if (!dbst->dbp || !RTEST(dbst->secondary)) return DB_DONOTINDEX;
+    for (i = 0; i < RARRAY(dbst->secondary)->len; i++) {
+	ary = RARRAY(dbst->secondary)->ptr[i];
+	if (RARRAY(ary)->len != 2) continue;
+	second = RARRAY(ary)->ptr[0];
+	Data_Get_Struct(second, bdb_DB, secondst);
+	if (!secondst->dbp) continue;
+	if (secondst->dbp == secst) {
+	    VALUE tmp[4];
+
+	    tmp[0] = RARRAY(ary)->ptr[1];
+	    tmp[1] = second;
+	    tmp[2] = bdb_test_load_key(obj, pkey);
+	    tmp[3] = bdb_test_load(obj, pdata, FILTER_VALUE|FILTER_FREE);
+	    inter = 0;
+	    result = rb_protect(bdb_internal_second_call, (VALUE)tmp, &inter);
+	    if (inter) return BDB_ERROR_PRIVATE;
+	    if (result == Qfalse) return DB_DONOTINDEX;
+	    MEMZERO(skey, DBT, 1);
+	    if (result == Qtrue) {
+		skey->data = pkey->data;
+		skey->size = pkey->size;
+	    }
+	    else {
+		DBT stmp;
+		MEMZERO(&stmp, DBT, 1);
+		result = bdb_test_dump(second, &stmp, result, FILTER_KEY);
+                skey->data = stmp.data;
+		skey->size = stmp.size;
+	    }
+	    return 0;
+	}
+    }
+    rb_gv_set("$!", rb_str_new2("secondary index not found ?"));
+    return BDB_ERROR_PRIVATE;
+}
+
+static VALUE
+bdb_associate(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst, *secondst;
+    VALUE second, flag;
+    int flags = 0;
+
+    if (!rb_block_given_p()) {
+	rb_raise(bdb_eFatal, "call out of an iterator");
+    }
+    if (rb_scan_args(argc, argv, "11", &second, &flag) == 2) {
+	flags = NUM2INT(flag);
+    }
+    if (!rb_obj_is_kind_of(second, bdb_cCommon)) {
+	rb_raise(bdb_eFatal, "associate expect a BDB object");
+    }
+    GetDB(second, secondst);
+    if (RTEST(secondst->secondary)) {
+	rb_raise(bdb_eFatal, "associate with a primary index");
+    }
+    GetDB(obj, dbst);
+
+    dbst->options |= BDB_NEED_CURRENT;
+    if (!dbst->secondary) {
+	dbst->secondary = rb_ary_new();
+    }
+    rb_thread_local_aset(rb_thread_current(), bdb_id_current_db, obj);
+#if RUBY_VERSION_CODE >= 180
+    rb_ary_push(dbst->secondary, rb_assoc_new(second, rb_block_proc()));
+#else
+    rb_ary_push(dbst->secondary, rb_assoc_new(second, rb_f_lambda()));
+#endif
+    secondst->secondary = Qnil;
+
+#if BDB_VERSION >= 40100
+    {
+	DB_TXN *txnid = NULL;
+	if (RTEST(dbst->txn)) {
+	    bdb_TXN *txnst;
+
+	    GetTxnDB(dbst->txn, txnst);
+	    txnid = txnst->txnid;
+	}
+	else if (dbst->options & BDB_AUTO_COMMIT) {
+	  flags |= DB_AUTO_COMMIT;
+	}
+	bdb_test_error(dbst->dbp->associate(dbst->dbp, txnid, secondst->dbp, 
+					    bdb_call_secondary, flags));
+    }
+#else
+    bdb_test_error(dbst->dbp->associate(dbst->dbp, secondst->dbp, 
+					bdb_call_secondary, flags));
+#endif
+    return obj;
+}
+
+#endif
+
+static VALUE
+bdb_filename(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+    GetDB(obj, dbst);
+    return dbst->filename;
+}
+
+static VALUE
+bdb_database(obj)
+    VALUE obj;
+{
+    bdb_DB *dbst;
+    GetDB(obj, dbst);
+    return dbst->database;
+}
+
+#if BDB_VERSION >= 30300
+static VALUE
+bdb_verify(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    char *file, *database;
+    VALUE flagv = Qnil, iov = Qnil;
+    int flags = 0;
+    OpenFile *fptr;
+    FILE *io = NULL;
+
+    rb_secure(4);
+    file = database = NULL;
+    switch(rb_scan_args(argc, argv, "02", &iov, &flagv)) {
+    case 2:
+	flags = NUM2INT(flagv);
+    case 1:
+	if (!NIL_P(iov)) {
+	    iov = rb_convert_type(iov, T_FILE, "IO", "to_io");
+	    GetOpenFile(iov, fptr);
+	    rb_io_check_writable(fptr);
+	    io = GetWriteFile(fptr);
+	}
+	break;
+    case 0:
+	break;
+    }
+    GetDB(obj, dbst);
+    if (!NIL_P(dbst->filename)) {
+	file = StringValuePtr(dbst->filename);
+    }
+    if (!NIL_P(dbst->database)) {
+	database = StringValuePtr(dbst->database);
+    }
+    bdb_test_error(dbst->dbp->verify(dbst->dbp, file, database, io, flags));
+    return Qnil;
+}
+#endif
+
+static VALUE
+bdb__txn__dup(VALUE obj, VALUE a)
+{
+    bdb_DB *dbp, *dbh;
+    bdb_TXN *txnst;
+    VALUE res;
+ 
+    GetDB(obj, dbp);
+    GetTxnDB(a, txnst);
+    res = Data_Make_Struct(CLASS_OF(obj), bdb_DB, bdb_mark, bdb_free, dbh);
+    MEMCPY(dbh, dbp, bdb_DB, 1);
+    dbh->txn = a;
+    dbh->orig = obj;
+    dbh->ori_val = res;
+    dbh->options |= (txnst->options & BDB_INIT_LOCK) | BDB_NOT_OPEN;
+    return res;
+}
+
+static VALUE
+bdb__txn__close(VALUE obj, VALUE commit, VALUE real)
+{
+    bdb_DB *dbst, *dbst1;
+
+    if (!real) {
+	Data_Get_Struct(obj, bdb_DB, dbst);
+	dbst->dbp = NULL;
+    }
+    else {
+	if (commit) {
+	    Data_Get_Struct(obj, bdb_DB, dbst);
+	    if (dbst->orig) {
+		Data_Get_Struct(dbst->orig, bdb_DB, dbst1);
+		dbst1->len = dbst->len;
+	    }
+	}
+	bdb_close(0, 0, obj);
+    }
+    return Qnil;
+}
+
+#ifdef DB_PRIORITY_DEFAULT
+
+static VALUE
+bdb_cache_priority_set(VALUE obj, VALUE a)
+{
+    int priority;
+    bdb_DB *dbst;
+    GetDB(obj, dbst);
+    priority = dbst->priority;
+    bdb_test_error(dbst->dbp->set_cache_priority(dbst->dbp, NUM2INT(a)));
+    dbst->priority = NUM2INT(a);
+    return INT2FIX(priority);
+}
+
+static VALUE
+bdb_cache_priority_get(VALUE obj)
+{
+    bdb_DB *dbst;
+    GetDB(obj, dbst);
+    return INT2FIX(dbst->priority);
+}
+#endif
+
+#if BDB_VERSION >= 30000
+
+static VALUE
+bdb_feedback_set(VALUE obj, VALUE a)
+{
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (NIL_P(a)) {
+	dbst->feedback = a;
+    }
+    else {
+	if (!rb_respond_to(a, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	dbst->feedback = a;
+	if (!(dbst->options & BDB_FEEDBACK)) {
+	    dbst->options |= BDB_FEEDBACK;
+	    rb_thread_local_aset(rb_thread_current(), bdb_id_current_db, obj); 
+	}
+    }
+    return a;
+}
+
+#endif
+
+#if BDB_VERSION >= 40250
+
+static VALUE
+bdb_i_conf(obj, a)
+    VALUE obj, a;
+{
+    bdb_DB *dbst;
+    u_int32_t bytes, gbytes, value;
+    int intval, ncache;
+    const char *str, *filename, *dbname, *strval;
+    VALUE res;
+
+    GetDB(obj, dbst);
+    str = StringValuePtr(a);
+    if (strcmp(str, "bt_minkey") == 0) {
+	bdb_test_error(dbst->dbp->get_bt_minkey(dbst->dbp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "cachesize") == 0) {
+	bdb_test_error(dbst->dbp->get_cachesize(dbst->dbp, &gbytes, &bytes, &ncache));
+	res = rb_ary_new2(3);
+	rb_ary_push(res, INT2NUM(gbytes));
+	rb_ary_push(res, INT2NUM(bytes));
+	rb_ary_push(res, INT2NUM(ncache));
+	return res;
+    }
+    if (strcmp(str, "dbname") == 0) {
+	bdb_test_error(dbst->dbp->get_dbname(dbst->dbp, &filename, &dbname));
+	res = rb_ary_new2(3);
+	if (filename && strlen(filename)) {
+	    rb_ary_push(res, rb_tainted_str_new2(filename));
+	}
+	else {
+	    rb_ary_push(res, Qnil);
+	}
+	if (dbname && strlen(dbname)) {
+	    rb_ary_push(res, rb_tainted_str_new2(dbname));
+	}
+	else {
+	    rb_ary_push(res, Qnil);
+	}
+	return res;
+    }
+    if (strcmp(str, "env") == 0) {
+	return bdb_env(obj);
+    }
+    if (strcmp(str, "h_ffactor") == 0) {
+	bdb_test_error(dbst->dbp->get_h_ffactor(dbst->dbp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "h_nelem") == 0) {
+	bdb_test_error(dbst->dbp->get_h_nelem(dbst->dbp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "lorder") == 0) {
+	bdb_test_error(dbst->dbp->get_lorder(dbst->dbp, &intval));
+	return INT2NUM(intval);
+    }
+    if (strcmp(str, "pagesize") == 0) {
+	bdb_test_error(dbst->dbp->get_pagesize(dbst->dbp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "q_extentsize") == 0) {
+	bdb_test_error(dbst->dbp->get_q_extentsize(dbst->dbp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "re_delim") == 0) {
+	bdb_test_error(dbst->dbp->get_re_delim(dbst->dbp, &intval));
+	return INT2NUM(intval);
+    }
+    if (strcmp(str, "re_len") == 0) {
+	bdb_test_error(dbst->dbp->get_re_len(dbst->dbp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "re_pad") == 0) {
+	bdb_test_error(dbst->dbp->get_re_pad(dbst->dbp, &intval));
+	return INT2NUM(intval);
+    }
+    if (strcmp(str, "re_source") == 0) {
+	bdb_test_error(dbst->dbp->get_re_source(dbst->dbp, &strval));
+	if (strval && strlen(strval)) {
+	    return rb_tainted_str_new2(strval);
+	}
+	return Qnil;
+    }
+    if (strcmp(str, "flags") == 0) {
+	bdb_test_error(dbst->dbp->get_flags(dbst->dbp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "open_flags") == 0) {
+	bdb_test_error(dbst->dbp->get_open_flags(dbst->dbp, &value));
+	return INT2NUM(value);
+    }
+    rb_raise(rb_eArgError, "Unknown option %s", str);
+    return obj;
+}
+
+static char *
+options[] = {
+    "bt_minkey", "cachesize", "dbname", "env", "h_ffactor", "h_nelem",
+    "lorder", "pagesize", "q_extentsize", "re_delim", "re_len", "re_pad",
+    "re_source", "flags", "open_flags", 0
+};
+
+struct optst {
+    VALUE obj, str;
+};
+
+static VALUE
+bdb_intern_conf(optp)
+    struct optst *optp;
+{
+    return bdb_i_conf(optp->obj, optp->str);
+}
+
+static VALUE
+bdb_conf(int argc, VALUE *argv, VALUE obj)
+{
+    int i, state;
+    VALUE res, val;
+    struct optst opt;
+
+    if (argc > 1) {
+	rb_raise(rb_eArgError, "invalid number of arguments (%d for 1)", argc);
+    }
+    if (argc == 1) {
+	return bdb_i_conf(obj, argv[0]);
+    }
+    res = rb_hash_new();
+    opt.obj = obj;
+    for (i = 0; options[i] != NULL; i++) {
+	opt.str = rb_str_new2(options[i]);
+	val = rb_protect(bdb_intern_conf, (VALUE)&opt, &state);
+	if (state == 0) {
+	    rb_hash_aset(res, opt.str, val);
+	}
+    }
+    return res;
+}
+	
+#endif
+
+void bdb_init_common()
+{
+    id_bt_compare = rb_intern("bdb_bt_compare");
+    id_bt_prefix = rb_intern("bdb_bt_prefix");
+    id_dup_compare = rb_intern("bdb_dup_compare");
+    id_h_hash = rb_intern("bdb_h_hash");
+#if BDB_VERSION >= 40100
+    id_append_recno = rb_intern("bdb_append_recno");
+#endif
+#if BDB_VERSION >= 30000
+    id_feedback = rb_intern("bdb_feedback");
+#endif
+    bdb_cCommon = rb_define_class_under(bdb_mDb, "Common", rb_cObject);
+    rb_define_private_method(bdb_cCommon, "initialize", bdb_init, -1);
+    rb_include_module(bdb_cCommon, rb_mEnumerable);
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+    rb_define_alloc_func(bdb_cCommon, bdb_s_alloc);
+#else
+    rb_define_singleton_method(bdb_cCommon, "allocate", bdb_s_alloc, 0);
+#endif
+    rb_define_singleton_method(bdb_cCommon, "new", bdb_s_new, -1);
+    rb_define_singleton_method(bdb_cCommon, "create", bdb_s_new, -1);
+    rb_define_singleton_method(bdb_cCommon, "open", bdb_s_open, -1);
+    rb_define_singleton_method(bdb_cCommon, "[]", bdb_s_create, -1);
+    rb_define_singleton_method(bdb_cCommon, "remove", bdb_s_remove, -1);
+    rb_define_singleton_method(bdb_cCommon, "bdb_remove", bdb_s_remove, -1);
+    rb_define_singleton_method(bdb_cCommon, "unlink", bdb_s_remove, -1);
+    rb_define_singleton_method(bdb_cCommon, "upgrade", bdb_s_upgrade, -1);
+    rb_define_singleton_method(bdb_cCommon, "bdb_upgrade", bdb_s_upgrade, -1);
+    rb_define_singleton_method(bdb_cCommon, "rename", bdb_s_rename, -1);
+    rb_define_singleton_method(bdb_cCommon, "bdb_rename", bdb_s_rename, -1);
+    rb_define_private_method(bdb_cCommon, "__txn_close__", bdb__txn__close, 2);
+    rb_define_private_method(bdb_cCommon, "__txn_dup__", bdb__txn__dup, 1);
+    rb_define_method(bdb_cCommon, "filename", bdb_filename, 0);
+    rb_define_method(bdb_cCommon, "subname", bdb_database, 0);
+    rb_define_method(bdb_cCommon, "database", bdb_database, 0);
+#if BDB_VERSION >= 30300
+    rb_define_method(bdb_cCommon, "verify", bdb_verify, -1);
+#endif
+    rb_define_method(bdb_cCommon, "close", bdb_close, -1);
+    rb_define_method(bdb_cCommon, "db_close", bdb_close, -1);
+    rb_define_method(bdb_cCommon, "put", bdb_put, -1);
+    rb_define_method(bdb_cCommon, "db_put", bdb_put, -1);
+    rb_define_method(bdb_cCommon, "[]=", bdb_aset, 2);
+    rb_define_method(bdb_cCommon, "store", bdb_put, -1);
+    rb_define_method(bdb_cCommon, "env", bdb_env, 0);
+    rb_define_method(bdb_cCommon, "environment", bdb_env, 0);
+    rb_define_method(bdb_cCommon, "has_env?", bdb_env_p, 0);
+    rb_define_method(bdb_cCommon, "has_environment?", bdb_env_p, 0);
+    rb_define_method(bdb_cCommon, "env?", bdb_env_p, 0);
+    rb_define_method(bdb_cCommon, "environment?", bdb_env_p, 0);
+    rb_define_method(bdb_cCommon, "txn", bdb_txn, 0);
+    rb_define_method(bdb_cCommon, "transaction", bdb_txn, 0);
+    rb_define_method(bdb_cCommon, "txn?", bdb_txn_p, 0);
+    rb_define_method(bdb_cCommon, "transaction?", bdb_txn_p, 0);
+    rb_define_method(bdb_cCommon, "in_txn?", bdb_txn_p, 0);
+    rb_define_method(bdb_cCommon, "in_transaction?", bdb_txn_p, 0);
+#if BDB_VERSION >= 20600
+    rb_define_method(bdb_cCommon, "count", bdb_count, 1);
+    rb_define_method(bdb_cCommon, "dup_count", bdb_count, 1);
+    rb_define_method(bdb_cCommon, "each_dup", bdb_common_each_dup, -1);
+    rb_define_method(bdb_cCommon, "each_dup_value", bdb_common_each_dup_val, -1);
+    rb_define_method(bdb_cCommon, "dups", bdb_common_dups, -1);
+    rb_define_method(bdb_cCommon, "duplicates", bdb_common_dups, -1);
+    rb_define_method(bdb_cCommon, "get_dup", bdb_get_dup, -1);
+#endif
+    rb_define_method(bdb_cCommon, "get", bdb_get_dyna, -1);
+    rb_define_method(bdb_cCommon, "db_get", bdb_get_dyna, -1);
+    rb_define_method(bdb_cCommon, "[]", bdb_get_dyna, -1);
+#if BDB_VERSION >= 30300
+    rb_define_method(bdb_cCommon, "pget", bdb_pget, -1);
+    rb_define_method(bdb_cCommon, "primary_get", bdb_pget, -1);
+    rb_define_method(bdb_cCommon, "db_pget", bdb_pget, -1);
+#endif
+    rb_define_method(bdb_cCommon, "fetch", bdb_fetch, -1);
+    rb_define_method(bdb_cCommon, "delete", bdb_del, 1);
+    rb_define_method(bdb_cCommon, "del", bdb_del, 1);
+    rb_define_method(bdb_cCommon, "db_del", bdb_del, 1);
+    rb_define_method(bdb_cCommon, "sync", bdb_sync, 0);
+    rb_define_method(bdb_cCommon, "db_sync", bdb_sync, 0);
+    rb_define_method(bdb_cCommon, "flush", bdb_sync, 0);
+    rb_define_method(bdb_cCommon, "each", bdb_each_pair, -1);
+    rb_define_method(bdb_cCommon, "each_primary", bdb_each_pair_prim, -1);
+    rb_define_method(bdb_cCommon, "each_value", bdb_each_value, -1);
+    rb_define_method(bdb_cCommon, "reverse_each_value", bdb_each_eulav, -1);
+    rb_define_method(bdb_cCommon, "each_key", bdb_each_key, -1);
+    rb_define_method(bdb_cCommon, "reverse_each_key", bdb_each_yek, -1);
+    rb_define_method(bdb_cCommon, "each_pair", bdb_each_pair, -1);
+    rb_define_method(bdb_cCommon, "reverse_each", bdb_each_riap, -1);
+    rb_define_method(bdb_cCommon, "reverse_each_pair", bdb_each_riap, -1);
+    rb_define_method(bdb_cCommon, "reverse_each_primary", bdb_each_riap_prim, -1);
+    rb_define_method(bdb_cCommon, "keys", bdb_keys, 0);
+    rb_define_method(bdb_cCommon, "values", bdb_values, 0);
+    rb_define_method(bdb_cCommon, "delete_if", bdb_delete_if, -1);
+    rb_define_method(bdb_cCommon, "reject!", bdb_delete_if, -1);
+    rb_define_method(bdb_cCommon, "reject", bdb_reject, -1);
+    rb_define_method(bdb_cCommon, "clear", bdb_clear, -1);
+    rb_define_method(bdb_cCommon, "truncate", bdb_clear, -1);
+    rb_define_method(bdb_cCommon, "replace", bdb_replace, 1);
+    rb_define_method(bdb_cCommon, "update", bdb_update, 1);
+    rb_define_method(bdb_cCommon, "include?", bdb_has_key, 1);
+    rb_define_method(bdb_cCommon, "has_key?", bdb_has_key, 1);
+    rb_define_method(bdb_cCommon, "key?", bdb_has_key, 1);
+    rb_define_method(bdb_cCommon, "member?", bdb_has_key, 1);
+    rb_define_method(bdb_cCommon, "has_value?", bdb_has_value, 1);
+    rb_define_method(bdb_cCommon, "value?", bdb_has_value, 1);
+    rb_define_method(bdb_cCommon, "has_both?", bdb_has_both, 2);
+    rb_define_method(bdb_cCommon, "both?", bdb_has_both, 2);
+    rb_define_method(bdb_cCommon, "to_a", bdb_to_a, 0);
+    rb_define_method(bdb_cCommon, "to_hash", bdb_to_hash, 0);
+    rb_define_method(bdb_cCommon, "invert", bdb_invert, 0);
+    rb_define_method(bdb_cCommon, "empty?", bdb_empty, 0);
+    rb_define_method(bdb_cCommon, "length", bdb_length, 0);
+    rb_define_alias(bdb_cCommon,  "size", "length");
+    rb_define_method(bdb_cCommon, "index", bdb_index, 1);
+    rb_define_method(bdb_cCommon, "indexes", bdb_indexes, -1);
+    rb_define_method(bdb_cCommon, "indices", bdb_indexes, -1);
+    rb_define_method(bdb_cCommon, "select", bdb_select, -1);
+    rb_define_method(bdb_cCommon, "values_at", bdb_values_at, -1);
+    rb_define_method(bdb_cCommon, "set_partial", bdb_set_partial, 2);
+    rb_define_method(bdb_cCommon, "clear_partial", bdb_clear_partial, 0);
+    rb_define_method(bdb_cCommon, "partial_clear", bdb_clear_partial, 0);
+#if BDB_VERSION >= 20600
+    rb_define_method(bdb_cCommon, "join", bdb_join, -1);
+    rb_define_method(bdb_cCommon, "byteswapped?", bdb_byteswapp, 0);
+    rb_define_method(bdb_cCommon, "get_byteswapped", bdb_byteswapp, 0);
+#endif
+#if BDB_VERSION >= 30300
+    rb_define_method(bdb_cCommon, "associate", bdb_associate, -1);
+#endif
+#ifdef DB_PRIORITY_DEFAULT
+    rb_define_method(bdb_cCommon, "cache_priority=", bdb_cache_priority_set, 1);
+    rb_define_method(bdb_cCommon, "cache_priority", bdb_cache_priority_get, 0);
+#endif
+#if BDB_VERSION >= 30000
+    rb_define_method(bdb_cCommon, "feedback=", bdb_feedback_set, 1);
+#endif
+    bdb_cBtree = rb_define_class_under(bdb_mDb, "Btree", bdb_cCommon);
+    rb_define_method(bdb_cBtree, "stat", bdb_tree_stat, -1);
+    rb_define_method(bdb_cBtree, "each_by_prefix", bdb_each_prefix, -1);
+    rb_define_method(bdb_cBtree, "reverse_each_by_prefix", bdb_each_xiferp, -1);
+#if BDB_VERSION >= 40416
+    rb_define_method(bdb_cBtree, "compact", bdb_treerec_compact, -1);
+#endif
+#if BDB_VERSION >= 30100
+    bdb_sKeyrange = rb_struct_define("Keyrange", "less", "equal", "greater", 0);
+    rb_global_variable(&bdb_sKeyrange);
+    rb_define_method(bdb_cBtree, "key_range", bdb_btree_key_range, 1);
+#endif
+    bdb_cHash = rb_define_class_under(bdb_mDb, "Hash", bdb_cCommon);
+#if BDB_VERSION >= 30000
+    rb_define_method(bdb_cHash, "stat", bdb_hash_stat, -1);
+#endif
+    bdb_cRecno = rb_define_class_under(bdb_mDb, "Recno", bdb_cCommon);
+    rb_define_method(bdb_cRecno, "each_index", bdb_each_key, -1);
+    rb_define_method(bdb_cRecno, "unshift", bdb_unshift, -1);
+    rb_define_method(bdb_cRecno, "<<", bdb_append, 1);
+    rb_define_method(bdb_cRecno, "push", bdb_append_m, -1);
+    rb_define_method(bdb_cRecno, "stat", bdb_tree_stat, -1);
+#if BDB_VERSION >= 40416
+    rb_define_method(bdb_cRecno, "compact", bdb_treerec_compact, -1);
+#endif
+#if BDB_VERSION >= 30000
+    bdb_cQueue = rb_define_class_under(bdb_mDb, "Queue", bdb_cCommon);
+    rb_define_singleton_method(bdb_cQueue, "new", bdb_queue_s_new, -1);
+    rb_define_singleton_method(bdb_cQueue, "create", bdb_queue_s_new, -1);
+    rb_define_method(bdb_cQueue, "each_index", bdb_each_key, -1);
+    rb_define_method(bdb_cQueue, "<<", bdb_append, 1);
+    rb_define_method(bdb_cQueue, "push", bdb_append_m, -1);
+    rb_define_method(bdb_cQueue, "shift", bdb_consume, 0);
+    rb_define_method(bdb_cQueue, "stat", bdb_queue_stat, -1);
+    rb_define_method(bdb_cQueue, "pad", bdb_queue_padlen, 0);
+#endif
+#if BDB_VERSION >= 40250
+    rb_define_method(bdb_cCommon, "configuration", bdb_conf, -1);
+    rb_define_method(bdb_cCommon, "conf", bdb_conf, -1);
+#endif
+    bdb_cUnknown = rb_define_class_under(bdb_mDb, "Unknown", bdb_cCommon);
+}

Added: packages/libdb4.3-ruby/branches/upstream/current/src/env.c
===================================================================
--- packages/libdb4.3-ruby/branches/upstream/current/src/env.c	                        (rev 0)
+++ packages/libdb4.3-ruby/branches/upstream/current/src/env.c	2007-12-08 12:00:45 UTC (rev 2142)
@@ -0,0 +1,1806 @@
+#include "bdb.h"
+
+ID bdb_id_call;
+
+#if BDB_VERSION >= 30000
+static ID id_feedback;
+#endif
+
+ID bdb_id_current_env;
+static void bdb_env_mark _((bdb_ENV *));
+
+#define GetIdEnv(obj, envst) do {					   \
+    (obj) = rb_thread_local_aref(rb_thread_current(), bdb_id_current_env); \
+    if (TYPE(obj) != T_DATA ||						   \
+	RDATA(obj)->dmark != (RUBY_DATA_FUNC)bdb_env_mark) {		   \
+	rb_raise(bdb_eFatal, "BUG : current_env not set");		   \
+    }									   \
+    GetEnvDB(obj, envst);						   \
+} while (0)
+
+#if BDB_VERSION >= 40100
+static ID id_app_dispatch;
+#endif
+
+struct db_stoptions {
+    bdb_ENV *env;
+    VALUE config;
+    int lg_max, lg_bsize;
+};
+
+#if BDB_VERSION >= 40000
+#if BDB_VERSION >= 40200
+static int
+bdb_env_rep_transport(DB_ENV *env, const DBT *control, const DBT *rec,
+		      const DB_LSN *lsn, int envid, u_int32_t flags)
+
+{
+    VALUE obj, av, bv, res;
+    bdb_ENV *envst;
+    struct dblsnst *lsnst;
+    VALUE lsnobj;
+
+    GetIdEnv(obj, envst);
+    lsnobj = bdb_makelsn(obj);
+    Data_Get_Struct(lsnobj, struct dblsnst, lsnst);
+    MEMCPY(lsnst->lsn, lsn, DB_LSN, 1);
+    av = rb_tainted_str_new(control->data, control->size);
+    bv = rb_tainted_str_new(rec->data, rec->size);
+    if (envst->rep_transport == 0) {
+	res = rb_funcall(obj, rb_intern("bdb_rep_transport"), 5, av, bv, lsnobj,
+			 INT2FIX(envid), INT2FIX(flags));
+    }
+    else {
+	res = rb_funcall(envst->rep_transport, bdb_id_call, 4, 
+			 av, bv, lsnobj, INT2FIX(envid), INT2FIX(flags));
+    }
+    return NUM2INT(res);
+}
+#else
+static int
+bdb_env_rep_transport(DB_ENV *env, const DBT *control, const DBT *rec,
+		      int envid, u_int32_t flags)
+{
+    VALUE obj, av, bv, res;
+    bdb_ENV *envst;
+
+    GetIdEnv(obj, envst);
+    av = rb_tainted_str_new(control->data, control->size);
+    bv = rb_tainted_str_new(rec->data, rec->size);
+    if (envst->rep_transport == 0) {
+	res = rb_funcall(obj, rb_intern("bdb_rep_transport"), 4, av, bv,
+			 INT2FIX(envid), INT2FIX(flags));
+    }
+    else {
+	res = rb_funcall(envst->rep_transport, bdb_id_call, 4, 
+			 av, bv, INT2FIX(envid), INT2FIX(flags));
+    }
+    return NUM2INT(res);
+}
+#endif
+
+static VALUE
+bdb_env_rep_elect(int argc, VALUE *argv, VALUE env)
+{
+    VALUE nb, pri, ti, nvo;
+    bdb_ENV *envst;
+    int envid = 0, nvotes = 0;
+
+    GetEnvDB(env, envst);
+    if (rb_scan_args(argc, argv, "31", &nb, &pri, &ti, &nvo) == 4) {
+        nvotes = NUM2INT(nvo);
+    }
+    
+#if BDB_VERSION >= 40300
+    bdb_test_error(envst->envp->rep_elect(envst->envp, NUM2INT(nb), nvotes,
+					  NUM2INT(pri), NUM2INT(ti), &envid, 0));
+#else
+    bdb_test_error(envst->envp->rep_elect(envst->envp, NUM2INT(nb),
+					  NUM2INT(pri), NUM2INT(ti), &envid));
+#endif
+    return INT2NUM(envid);
+}
+
+static VALUE
+bdb_env_rep_process_message(VALUE env, VALUE av, VALUE bv, VALUE ev)
+{
+    bdb_ENV *envst;
+    DBT control, rec;
+    int ret, envid;
+    VALUE result;
+#if BDB_VERSION >= 40200
+    VALUE lsn;
+    struct dblsnst *lsnst;
+#endif
+
+
+    GetEnvDB(env, envst);
+    av = rb_str_to_str(av);
+    bv = rb_str_to_str(bv);
+    MEMZERO(&control, DBT, 1);
+    MEMZERO(&rec, DBT, 1);
+    control.size = RSTRING(av)->len;
+    control.data = StringValuePtr(av);
+    rec.size = RSTRING(bv)->len;
+    rec.data = StringValuePtr(bv);
+    envid = NUM2INT(ev);
+#if BDB_VERSION >= 40200
+    lsn = bdb_makelsn(env);
+    Data_Get_Struct(lsn, struct dblsnst, lsnst);
+
+    ret = envst->envp->rep_process_message(envst->envp, &control, &rec, 
+					   &envid, lsnst->lsn);
+#else
+    ret = envst->envp->rep_process_message(envst->envp, &control, &rec, 
+					   &envid);
+#endif
+    if (ret == DB_RUNRECOVERY) {
+	bdb_test_error(ret);
+    }
+    result = rb_ary_new();
+    rb_ary_push(result, INT2NUM(ret));
+    rb_ary_push(result, rb_str_new(rec.data, rec.size));
+    rb_ary_push(result, INT2NUM(envid));
+#if BDB_VERSION >= 42000
+    if (ret == DB_REP_NOTPERM || ret == DB_REP_ISPERM) {
+	rb_ary_push(result, lsn);
+    }
+#endif
+    return result;
+}
+
+static VALUE
+bdb_env_rep_start(VALUE env, VALUE ident, VALUE flags)
+{
+    bdb_ENV *envst;
+    DBT cdata;
+
+    GetEnvDB(env, envst);
+    if (!NIL_P(ident)) {
+	ident = rb_str_to_str(ident);
+	MEMZERO(&cdata, DBT, 1);
+	cdata.size = RSTRING(ident)->len;
+	cdata.data = StringValuePtr(ident);
+    }
+    bdb_test_error(envst->envp->rep_start(envst->envp, 
+					  NIL_P(ident)?NULL:&cdata,
+					  NUM2INT(flags)));
+    return Qnil;
+}
+
+#if BDB_VERSION >= 40100
+
+static VALUE
+bdb_env_rep_limit(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_ENV *envst;
+    VALUE a, b;
+    u_int32_t gbytes, bytes;
+
+    GetEnvDB(obj, envst);
+    gbytes = bytes = 0;
+    switch(rb_scan_args(argc, argv, "11", &a, &b)) {
+    case 1:
+	if (TYPE(a) == T_ARRAY) {
+	    if (RARRAY(a)->len != 2) {
+		rb_raise(bdb_eFatal, "Expected an Array with 2 values");
+	    }
+	    gbytes = NUM2UINT(RARRAY(a)->ptr[0]);
+	    bytes = NUM2UINT(RARRAY(a)->ptr[1]);
+	}
+	else {
+	    bytes = NUM2UINT(RARRAY(a)->ptr[1]);
+	}
+	break;
+    case 2:
+	gbytes = NUM2UINT(a);
+	bytes = NUM2UINT(b);
+	break;
+    }
+    bdb_test_error(envst->envp->set_rep_limit(envst->envp, gbytes, bytes));
+    return obj;
+}
+#endif
+ 
+#endif
+
+#if BDB_VERSION >= 30000
+static void
+bdb_env_feedback(DB_ENV *envp, int opcode, int pct)
+{
+    VALUE obj;
+    bdb_ENV *envst;
+
+    GetIdEnv(obj, envst);
+    if (NIL_P(envst->feedback)) {
+	return;
+    }
+    if (envst->feedback == 0) {
+	rb_funcall(obj, id_feedback, 2, INT2NUM(opcode), INT2NUM(pct));
+    }
+    else {
+	rb_funcall(envst->feedback, bdb_id_call, 2, INT2NUM(opcode), INT2NUM(pct));
+    }
+}
+
+#endif
+
+#if BDB_VERSION >= 40100
+
+static int
+bdb_env_app_dispatch(DB_ENV *envp, DBT *log_rec, DB_LSN *lsn, db_recops op)
+{
+    VALUE obj, lsnobj, logobj, res;
+    bdb_ENV *envst;
+    struct dblsnst *lsnst;
+
+    GetIdEnv(obj, envst);
+    lsnobj = bdb_makelsn(obj);
+    Data_Get_Struct(lsnobj, struct dblsnst, lsnst);
+    MEMCPY(lsnst->lsn, lsn, DB_LSN, 1);
+    logobj = rb_str_new(log_rec->data, log_rec->size);
+    if (envst->app_dispatch == 0) {
+	res = rb_funcall(obj, id_app_dispatch, 3, logobj, lsnobj, INT2NUM(op));
+    }
+    else {
+	res = rb_funcall(envst->app_dispatch, bdb_id_call, 3, logobj, lsnobj, INT2NUM(op));
+    }
+    return NUM2INT(res);
+}
+
+#endif
+
+#if BDB_VERSION >= 40416
+
+static ID id_msgcall;
+
+static void
+bdb_env_msgcall(const DB_ENV *dbenv, const char *msg)
+{
+    VALUE obj;
+    bdb_ENV *envst;
+
+    GetIdEnv(obj, envst);
+    if (NIL_P(envst->msgcall)) {
+	return;
+    }
+    if (envst->msgcall == 0) {
+	rb_funcall(obj, id_msgcall, 1, rb_tainted_str_new2(msg));
+    }
+    else {
+	rb_funcall(envst->msgcall, bdb_id_call, 1, rb_tainted_str_new2(msg));
+    }
+}
+
+static ID id_thread_id;
+
+static void
+bdb_env_thread_id(DB_ENV *dbenv, pid_t *pid, db_threadid_t *tid)
+{
+    VALUE obj;
+    bdb_ENV *envst;
+    VALUE res;
+
+    GetIdEnv(obj, envst);
+    if (NIL_P(envst->thread_id)) {
+	*pid = 0;
+	*tid = 0;
+	return;
+    }
+    if (envst->thread_id == 0) {
+	res = rb_funcall2(obj, id_thread_id, 0, 0);
+    }
+    else {
+	res = rb_funcall2(envst->thread_id, bdb_id_call, 0, 0);
+    }
+    res = rb_Array(res);
+    if (TYPE(res) != T_ARRAY || RARRAY(res)->len != 2) {
+	rb_raise(bdb_eFatal, "expected [pid, threadid]");
+    }
+    *pid = NUM2INT(RARRAY(res)->ptr[0]);
+    *tid = NUM2INT(RARRAY(res)->ptr[0]);
+    return;
+}
+
+static ID id_thread_id_string;
+
+static char *
+bdb_env_thread_id_string(DB_ENV *dbenv, pid_t pid, db_threadid_t tid, char *buf)
+{
+    VALUE obj;
+    bdb_ENV *envst;
+    VALUE res, a, b;
+
+    GetIdEnv(obj, envst);
+    if (NIL_P(envst->thread_id_string)) {
+	snprintf(buf, DB_THREADID_STRLEN, "%d/%d", pid, (int)tid);
+	return buf;
+    }
+    a = INT2NUM(pid);
+    b = INT2NUM(tid);
+    if (envst->thread_id_string == 0) {
+	res = rb_funcall(obj, id_thread_id_string, 2, a, b);
+    }
+    else {
+	res = rb_funcall(envst->thread_id_string, bdb_id_call, 2, a, b);
+    }
+    snprintf(buf, DB_THREADID_STRLEN, "%s", StringValuePtr(res));
+    return buf;
+}
+
+static ID id_isalive;
+
+static int
+bdb_env_isalive(DB_ENV *dbenv, pid_t pid, db_threadid_t tid)
+{
+    VALUE obj;
+    bdb_ENV *envst;
+    VALUE res, a ,b;
+
+    GetIdEnv(obj, envst);
+    if (NIL_P(envst->isalive)) {
+	return 0;
+    }
+    a = INT2NUM(pid);
+    b = INT2NUM(tid);
+    if (envst->isalive == 0) {
+	res = rb_funcall(obj, id_isalive, 2, INT2NUM(pid), INT2NUM(tid));
+    }
+    else {
+	res = rb_funcall(envst->isalive, bdb_id_call, 2, INT2NUM(pid), INT2NUM(tid));
+    }
+    if (RTEST(res)) {
+	return 1;
+    }
+    return 0;
+}
+
+
+#endif
+
+static VALUE
+bdb_env_i_options(VALUE obj, VALUE db_stobj)
+{
+    char *options;
+    DB_ENV *envp;
+    VALUE key, value;
+    bdb_ENV *envst;
+    struct db_stoptions *db_st;
+
+    Data_Get_Struct(db_stobj, struct db_stoptions, db_st);
+    envst = db_st->env;
+
+    key = rb_ary_entry(obj, 0);
+    value = rb_ary_entry(obj, 1);
+    envp = envst->envp;
+    key = rb_obj_as_string(key);
+    options = StringValuePtr(key);
+    if (strcmp(options, "set_cachesize") == 0) {
+	switch (TYPE(value)) {
+	case T_FIXNUM:
+	case T_FLOAT:
+	case T_BIGNUM:
+#if BDB_VERSION < 30000
+	    envp->mp_size = NUM2INT(value);
+#else
+	    bdb_test_error(envp->set_cachesize(envp, 0, NUM2UINT(value), 0));
+#endif
+	    break;
+	default:
+	    Check_Type(value, T_ARRAY);
+	    if (RARRAY(value)->len < 3) {
+		rb_raise(bdb_eFatal, "expected 3 values for cachesize");
+	    }
+#if BDB_VERSION < 30000
+	    envp->mp_size = NUM2INT(RARRAY(value)->ptr[1]);
+#else
+	    bdb_test_error(envp->set_cachesize(envp, 
+					       NUM2UINT(RARRAY(value)->ptr[0]),
+					       NUM2UINT(RARRAY(value)->ptr[1]),
+					       NUM2INT(RARRAY(value)->ptr[2])));
+#endif
+	    break;
+	}
+    }
+#if DB_VERSION_MAJOR == 3
+    else if (strcmp(options, "set_region_init") == 0) {
+#if BDB_VERSION < 30114
+        bdb_test_error(envp->set_region_init(envp, NUM2INT(value)));
+#else
+        bdb_test_error(db_env_set_region_init(NUM2INT(value)));
+#endif
+    }
+#endif
+#if BDB_VERSION >= 30000
+    else if (strcmp(options, "set_tas_spins") == 0) {
+#if BDB_VERSION < 30114 || BDB_VERSION >= 40000
+#if BDB_VERSION >= 40416
+	rb_warning("Invalid option :set_tas_spins");
+#else
+        bdb_test_error(envp->set_tas_spins(envp, NUM2INT(value)));
+#endif
+#else
+        bdb_test_error(db_env_set_tas_spins(NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_tx_max") == 0) {
+        bdb_test_error(envp->set_tx_max(envp, NUM2INT(value)));
+    }
+#if BDB_VERSION >= 30100
+    else if (strcmp(options, "set_tx_timestamp") == 0) {
+	time_t ti;
+	value = rb_Integer(value);
+	ti = (time_t)NUM2INT(value);
+        bdb_test_error(envp->set_tx_timestamp(envp, &ti));
+    }
+#endif
+#endif
+#if BDB_VERSION < 30000
+    else if  (strcmp(options, "set_verbose") == 0) {
+        envp->db_verbose = NUM2INT(value);
+    }
+#else
+#if BDB_VERSION < 40300
+    else if (strcmp(options, "set_verb_chkpoint") == 0) {
+        bdb_test_error(envp->set_verbose(envp, DB_VERB_CHKPOINT, NUM2INT(value)));
+    }
+#endif
+    else if (strcmp(options, "set_verb_deadlock") == 0) {
+        bdb_test_error(envp->set_verbose(envp, DB_VERB_DEADLOCK, NUM2INT(value)));
+    }
+    else if (strcmp(options, "set_verb_recovery") == 0) {
+        bdb_test_error(envp->set_verbose(envp, DB_VERB_RECOVERY, NUM2INT(value)));
+    }
+    else if (strcmp(options, "set_verb_waitsfor") == 0) {
+        bdb_test_error(envp->set_verbose(envp, DB_VERB_WAITSFOR, NUM2INT(value)));
+    }
+#if BDB_VERSION >= 40000
+    else if (strcmp(options, "set_verb_replication") == 0) {
+        bdb_test_error(envp->set_verbose(envp, DB_VERB_REPLICATION, NUM2INT(value)));
+    }
+#endif
+#endif
+    else if (strcmp(options, "set_lk_detect") == 0) {
+#if BDB_VERSION < 30000
+	envp->lk_detect = NUM2INT(value);
+#else
+        bdb_test_error(envp->set_lk_detect(envp, NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_lk_max") == 0) {
+#if BDB_VERSION < 30000
+	envp->lk_max = NUM2INT(value);
+#else
+        bdb_test_error(envp->set_lk_max(envp, NUM2INT(value)));
+#endif
+    }
+    else if (strcmp(options, "set_lk_conflicts") == 0) {
+	int i, j, l, v;
+	unsigned char *conflits, *p;
+
+	Check_Type(value, T_ARRAY);
+	l = RARRAY(value)->len;
+	p = conflits = ALLOC_N(unsigned char, l * l);
+	for (i = 0; i < l; i++) {
+	    if (TYPE(RARRAY(value)->ptr[i]) != T_ARRAY ||
+		RARRAY(RARRAY(value)->ptr[i])->len != l) {
+		free(conflits);
+		rb_raise(bdb_eFatal, "invalid array for lk_conflicts");
+	    }
+	    for (j = 0; j < l; j++, p++) {
+		if (TYPE(RARRAY(RARRAY(value)->ptr[i])->ptr[j]) != T_FIXNUM) {
+		    free(conflits);
+		    rb_raise(bdb_eFatal, "invalid value for lk_conflicts");
+		}
+		v = NUM2INT(RARRAY(RARRAY(value)->ptr[i])->ptr[j]);
+		if (v != 0 && v != 1) {
+		    free(conflits);
+		    rb_raise(bdb_eFatal, "invalid value for lk_conflicts");
+		}
+		*p = (unsigned char)v;
+	    }
+	}
+#if BDB_VERSION < 30000
+	envp->lk_modes = l;
+	envp->lk_conflicts = conflits;
+#else
+        bdb_test_error(envp->set_lk_conflicts(envp, conflits, l));
+#endif
+    }
+    else if (strcmp(options, "set_lg_max") == 0) {
+	db_st->lg_max = NUM2INT(value);
+    }
+#if BDB_VERSION >= 30000
+    else if (strcmp(options, "set_lg_bsize") == 0) {
+	db_st->lg_bsize = NUM2INT(value);
+    }
+#endif
+    else if (strcmp(options, "set_data_dir") == 0) {
+	SafeStringValue(value);
+#if BDB_VERSION >= 30100
+	bdb_test_error(envp->set_data_dir(envp, StringValuePtr(value)));
+#else
+	{
+	    char *tmp;
+
+	    tmp = ALLOCA_N(char, strlen("DB_DATA_DIR") + RSTRING(value)->len + 2);
+	    sprintf(tmp, "DB_DATA_DIR %s", StringValuePtr(value));
+	    rb_ary_push(db_st->config, rb_str_new2(tmp));
+	}
+#endif
+    }
+    else if (strcmp(options, "set_lg_dir") == 0) {
+	SafeStringValue(value);
+#if BDB_VERSION >= 30100
+	bdb_test_error(envp->set_lg_dir(envp, StringValuePtr(value)));
+#else
+	{
+	    char *tmp;
+
+	    tmp = ALLOCA_N(char, strlen("DB_LOG_DIR") + RSTRING(value)->len + 2);
+	    sprintf(tmp, "DB_LOG_DIR %s", StringValuePtr(value));
+	    rb_ary_push(db_st->config, rb_str_new2(tmp));
+	}
+#endif
+    }
+    else if (strcmp(options, "set_tmp_dir") == 0) {
+	SafeStringValue(value);
+#if BDB_VERSION >= 30100
+	bdb_test_error(envp->set_tmp_dir(envp, StringValuePtr(value)));
+#else
+	{
+	    char *tmp;
+
+	    tmp = ALLOCA_N(char, strlen("DB_TMP_DIR") + RSTRING(value)->len + 2);
+	    sprintf(tmp, "DB_TMP_DIR %s", StringValuePtr(value));
+	    rb_ary_push(db_st->config, rb_str_new2(tmp));
+	}
+#endif
+    }
+#if BDB_VERSION >= 30100
+    else if (strcmp(options, "set_server") == 0 ||
+	strcmp(options, "set_rpc_server") == 0) {
+	char *host;
+	long sv_timeout, cl_timeout;
+	unsigned long flags;
+
+	host = 0;
+	sv_timeout = cl_timeout = 0;
+	flags = 0;
+	switch (TYPE(value)) {
+	case T_STRING:
+	    SafeStringValue(value);
+	    host = StringValuePtr(value);
+	    break;
+	case T_ARRAY:
+	    switch (RARRAY(value)->len) {
+	    default:
+	    case 3:
+		sv_timeout = NUM2INT(RARRAY(value)->ptr[2]);
+	    case 2:
+		cl_timeout = NUM2INT(RARRAY(value)->ptr[1]);
+	    case 1:
+		SafeStringValue(RARRAY(value)->ptr[0]);
+		host = StringValuePtr(RARRAY(value)->ptr[0]);
+		break;
+	    case 0:
+		rb_raise(bdb_eFatal, "Empty array for \"set_server\"");
+		break;
+	    }
+	    break;
+	default:
+	    rb_raise(bdb_eFatal, "Invalid type for \"set_server\"");
+	    break;
+	}
+#if BDB_VERSION >= 30300
+	bdb_test_error(envp->set_rpc_server(envp, NULL, host, cl_timeout, sv_timeout, flags));
+#else
+	bdb_test_error(envp->set_server(envp, host, cl_timeout, sv_timeout, flags));
+#endif
+    }
+#endif
+#if BDB_VERSION >= 30200
+    else if (strcmp(options, "set_flags") == 0) {
+        bdb_test_error(envp->set_flags(envp, NUM2UINT(value), 1));
+    }
+#endif
+    else if (strcmp(options, "marshal") == 0) {
+        switch (value) {
+        case Qtrue: envst->marshal = bdb_mMarshal; break;
+        case Qfalse: envst->marshal = Qfalse; break;
+        default: 
+	    if (!rb_respond_to(value, bdb_id_load) ||
+		!rb_respond_to(value, bdb_id_dump)) {
+		rb_raise(bdb_eFatal, "marshal value must be true or false");
+	    }
+	    envst->marshal = value;
+	    break;
+        }
+    }
+    else if (strcmp(options, "thread") == 0) {
+	if (RTEST(value)) {
+	    envst->options &= ~BDB_NO_THREAD;
+	}
+	else {
+	    envst->options |= BDB_NO_THREAD;
+	}
+    }
+#if BDB_VERSION >= 40000
+    else if (strcmp(options, "set_rep_transport") == 0) {
+	if (TYPE(value) != T_ARRAY || RARRAY(value)->len != 2) {
+	    rb_raise(bdb_eFatal, "expected an Array of length 2 for set_rep_transport");
+	}
+	if (!FIXNUM_P(RARRAY(value)->ptr[0])) {
+	    rb_raise(bdb_eFatal, "expected a Fixnum for the 1st arg of set_rep_transport");
+	}
+	if (!rb_respond_to(RARRAY(value)->ptr[1], bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "2nd arg must respond to #call");
+	}
+	envst->rep_transport = RARRAY(value)->ptr[1];
+	bdb_test_error(envst->envp->set_rep_transport(envst->envp, NUM2INT(RARRAY(value)->ptr[0]), bdb_env_rep_transport));
+	envst->options |= BDB_REP_TRANSPORT;
+    }
+    else if (strcmp(options, "set_timeout") == 0) {
+	if (TYPE(value) == T_ARRAY) {
+	    if (RARRAY(value)->len >= 1 && !NIL_P(RARRAY(value)->ptr[0])) {
+
+		bdb_test_error(envst->envp->set_timeout(envst->envp, 
+							NUM2UINT(RARRAY(value)->ptr[0]),
+							DB_SET_TXN_TIMEOUT));
+	    }
+	    if (RARRAY(value)->len == 2 && !NIL_P(RARRAY(value)->ptr[1])) {
+		bdb_test_error(envst->envp->set_timeout(envst->envp, 
+							NUM2UINT(RARRAY(value)->ptr[0]),
+							DB_SET_LOCK_TIMEOUT));
+	    }
+	}
+	else {
+	    bdb_test_error(envst->envp->set_timeout(envst->envp,
+						    NUM2UINT(value),
+						    DB_SET_TXN_TIMEOUT));
+	}
+    }
+    else if (strcmp(options, "set_txn_timeout") == 0) {
+	bdb_test_error(envst->envp->set_timeout(envst->envp,
+						NUM2UINT(value),
+						DB_SET_TXN_TIMEOUT));
+    }
+    else if (strcmp(options, "set_lock_timeout") == 0) {
+	bdb_test_error(envst->envp->set_timeout(envst->envp,
+						NUM2UINT(value),
+						DB_SET_LOCK_TIMEOUT));
+    }
+#endif
+#if BDB_VERSION >= 40100
+    else if (strcmp(options, "set_encrypt") == 0) {
+	char *passwd;
+	int flags = DB_ENCRYPT_AES;
+	if (TYPE(value) == T_ARRAY) {
+	    if (RARRAY(value)->len != 2) {
+		rb_raise(bdb_eFatal, "Expected an Array with 2 values");
+	    }
+	    passwd = StringValuePtr(RARRAY(value)->ptr[0]);
+	    flags = NUM2INT(RARRAY(value)->ptr[1]);
+	}
+	else {
+	    passwd = StringValuePtr(value);
+	}
+	bdb_test_error(envst->envp->set_encrypt(envst->envp, passwd, flags));
+	envst->options |= BDB_ENV_ENCRYPT;
+    }
+    else if (strcmp(options, "set_rep_limit") == 0) {
+	u_int32_t gbytes, bytes;
+	gbytes = bytes = 0;
+	if (TYPE(value) == T_ARRAY) {
+	    if (RARRAY(value)->len != 2) {
+		rb_raise(bdb_eFatal, "Expected an Array with 2 values");
+	    }
+	    gbytes = NUM2UINT(RARRAY(value)->ptr[0]);
+	    bytes = NUM2UINT(RARRAY(value)->ptr[1]);
+	}
+	else {
+	    bytes = NUM2UINT(RARRAY(value)->ptr[1]);
+	}
+	bdb_test_error(envst->envp->set_rep_limit(envst->envp, gbytes, bytes));
+    }
+#endif
+#if BDB_VERSION >= 30000
+    else if (strcmp(options, "set_feedback") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	envst->options |= BDB_FEEDBACK;
+	envst->feedback = value;
+	envst->envp->set_feedback(envst->envp, bdb_env_feedback);
+    }
+#endif
+#if BDB_VERSION >= 40100
+    else if (strcmp(options, "set_app_dispatch") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	envst->options |= BDB_APP_DISPATCH;
+	envst->app_dispatch = value;
+	envst->envp->set_app_dispatch(envst->envp, bdb_env_app_dispatch);
+    }
+#endif
+#if BDB_VERSION >= 40416
+    else if (strcmp(options, "set_msgcall") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	envst->msgcall = value;
+	envst->envp->set_msgcall(envst->envp, bdb_env_msgcall);
+    }
+    else if (strcmp(options, "set_thread_id") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	envst->thread_id = value;
+	envst->envp->set_thread_id(envst->envp, bdb_env_thread_id);
+    }
+    else if (strcmp(options, "set_thread_id_string") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	envst->thread_id_string = value;
+	envst->envp->set_thread_id_string(envst->envp, bdb_env_thread_id_string);
+    }
+    else if (strcmp(options, "set_isalive") == 0) {
+	if (!rb_respond_to(value, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	envst->isalive = value;
+	envst->envp->set_isalive(envst->envp, bdb_env_isalive);
+    }
+#endif
+#if BDB_VERSION >= 40250
+    else if (strcmp(options, "set_shm_key") == 0) {
+	bdb_test_error(envst->envp->set_shm_key(envst->envp, NUM2INT(value)));
+    }
+#endif
+    return Qnil;
+}
+
+struct env_iv {
+    bdb_ENV *envst;
+    VALUE env;
+};
+
+#if BDB_VERSION >= 30000
+
+static VALUE
+bdb_env_aref()
+{
+    VALUE obj;
+    bdb_ENV *envst;
+
+    GetIdEnv(obj, envst);
+    return obj;
+}
+
+#endif
+
+VALUE
+bdb_protect_close(VALUE obj)
+{
+    return rb_funcall2(obj, rb_intern("close"), 0, 0);
+}
+
+static void
+bdb_final(bdb_ENV *envst)
+{
+    VALUE obj, *ary;
+    bdb_ENV *thst;
+    int i;
+
+    ary = envst->db_ary.ptr;
+    if (ary) {
+        envst->db_ary.mark = Qtrue;
+        for (i = 0; i < envst->db_ary.len; i++) {
+            if (rb_respond_to(ary[i], rb_intern("close"))) {
+                rb_protect(bdb_protect_close, ary[i], 0);
+            }
+        }
+        envst->db_ary.mark = Qfalse;
+        envst->db_ary.total = envst->db_ary.len = 0;
+        envst->db_ary.ptr = 0;
+        free(ary);
+    }
+    if (envst->envp) {
+	if (!(envst->options & BDB_ENV_NOT_OPEN)) {
+#if BDB_VERSION < 30000
+	    db_appexit(envst->envp);
+	    free(envst->envp);
+#else
+	    envst->envp->close(envst->envp, 0);
+#endif
+	}
+	envst->envp = NULL;
+    }
+#if BDB_VERSION >= 30000
+    {
+	int status;
+
+	obj = rb_protect(bdb_env_aref, 0, &status);
+	if (!status) {
+	    Data_Get_Struct(obj, bdb_ENV, thst);
+	    if (thst == envst) {
+		rb_thread_local_aset(rb_thread_current(), bdb_id_current_env, Qnil);
+	    }
+	}
+    }
+#endif
+}
+
+static void
+bdb_env_free(bdb_ENV *envst)
+{
+    bdb_final(envst);
+    free(envst);
+}
+
+static VALUE
+bdb_env_close(VALUE obj)
+{
+    bdb_ENV *envst;
+
+    if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4) {
+	rb_raise(rb_eSecurityError, "Insecure: can't close the environnement");
+    }
+    GetEnvDB(obj, envst);
+    bdb_final(envst);
+    RDATA(obj)->dfree = free;
+    return Qnil;
+}
+
+static void
+bdb_env_mark(bdb_ENV *envst)
+{ 
+    rb_gc_mark(envst->marshal);
+#if BDB_VERSION >= 40000
+    rb_gc_mark(envst->rep_transport);
+#if BDB_VERSION >= 40100
+    rb_gc_mark(envst->app_dispatch);
+#endif
+#endif
+#if BDB_VERSION >= 40416
+    rb_gc_mark(envst->msgcall);
+    rb_gc_mark(envst->thread_id);
+    rb_gc_mark(envst->thread_id_string);
+    rb_gc_mark(envst->isalive);
+#endif
+#if BDB_VERSION >= 30000
+    rb_gc_mark(envst->feedback);
+#endif
+    rb_gc_mark(envst->home);
+    bdb_ary_mark(&envst->db_ary);
+}
+
+VALUE
+bdb_env_open_db(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE cl;
+
+    if (argc < 1)
+        rb_raise(bdb_eFatal, "Invalid number of arguments");
+    cl = argv[0];
+    if (FIXNUM_P(cl)) {
+	switch (NUM2INT(cl)) {
+	case DB_BTREE: cl = bdb_cBtree; break;
+	case DB_HASH: cl = bdb_cHash; break;
+	case DB_RECNO: cl = bdb_cRecno; break;
+#if BDB_VERSION >= 30000
+	case DB_QUEUE: cl = bdb_cQueue; break;
+#endif
+	case DB_UNKNOWN: cl = bdb_cUnknown; break;
+	default: rb_raise(bdb_eFatal, "Unknown database type");
+	}
+    }
+    else if (TYPE(cl) != T_CLASS) {
+	cl = CLASS_OF(cl);
+    }
+    MEMCPY(argv, argv + 1, VALUE, argc - 1);
+    if (argc > 1 && TYPE(argv[argc - 2]) == T_HASH) {
+	argc--;
+    }
+    else {
+	argv[argc - 1] = rb_hash_new();
+    }
+    if (rb_obj_is_kind_of(obj, bdb_cEnv)) {
+	rb_hash_aset(argv[argc - 1], rb_tainted_str_new2("env"), obj);
+    }
+    else {
+	rb_hash_aset(argv[argc - 1], rb_tainted_str_new2("txn"), obj);
+    }
+    return rb_funcall2(cl, rb_intern("new"), argc, argv);
+}
+
+#if BDB_VERSION >= 40300
+void
+bdb_env_errcall(const DB_ENV *env, const char *errpfx, const char *msg)
+{
+    bdb_errcall = 1;
+    bdb_errstr = rb_tainted_str_new2(msg);
+}
+
+#else
+
+void
+bdb_env_errcall(const char *errpfx, char *msg)
+{
+    bdb_errcall = 1;
+    bdb_errstr = rb_tainted_str_new2(msg);
+}
+
+#endif
+
+VALUE
+bdb_return_err()
+{
+    if (bdb_errcall) {
+	bdb_errcall = 0;
+	return bdb_errstr;
+    }
+    return Qnil;
+}
+
+#ifndef NT
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+struct timeval {
+    long    tv_sec;
+    long    tv_usec;
+};
+#endif /* HAVE_SYS_TIME_H */
+#endif /* NT */
+
+static int
+bdb_func_sleep(unsigned long sec, unsigned long usec)
+{
+    struct timeval timeout;
+    timeout.tv_sec = sec;
+    timeout.tv_usec = usec;
+    rb_thread_wait_for(timeout);
+    return 0;
+}
+
+static int
+bdb_func_yield()
+{
+    rb_thread_schedule();
+    return 0;
+}
+
+static void *
+bdb_func_malloc(size_t size)
+{
+    return malloc(size);
+}
+
+static VALUE
+bdb_set_func(bdb_ENV *envst)
+{
+#if BDB_VERSION >= 30000
+#if BDB_VERSION < 30114
+    bdb_test_error(envst->envp->set_func_sleep(envst->envp, bdb_func_sleep));
+    bdb_test_error(envst->envp->set_func_yield(envst->envp, bdb_func_yield));
+#else
+    bdb_test_error(db_env_set_func_sleep(bdb_func_sleep));
+    bdb_test_error(db_env_set_func_yield(bdb_func_yield));
+#endif
+#else
+    bdb_test_error(db_jump_set((void *)bdb_func_sleep, DB_FUNC_SLEEP));
+    bdb_test_error(db_jump_set((void *)bdb_func_yield, DB_FUNC_YIELD));
+#endif
+    return Qtrue;
+}
+
+static VALUE
+bdb_env_each_options(VALUE opt, VALUE stobj)
+{
+    VALUE res;
+    DB_ENV *envp;
+    struct db_stoptions *db_st;
+
+    res = rb_iterate(rb_each, opt, bdb_env_i_options, stobj);
+    Data_Get_Struct(stobj, struct db_stoptions, db_st);
+    envp = db_st->env->envp;
+#if BDB_VERSION >= 30000
+    if (db_st->lg_bsize) {
+	bdb_test_error(envp->set_lg_bsize(envp, db_st->lg_bsize));
+    }
+#endif
+    if (db_st->lg_max) {
+#if BDB_VERSION < 30000
+	envp->lg_max = db_st->lg_max;
+#else
+	bdb_test_error(envp->set_lg_max(envp, db_st->lg_max));
+#endif
+    }
+    return res;
+}
+
+static VALUE
+bdb_env_s_i_options(VALUE obj, int *flags)
+{
+    char *options;
+    VALUE key, value;
+
+    key = rb_ary_entry(obj, 0);
+    value = rb_ary_entry(obj, 1);
+    key = rb_obj_as_string(key);
+    options = StringValuePtr(key);
+    if (strcmp(options, "env_flags") == 0) {
+	*flags = NUM2INT(value);
+    }
+#ifdef DB_CLIENT
+    else if (strcmp(options, "set_rpc_server") == 0 ||
+	     strcmp(options, "set_server") == 0) {
+	*flags |= DB_CLIENT;
+    }
+#endif
+    return Qnil;
+}
+
+static VALUE
+bdb_env_s_alloc(VALUE obj)
+{
+    VALUE res;
+    bdb_ENV *envst;
+
+    res = Data_Make_Struct(obj, bdb_ENV, bdb_env_mark, bdb_env_free, envst);
+    envst->options |= BDB_ENV_NOT_OPEN;
+    return res;
+}
+
+static VALUE
+bdb_env_s_new(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_ENV *envst;
+    VALUE res;
+    int flags = 0;
+
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+    res = rb_obj_alloc(obj);
+#else
+    res = rb_funcall2(obj, rb_intern("allocate"), 0, 0);
+#endif
+    Data_Get_Struct(res, bdb_ENV, envst);
+#if BDB_VERSION < 30000
+    envst->envp = ALLOC(DB_ENV);
+    MEMZERO(envst->envp, DB_ENV, 1);
+    envst->envp->db_errpfx = "BDB::";
+    envst->envp->db_errcall = bdb_env_errcall;
+#else
+#if BDB_VERSION >= 30100
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+	rb_iterate(rb_each, argv[argc - 1], bdb_env_s_i_options, (VALUE)&flags);
+    }
+#endif
+    bdb_test_error(db_env_create(&(envst->envp), flags));
+    envst->envp->set_errpfx(envst->envp, "BDB::");
+    envst->envp->set_errcall(envst->envp, bdb_env_errcall);
+#if BDB_VERSION >= 30300
+    bdb_test_error(envst->envp->set_alloc(envst->envp, malloc, realloc, free));
+#endif
+#endif
+    rb_obj_call_init(res, argc, argv);
+    return res;
+}
+
+
+#if BDB_VERSION >= 40000
+
+VALUE 
+bdb_env_s_rslbl(int argc, VALUE *argv, VALUE obj, DB_ENV *env)
+{
+    bdb_ENV *envst;
+    VALUE res;
+
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+    res = rb_obj_alloc(obj);
+#else
+    res = rb_funcall2(obj, rb_intern("allocate"), 0, 0);
+#endif
+    Data_Get_Struct(res, bdb_ENV, envst);
+    envst->envp = env;
+    envst->envp->set_errpfx(envst->envp, "BDB::");
+    envst->envp->set_errcall(envst->envp, bdb_env_errcall);
+    bdb_test_error(envst->envp->set_alloc(envst->envp, malloc, realloc, free));
+    rb_obj_call_init(res, argc, argv);
+    return res;
+}
+
+#endif
+
+VALUE
+bdb_env_init(int argc, VALUE *argv, VALUE obj)
+{
+    DB_ENV *envp;
+    bdb_ENV *envst;
+    VALUE a, c, d;
+    char *db_home, **db_config;
+    int ret, mode, flags;
+    VALUE st_config;
+    VALUE envid;
+
+    envid = 0;
+    st_config = 0;
+    db_config = 0;
+    mode = flags = 0;
+    if (!RDATA(obj)->dmark) {
+        RDATA(obj)->dmark = (RUBY_DATA_FUNC)bdb_env_mark;
+    }
+    Data_Get_Struct(obj, bdb_ENV, envst);
+#if BDB_VERSION >= 30000
+    envst->envp->set_errcall(envst->envp, bdb_env_errcall);
+#endif
+    envp = envst->envp;
+#if BDB_VERSION >= 40100
+    if (rb_const_defined(CLASS_OF(obj), rb_intern("BDB_ENCRYPT"))) {
+	char *passwd;
+	int flags = DB_ENCRYPT_AES;
+	VALUE value = rb_const_get(CLASS_OF(obj), rb_intern("BDB_ENCRYPT"));
+	if (TYPE(value) == T_ARRAY) {
+	    if (RARRAY(value)->len != 2) {
+		rb_raise(bdb_eFatal, "Expected an Array with 2 values");
+	    }
+	    passwd = StringValuePtr(RARRAY(value)->ptr[0]);
+	    flags = NUM2INT(RARRAY(value)->ptr[1]);
+	}
+	else {
+	    passwd = StringValuePtr(value);
+	}
+	bdb_test_error(envp->set_encrypt(envp, passwd, flags));
+	envst->options |= BDB_ENV_ENCRYPT;
+    }
+#endif
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+	int i;
+	VALUE db_stobj;
+	struct db_stoptions *db_st;
+
+	st_config = rb_ary_new();
+	db_stobj = Data_Make_Struct(rb_cObject, struct db_stoptions, 0, free, db_st);
+	db_st->env = envst;
+	db_st->config = st_config;
+	bdb_env_each_options(argv[argc - 1], db_stobj);
+	if (RARRAY(st_config)->len > 0) {
+	    db_config = ALLOCA_N(char *, RARRAY(st_config)->len + 1);
+	    for (i = 0; i < RARRAY(st_config)->len; i++) {
+		db_config[i] = StringValuePtr(RARRAY(st_config)->ptr[i]);
+	    }
+	    db_config[RARRAY(st_config)->len] = 0;
+	}
+	argc--;
+    }
+    rb_scan_args(argc, argv, "12", &a, &c, &d);
+    SafeStringValue(a);
+    db_home = StringValuePtr(a);
+    switch (argc) {
+    case 3:
+	mode = NUM2INT(d);
+    case 2:
+	flags = NUM2INT(c);
+        break;
+    }
+    if (flags & DB_CREATE) {
+	rb_secure(4);
+    }
+    if (flags & DB_USE_ENVIRON) {
+	rb_secure(1);
+    }
+#ifndef BDB_NO_THREAD_COMPILE
+    if (!(envst->options & BDB_NO_THREAD)) {
+	bdb_set_func(envst);
+	flags |= DB_THREAD;
+    }
+#endif
+#if BDB_VERSION < 30000
+    if ((ret = db_appinit(db_home, db_config, envp, flags)) != 0) {
+	if (envst->envp) {
+	    free(envst->envp);
+	}
+	envst->envp = NULL;
+        if (bdb_errcall) {
+            bdb_errcall = 0;
+            rb_raise(bdb_eFatal, "%s -- %s", StringValuePtr(bdb_errstr), db_strerror(ret));
+        }
+        else
+            rb_raise(bdb_eFatal, "%s", db_strerror(ret));
+    }
+#else
+#if BDB_VERSION >= 40000
+    if (envst->rep_transport == 0 && rb_respond_to(obj, rb_intern("bdb_rep_transport")) == Qtrue) {
+	if (!rb_const_defined(CLASS_OF(obj), rb_intern("ENVID"))) {
+	    rb_raise(bdb_eFatal, "ENVID must be defined to use rep_transport");
+	}
+	envid = rb_const_get(CLASS_OF(obj), rb_intern("ENVID"));
+	bdb_test_error(envp->set_rep_transport(envp, NUM2INT(envid),
+						 bdb_env_rep_transport));
+	envst->options |= BDB_REP_TRANSPORT;
+    }
+#endif
+#if BDB_VERSION >= 30000
+    if (envst->feedback == 0 && rb_respond_to(obj, id_feedback) == Qtrue) {
+	envp->set_feedback(envp, bdb_env_feedback);
+	envst->options |= BDB_FEEDBACK;
+    }
+#endif
+#if BDB_VERSION >= 40100
+    if (envst->app_dispatch == 0 && rb_respond_to(obj, id_app_dispatch) == Qtrue) {
+	envp->set_app_dispatch(envp, bdb_env_app_dispatch);
+	envst->options |= BDB_APP_DISPATCH;
+    }
+#endif
+#if BDB_VERSION >= 40416
+    if (envst->msgcall == 0 && rb_respond_to(obj, id_msgcall) == Qtrue) {
+	envp->set_msgcall(envp, bdb_env_msgcall);
+    }
+    if (envst->thread_id == 0 && rb_respond_to(obj, id_thread_id) == Qtrue) {
+	envp->set_thread_id(envp, bdb_env_thread_id);
+    }
+    if (envst->thread_id_string == 0 && rb_respond_to(obj, id_thread_id_string) == Qtrue) {
+	envp->set_thread_id_string(envp, bdb_env_thread_id_string);
+    }
+    if (envst->isalive == 0 && rb_respond_to(obj, id_isalive) == Qtrue) {
+	envp->set_isalive(envp, bdb_env_isalive);
+    }
+#endif
+#if BDB_VERSION >= 30100
+    if ((ret = envp->open(envp, db_home, flags, mode)) != 0)
+#else
+    if ((ret = envp->open(envp, db_home, db_config, flags, mode)) != 0)
+#endif
+    {
+        envp->close(envp, 0);
+	envst->envp = NULL;
+        if (bdb_errcall) {
+            bdb_errcall = 0;
+            rb_raise(bdb_eFatal, "%s -- %s", StringValuePtr(bdb_errstr), db_strerror(ret));
+        }
+        else
+            rb_raise(bdb_eFatal, "%s", db_strerror(ret));
+    }
+#endif
+    envst->options &= ~BDB_ENV_NOT_OPEN;
+    if (flags & DB_INIT_LOCK) {
+	envst->options |= BDB_INIT_LOCK;
+    }
+    if (flags & DB_INIT_TXN) {
+	envst->options |= BDB_AUTO_COMMIT;
+    }
+    envst->home = rb_tainted_str_new2(db_home);
+    OBJ_FREEZE(envst->home);
+#if BDB_VERSION >= 30000
+    if (envst->options & BDB_NEED_ENV_CURRENT) {
+	rb_thread_local_aset(rb_thread_current(), bdb_id_current_env, obj);
+    }
+#endif
+    return obj;
+}
+
+static VALUE
+bdb_env_internal_close(VALUE obj)
+{
+    return rb_funcall2(obj, rb_intern("close"), 0, 0);
+}
+
+static VALUE
+bdb_env_s_open(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE res = rb_funcall2(obj, rb_intern("new"), argc, argv);
+    if (rb_block_given_p()) {
+	return rb_ensure(rb_yield, res, bdb_env_internal_close, res);
+    }
+    return res;
+}
+
+static VALUE
+bdb_env_s_remove(int argc, VALUE *argv, VALUE obj)
+{
+    DB_ENV *envp;
+    VALUE a, b;
+    char *db_home;
+    int flag = 0;
+
+    rb_secure(2);
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+	flag = NUM2INT(b);
+    }
+    db_home = StringValuePtr(a);
+#if BDB_VERSION < 30000
+    envp = ALLOCA_N(DB_ENV, 1);
+    MEMZERO(envp, DB_ENV, 1);
+    envp->db_errpfx = "BDB::";
+    envp->db_errcall = bdb_env_errcall;
+    if (lock_unlink(db_home, flag, envp) == EBUSY) {
+	rb_raise(bdb_eFatal, "The shared memory region was in use");
+    }
+    if (log_unlink(db_home, flag, envp) == EBUSY) {
+	rb_raise(bdb_eFatal, "The shared memory region was in use");
+    }
+    if (memp_unlink(db_home, flag, envp) == EBUSY) {
+	rb_raise(bdb_eFatal, "The shared memory region was in use");
+    }
+    if (txn_unlink(db_home, flag, envp) == EBUSY) {
+	rb_raise(bdb_eFatal, "The shared memory region was in use");
+    }
+#else
+    bdb_test_error(db_env_create(&envp, 0));
+    envp->set_errpfx(envp, "BDB::");
+    envp->set_errcall(envp, bdb_env_errcall);
+#if BDB_VERSION < 30100
+    bdb_test_error(envp->remove(envp, db_home, NULL, flag));
+#else
+    bdb_test_error(envp->remove(envp, db_home, flag));
+#endif
+#endif
+    return Qtrue;
+}
+
+static VALUE
+bdb_env_set_flags(int argc, VALUE *argv, VALUE obj)
+{
+#if BDB_VERSION >= 30200
+    bdb_ENV *envst;
+    VALUE opt, flag;
+    int state = 1;
+
+    GetEnvDB(obj, envst);
+    if (rb_scan_args(argc, argv, "11", &flag, &opt)) {
+	switch (TYPE(opt)) {
+	case T_TRUE:
+	    state = 1;
+	    break;
+	case T_FALSE:
+	    state = 0;
+	    break;
+	case T_FIXNUM:
+	    state = NUM2INT(opt);
+	    break;
+	default:
+	    rb_raise(bdb_eFatal, "invalid value for onoff");
+	}
+    }
+    bdb_test_error(envst->envp->set_flags(envst->envp, NUM2INT(flag), state));
+#endif
+    return Qnil;
+}
+
+static VALUE
+bdb_env_home(VALUE obj)
+{
+    bdb_ENV *envst;
+    GetEnvDB(obj, envst);
+    return envst->home;
+}
+
+#if BDB_VERSION >= 40000
+
+static VALUE
+bdb_env_iterate(VALUE *tmp)
+{
+    return rb_funcall2(tmp[0], rb_intern("__bdb_thread_init__"), 
+		       (int)tmp[1], (VALUE *)tmp[2]);
+}
+
+static VALUE
+bdb_thread_init(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE env;
+
+    if ((env = rb_thread_local_aref(rb_thread_current(), bdb_id_current_env)) != Qnil) {
+	rb_thread_local_aset(obj, bdb_id_current_env, env);
+    }
+    if (rb_block_given_p()) {
+	VALUE tmp[3];
+	tmp[0] = obj;
+	tmp[1] = (VALUE)argc;
+	tmp[2] = (VALUE)argv;
+	return rb_iterate((VALUE (*)(VALUE))bdb_env_iterate, (VALUE)tmp, rb_yield, obj);
+    }
+    return rb_funcall2(obj, rb_intern("__bdb_thread_init__"), argc, argv);
+}
+
+#endif
+
+#if BDB_VERSION >= 30000
+
+static VALUE
+bdb_env_feedback_set(VALUE obj, VALUE a)
+{
+    bdb_ENV *envst;
+
+    GetEnvDB(obj, envst);
+    if (NIL_P(a)) {
+	envst->feedback = a;
+    }
+    else {
+	if (!rb_respond_to(a, bdb_id_call)) {
+	    rb_raise(bdb_eFatal, "arg must respond to #call");
+	}
+	envst->feedback = a;
+	if (!(envst->options & BDB_NEED_ENV_CURRENT)) {
+	    envst->options |= BDB_FEEDBACK;
+	    rb_thread_local_aset(rb_thread_current(), bdb_id_current_env, obj);
+	}
+    }
+    return a;
+}
+
+#endif
+
+#if BDB_VERSION >= 40250
+
+static VALUE
+bdb_env_i_conf(VALUE obj, VALUE a)
+{
+    bdb_ENV *envst;
+    u_int32_t bytes, gbytes, value, lk_detect;
+    int i, ncache;
+    char *str;
+    const char *strval, **dirs;
+    db_timeout_t timeout;
+    long shm_key;
+    time_t timeval;
+    size_t size;
+    VALUE res;
+
+    GetEnvDB(obj, envst);
+    str = StringValuePtr(a);
+    if (strcmp(str, "cachesize") == 0) {
+	bdb_test_error(envst->envp->get_cachesize(envst->envp, &gbytes, &bytes, &ncache));
+	res = rb_ary_new2(3);
+	rb_ary_push(res, INT2NUM(gbytes));
+	rb_ary_push(res, INT2NUM(bytes));
+	rb_ary_push(res, INT2NUM(ncache));
+	return res;
+    }
+    if (strcmp(str, "data_dirs") == 0) {
+	bdb_test_error(envst->envp->get_data_dirs(envst->envp, &dirs));
+	res = rb_ary_new();
+	if (dirs) {
+	    for (i = 0; dirs[i] != NULL; i++) {
+		rb_ary_push(res, rb_tainted_str_new2(dirs[i]));
+	    }
+	}
+	return res;
+    }
+    if (strcmp(str, "flags") == 0) {
+	bdb_test_error(envst->envp->get_flags(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "home") == 0) {
+	bdb_test_error(envst->envp->get_home(envst->envp, &strval));
+	if (strval && strlen(strval)) {
+	    return rb_tainted_str_new2(strval);
+	}
+	return Qnil;
+    }
+    if (strcmp(str, "lg_bsize") == 0) {
+	bdb_test_error(envst->envp->get_lg_bsize(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "lg_dir") == 0) {
+	bdb_test_error(envst->envp->get_lg_dir(envst->envp, &strval));
+	if (strval && strlen(strval)) {
+	    return rb_tainted_str_new2(strval);
+	}
+	return Qnil;
+    }
+    if (strcmp(str, "lg_max") == 0) {
+	bdb_test_error(envst->envp->get_lg_max(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "lg_regionmax") == 0) {
+	bdb_test_error(envst->envp->get_lg_regionmax(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "lk_detect") == 0) {
+	bdb_test_error(envst->envp->get_lk_detect(envst->envp, &lk_detect));
+	return INT2NUM(lk_detect);
+    }
+    if (strcmp(str, "lk_max_lockers") == 0) {
+	bdb_test_error(envst->envp->get_lk_max_lockers(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "lk_max_locks") == 0) {
+	bdb_test_error(envst->envp->get_lk_max_locks(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "lk_max_objects") == 0) {
+	bdb_test_error(envst->envp->get_lk_max_objects(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "mp_mmapsize") == 0) {
+	bdb_test_error(envst->envp->get_mp_mmapsize(envst->envp, &size));
+	return INT2NUM(size);
+    }
+    if (strcmp(str, "open_flags") == 0) {
+	bdb_test_error(envst->envp->get_open_flags(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "rep_limit") == 0) {
+	bdb_test_error(envst->envp->get_rep_limit(envst->envp, &gbytes, &bytes));
+	res = rb_ary_new2(2);
+	rb_ary_push(res, INT2NUM(gbytes));
+	rb_ary_push(res, INT2NUM(bytes));
+	return res;
+    }
+    if (strcmp(str, "shm_key") == 0) {
+	bdb_test_error(envst->envp->get_shm_key(envst->envp, &shm_key));
+	return INT2NUM(shm_key);
+    }
+    if (strcmp(str, "tas_spins") == 0) {
+#if BDB_VERSION >= 40416
+	rb_warn("Invalid option :tas_spins");
+	return Qfalse;
+#else
+	bdb_test_error(envst->envp->get_tas_spins(envst->envp, &value));
+	return INT2NUM(value);
+#endif
+    }
+    if (strcmp(str, "txn_timeout") == 0) {
+	bdb_test_error(envst->envp->get_timeout(envst->envp, &timeout, DB_SET_TXN_TIMEOUT));
+	return INT2NUM(timeout);
+    }
+    if (strcmp(str, "lock_timeout") == 0) {
+	bdb_test_error(envst->envp->get_timeout(envst->envp, &timeout, DB_SET_LOCK_TIMEOUT));
+	return INT2NUM(timeout);
+    }
+    if (strcmp(str, "tmp_dir") == 0) {
+	bdb_test_error(envst->envp->get_tmp_dir(envst->envp, &strval));
+	if (strval && strlen(strval)) {
+	    return rb_tainted_str_new2(strval);
+	}
+	return Qnil;
+    }
+    if (strcmp(str, "tx_max") == 0) {
+	bdb_test_error(envst->envp->get_tx_max(envst->envp, &value));
+	return INT2NUM(value);
+    }
+    if (strcmp(str, "tx_timestamp") == 0) {
+	bdb_test_error(envst->envp->get_tx_timestamp(envst->envp, &timeval));
+	return INT2NUM(timeval);
+    }
+    rb_raise(rb_eArgError, "Unknown option %s", str);
+    return obj;
+}
+
+static char *
+options[] = {
+    "cachesize", "data_dirs", "flags", "home", "lg_bsize", "lg_dir",
+    "lg_max", "lg_regionmax", "lk_detect", "lk_max_lockers", "lk_max_locks",
+    "lk_max_objects", "mp_mmapsize", "open_flags", "rep_limit", "shm_key",
+    "tas_spins", "txn_timeout", "lock_timeout", "tmp_dir", "tx_max",
+    "tx_timestamp", 0
+};
+
+struct optst {
+    VALUE obj, str;
+};
+
+static VALUE
+bdb_env_intern_conf(struct optst *optp)
+{
+    return bdb_env_i_conf(optp->obj, optp->str);
+}
+
+static VALUE
+bdb_env_conf(int argc, VALUE *argv, VALUE obj)
+{
+    int i, state;
+    VALUE res, val;
+    struct optst opt;
+
+    if (argc > 1) {
+	rb_raise(rb_eArgError, "invalid number of arguments (%d for 1)", argc);
+    }
+    if (argc == 1) {
+	return bdb_env_i_conf(obj, argv[0]);
+    }
+    res = rb_hash_new();
+    opt.obj = obj;
+    for (i = 0; options[i] != NULL; i++) {
+	opt.str = rb_str_new2(options[i]);
+	val = rb_protect((VALUE (*)(ANYARGS))bdb_env_intern_conf, (VALUE)&opt, &state);
+	if (state == 0) {
+	    rb_hash_aset(res, opt.str, val);
+	}
+    }
+    return res;
+}
+
+#endif 
+
+#if BDB_VERSION >= 40416
+
+static VALUE
+bdb_env_lsn_reset(int argc, VALUE *argv, VALUE obj)
+{  
+    char *file;
+    int flags;
+    VALUE a, b;
+    bdb_ENV *envst;
+
+    GetEnvDB(obj, envst);
+    flags = 0;
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+	flags = NUM2INT(b);
+    }
+    file = StringValuePtr(a);
+    bdb_test_error(envst->envp->lsn_reset(envst->envp, file, flags));
+    return obj;
+}
+
+static VALUE
+bdb_env_fileid_reset(int argc, VALUE *argv, VALUE obj)
+{  
+    char *file;
+    int flags;
+    VALUE a, b;
+    bdb_ENV *envst;
+
+    GetEnvDB(obj, envst);
+    flags = 0;
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+	flags = NUM2INT(b);
+    }
+    file = StringValuePtr(a);
+    bdb_test_error(envst->envp->fileid_reset(envst->envp, file, flags));
+    return obj;
+}
+
+static VALUE
+bdb_env_set_msgcall(VALUE obj, VALUE a)
+{
+    bdb_ENV *envst;
+
+    GetEnvDB(obj, envst);
+    if (NIL_P(a)) {
+	envst->msgcall = Qnil;
+	envst->envp->set_msgcall(envst->envp, NULL);
+	return obj;
+    }
+    if (!rb_respond_to(a, bdb_id_call)) {
+	rb_raise(rb_eArgError, "object must respond to #call");
+    }
+    if (!RTEST(envst->msgcall)) {
+	envst->envp->set_msgcall(envst->envp, bdb_env_msgcall);
+    }
+    envst->msgcall = a;
+    return obj;
+}
+
+static VALUE
+bdb_env_set_thread_id(VALUE obj, VALUE a)
+{
+    bdb_ENV *envst;
+
+    GetEnvDB(obj, envst);
+    if (!rb_respond_to(a, bdb_id_call)) {
+	rb_raise(rb_eArgError, "object must respond to #call");
+    }
+    if (!RTEST(envst->thread_id)) {
+	envst->envp->set_thread_id(envst->envp, bdb_env_thread_id);
+    }
+    envst->thread_id = a;
+    return obj;
+}
+
+static VALUE
+bdb_env_set_thread_id_string(VALUE obj, VALUE a)
+{
+    bdb_ENV *envst;
+
+    GetEnvDB(obj, envst);
+    if (!rb_respond_to(a, bdb_id_call)) {
+	rb_raise(rb_eArgError, "object must respond to #call");
+    }
+    if (!RTEST(envst->thread_id_string)) {
+	envst->envp->set_thread_id_string(envst->envp, bdb_env_thread_id_string);
+    }
+    envst->thread_id_string = a;
+    return obj;
+}
+
+static VALUE
+bdb_env_set_isalive(VALUE obj, VALUE a)
+{
+    bdb_ENV *envst;
+
+    GetEnvDB(obj, envst);
+    if (!rb_respond_to(a, bdb_id_call)) {
+	rb_raise(rb_eArgError, "object must respond to #call");
+    }
+    if (!RTEST(envst->isalive)) {
+	envst->envp->set_isalive(envst->envp, bdb_env_isalive);
+    }
+    envst->isalive = a;
+    return obj;
+}
+
+static VALUE
+bdb_env_failcheck(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_ENV *envst;
+    int flags = 0;
+    VALUE a;
+
+    GetEnvDB(obj, envst);
+    if (rb_scan_args(argc, argv, "01", &a)) {
+	flags = NUM2INT(a);
+    }
+    bdb_test_error(flags = envst->envp->failchk(envst->envp, flags));
+    return INT2NUM(flags);
+}
+
+
+#endif
+
+void bdb_init_env()
+{
+    bdb_id_call = rb_intern("call");
+#if BDB_VERSION >= 30000
+    id_feedback = rb_intern("bdb_feedback");
+#endif
+    bdb_id_current_env = rb_intern("bdb_current_env");
+#if BDB_VERSION >= 40100
+    id_app_dispatch = rb_intern("bdb_app_dispatch");
+#if BDB_VERSION >= 40416
+    id_msgcall = rb_intern("bdb_msgcall");
+    id_thread_id = rb_intern("bdb_thread_id");
+    id_thread_id_string = rb_intern("bdb_thread_id_string");
+    id_isalive = rb_intern("bdb_isalive");
+#endif
+#endif
+    bdb_cEnv = rb_define_class_under(bdb_mDb, "Env", rb_cObject);
+    rb_define_private_method(bdb_cEnv, "initialize", bdb_env_init, -1);
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+    rb_define_alloc_func(bdb_cEnv, bdb_env_s_alloc);
+#else
+    rb_define_singleton_method(bdb_cEnv, "allocate", bdb_env_s_alloc, 0);
+#endif
+    rb_define_singleton_method(bdb_cEnv, "new", bdb_env_s_new, -1);
+    rb_define_singleton_method(bdb_cEnv, "create", bdb_env_s_new, -1);
+    rb_define_singleton_method(bdb_cEnv, "open", bdb_env_s_open, -1);
+    rb_define_singleton_method(bdb_cEnv, "remove", bdb_env_s_remove, -1);
+    rb_define_singleton_method(bdb_cEnv, "unlink", bdb_env_s_remove, -1);
+    rb_define_method(bdb_cEnv, "open_db", bdb_env_open_db, -1);
+    rb_define_method(bdb_cEnv, "close", bdb_env_close, 0);
+    rb_define_method(bdb_cEnv, "set_flags", bdb_env_set_flags, -1);
+    rb_define_method(bdb_cEnv, "home", bdb_env_home, 0);
+#if BDB_VERSION >= 40000
+    rb_define_method(bdb_cEnv, "rep_elect", bdb_env_rep_elect, -1);
+    rb_define_method(bdb_cEnv, "elect", bdb_env_rep_elect, -1);
+    rb_define_method(bdb_cEnv, "rep_process_message", bdb_env_rep_process_message, 3);
+    rb_define_method(bdb_cEnv, "process_message", bdb_env_rep_process_message, 3);
+    rb_define_method(bdb_cEnv, "rep_start", bdb_env_rep_start, 2);
+    if (!rb_method_boundp(rb_cThread, rb_intern("__bdb_thread_init__"), 1)) {
+	rb_alias(rb_cThread, rb_intern("__bdb_thread_init__"), rb_intern("initialize"));
+	rb_define_method(rb_cThread, "initialize", bdb_thread_init, -1);
+    }
+#if BDB_VERSION >= 40100
+    rb_define_method(bdb_cEnv, "rep_limit=", bdb_env_rep_limit, -1);
+#endif
+#endif
+#if BDB_VERSION >= 30000
+    rb_define_method(bdb_cEnv, "feedback=", bdb_env_feedback_set, 1);
+#endif
+#if BDB_VERSION >= 40250
+    rb_define_method(bdb_cEnv, "configuration", bdb_env_conf, -1);
+    rb_define_method(bdb_cEnv, "conf", bdb_env_conf, -1);
+#endif
+#if BDB_VERSION >= 40416
+    rb_define_method(bdb_cEnv, "lsn_reset", bdb_env_lsn_reset, -1);
+    rb_define_method(bdb_cEnv, "fileid_reset", bdb_env_fileid_reset, -1);
+    rb_define_method(bdb_cEnv, "msgcall=", bdb_env_set_msgcall, 1);
+    rb_define_method(bdb_cEnv, "thread_id=", bdb_env_set_thread_id, 1);
+    rb_define_method(bdb_cEnv, "thread_id_string=", bdb_env_set_thread_id_string, 1);
+    rb_define_method(bdb_cEnv, "isalive=", bdb_env_set_isalive, 1);
+    rb_define_method(bdb_cEnv, "failcheck", bdb_env_failcheck, -1);
+#endif
+}

Added: packages/libdb4.3-ruby/branches/upstream/current/src/lock.c
===================================================================
--- packages/libdb4.3-ruby/branches/upstream/current/src/lock.c	                        (rev 0)
+++ packages/libdb4.3-ruby/branches/upstream/current/src/lock.c	2007-12-08 12:00:45 UTC (rev 2142)
@@ -0,0 +1,499 @@
+#include "bdb.h"
+
+static void 
+lockid_mark( bdb_LOCKID *dblockid)
+{
+    rb_gc_mark(dblockid->env);
+}
+
+void
+bdb_clean_env(VALUE env, VALUE obj)
+{
+    bdb_ENV *envst;
+    Data_Get_Struct(env, bdb_ENV, envst);
+    bdb_ary_delete(&envst->db_ary, obj);
+}
+    
+static void 
+lockid_free( bdb_LOCKID *dblockid)
+{
+#if BDB_VERSION >= 40000
+    bdb_ENV *envst;
+
+    bdb_clean_env(dblockid->env, dblockid->self);
+    Data_Get_Struct(dblockid->env, bdb_ENV, envst);
+    if (envst->envp) {
+	envst->envp->lock_id_free(envst->envp, dblockid->lock);
+    }
+#endif
+    free(dblockid);
+}
+
+static VALUE
+bdb_env_lockid(VALUE obj)
+{
+    unsigned int idp;
+    bdb_ENV *envst;
+    bdb_LOCKID *dblockid;
+    VALUE a;
+
+    GetEnvDB(obj, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lk_info) {
+	rb_raise(bdb_eLock, "lock region not open");
+    }
+    bdb_test_error(lock_id(envst->envp->lk_info, &idp));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->lock_id(envst->envp, &idp));
+#else
+    bdb_test_error(lock_id(envst->envp, &idp));
+#endif
+#endif
+    a = Data_Make_Struct(bdb_cLockid, bdb_LOCKID, lockid_mark, lockid_free, dblockid);
+    dblockid->lock = idp;
+    dblockid->env = obj;
+    dblockid->self = a;
+#if BDB_VERSION >= 40000
+    bdb_ary_push(&envst->db_ary, a);
+#endif
+    return a;
+}
+
+static VALUE
+bdb_env_lockid_close(VALUE obj)
+{
+    bdb_ENV *envst;
+    bdb_LOCKID *dblockid;
+
+    Data_Get_Struct(obj, bdb_LOCKID, dblockid);
+    bdb_clean_env(dblockid->env, obj);
+#if BDB_VERSION >= 40000
+    GetEnvDB(dblockid->env, envst);
+    RDATA(obj)->dfree = free;
+    if (envst->envp) {
+	bdb_test_error(envst->envp->lock_id_free(envst->envp, dblockid->lock));
+    }
+#endif
+    dblockid->env = 0;
+    return Qnil;
+}
+
+static VALUE
+bdb_env_lockdetect(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE a, b;
+    bdb_ENV *envst;
+    int flags, atype, aborted;
+
+    flags = atype = aborted = 0;
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+	flags = NUM2INT(b);
+    }
+    atype = NUM2INT(a);
+    GetEnvDB(obj, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lk_info) {
+	rb_raise(bdb_eLock, "lock region not open");
+    }
+    bdb_test_error(lock_detect(envst->envp->lk_info, flags, atype));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->lock_detect(envst->envp, flags, atype, &aborted));
+#else
+    bdb_test_error(lock_detect(envst->envp, flags, atype, &aborted));
+#endif
+#endif
+    return INT2NUM(aborted);
+}
+
+static VALUE
+bdb_env_lockstat(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_ENV *envst;
+    DB_LOCK_STAT *statp;
+    VALUE a, b;
+    int flags;
+
+    GetEnvDB(obj, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lk_info) {
+	rb_raise(bdb_eLock, "lock region not open");
+    }
+    if (argc != 0) {
+	rb_raise(rb_eArgError, "invalid number of arguments (%d for 0)", argc);
+    }
+    bdb_test_error(lock_stat(envst->envp->lk_info, &statp, 0));
+    a = rb_hash_new();
+    rb_hash_aset(a, rb_tainted_str_new2("st_magic"), INT2NUM(statp->st_magic));
+    rb_hash_aset(a, rb_tainted_str_new2("st_version"), INT2NUM(statp->st_version));
+    rb_hash_aset(a, rb_tainted_str_new2("st_refcnt"), INT2NUM(statp->st_refcnt));
+    rb_hash_aset(a, rb_tainted_str_new2("st_numobjs"), INT2NUM(statp->st_numobjs));
+    rb_hash_aset(a, rb_tainted_str_new2("st_regsize"), INT2NUM(statp->st_regsize));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxlocks"), INT2NUM(statp->st_maxlocks));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nmodes"), INT2NUM(statp->st_nmodes));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nlockers"), INT2NUM(statp->st_nlockers));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nconflicts"), INT2NUM(statp->st_nconflicts));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nrequests"), INT2NUM(statp->st_nrequests));
+    rb_hash_aset(a, rb_tainted_str_new2("st_ndeadlocks"), INT2NUM(statp->st_ndeadlocks));
+    rb_hash_aset(a, rb_tainted_str_new2("st_region_wait"), INT2NUM(statp->st_region_wait));
+    rb_hash_aset(a, rb_tainted_str_new2("st_region_nowait"), INT2NUM(statp->st_region_nowait));
+#else
+#if BDB_VERSION >= 40000
+    flags = 0;
+    if (rb_scan_args(argc, argv, "01", &b) == 1) {
+	flags = NUM2INT(b);
+    }
+    bdb_test_error(envst->envp->lock_stat(envst->envp, &statp, flags));
+    a = rb_hash_new();
+#if BDB_VERSION >= 40100
+    rb_hash_aset(a, rb_tainted_str_new2("st_lastid"), INT2NUM(statp->st_id));
+#else
+    rb_hash_aset(a, rb_tainted_str_new2("st_lastid"), INT2NUM(statp->st_lastid));
+#endif
+    rb_hash_aset(a, rb_tainted_str_new2("st_nmodes"), INT2NUM(statp->st_nmodes));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxlocks"), INT2NUM(statp->st_maxlocks));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxlockers"), INT2NUM(statp->st_maxlockers));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxobjects"), INT2NUM(statp->st_maxobjects));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nlocks"), INT2NUM(statp->st_nlocks));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxnlocks"), INT2NUM(statp->st_maxnlocks));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nlockers"), INT2NUM(statp->st_nlockers));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxnlockers"), INT2NUM(statp->st_maxnlockers));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nobjects"), INT2NUM(statp->st_nobjects));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxnobjects"), INT2NUM(statp->st_maxnobjects));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nrequests"), INT2NUM(statp->st_nrequests));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nreleases"), INT2NUM(statp->st_nreleases));
+#if BDB_VERSION >= 40416
+    rb_hash_aset(a, rb_tainted_str_new2("st_lock_nowait"), INT2NUM(statp->st_lock_nowait));
+    rb_hash_aset(a, rb_tainted_str_new2("st_lock_wait"), INT2NUM(statp->st_lock_wait));
+#else
+    rb_hash_aset(a, rb_tainted_str_new2("st_nnowaits"), INT2NUM(statp->st_nnowaits));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nconflicts"), INT2NUM(statp->st_nconflicts));
+#endif
+    rb_hash_aset(a, rb_tainted_str_new2("st_ndeadlocks"), INT2NUM(statp->st_ndeadlocks));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nlocktimeouts"), INT2NUM(statp->st_nlocktimeouts));
+    rb_hash_aset(a, rb_tainted_str_new2("st_ntxntimeouts"), INT2NUM(statp->st_ntxntimeouts));
+    rb_hash_aset(a, rb_tainted_str_new2("st_regsize"), INT2NUM(statp->st_regsize));
+    rb_hash_aset(a, rb_tainted_str_new2("st_region_wait"), INT2NUM(statp->st_region_wait));
+    rb_hash_aset(a, rb_tainted_str_new2("st_region_nowait"), INT2NUM(statp->st_region_nowait));
+#else
+    if (argc != 0) {
+	rb_raise(rb_eArgError, "invalid number of arguments (%d for 0)", argc);
+    }
+#if BDB_VERSION < 30300
+    bdb_test_error(lock_stat(envst->envp, &statp, 0));
+#else
+    bdb_test_error(lock_stat(envst->envp, &statp));
+#endif
+    a = rb_hash_new();
+    rb_hash_aset(a, rb_tainted_str_new2("st_lastid"), INT2NUM(statp->st_lastid));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nmodes"), INT2NUM(statp->st_nmodes));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxlocks"), INT2NUM(statp->st_maxlocks));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nlockers"), INT2NUM(statp->st_nlockers));
+    rb_hash_aset(a, rb_tainted_str_new2("st_maxnlockers"), INT2NUM(statp->st_maxnlockers));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nconflicts"), INT2NUM(statp->st_nconflicts));
+    rb_hash_aset(a, rb_tainted_str_new2("st_nrequests"), INT2NUM(statp->st_nrequests));
+    rb_hash_aset(a, rb_tainted_str_new2("st_ndeadlocks"), INT2NUM(statp->st_ndeadlocks));
+    rb_hash_aset(a, rb_tainted_str_new2("st_regsize"), INT2NUM(statp->st_regsize));
+    rb_hash_aset(a, rb_tainted_str_new2("st_region_wait"), INT2NUM(statp->st_region_wait));
+    rb_hash_aset(a, rb_tainted_str_new2("st_region_nowait"), INT2NUM(statp->st_region_nowait));
+#endif
+#endif
+    free(statp);
+    return a;
+}
+
+#if BDB_VERSION < 30000
+#define GetLockid(obj, lockid, envst)		\
+{						\
+    Data_Get_Struct(obj, bdb_LOCKID, lockid);	\
+    GetEnvDB(lockid->env, envst);		\
+    if (envst->envp->lk_info == 0) {	\
+        rb_raise(bdb_eLock, "closed lockid");	\
+    }						\
+}
+#else
+#define GetLockid(obj, lockid, envst)		\
+{						\
+    Data_Get_Struct(obj, bdb_LOCKID, lockid);	\
+    GetEnvDB(lockid->env, envst);		\
+}
+#endif
+
+static void
+lock_mark(bdb_LOCK *lock)
+{
+    rb_gc_mark(lock->env);
+}
+
+static void
+lock_free(bdb_LOCK *lock)
+{
+#if BDB_VERSION < 30000
+    bdb_ENV *envst;
+
+    Data_Get_Struct(lock->env, bdb_ENV, envst);			\
+    if (envst->envp && envst->envp->lk_info) {
+	lock_close(envst->envp->lk_info);
+	envst->envp = NULL;
+    }
+#endif
+    free(lock);
+}
+
+static VALUE
+bdb_lockid_get(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_LOCKID *lockid;
+    bdb_ENV *envst;
+    DB_LOCK lock;
+    bdb_LOCK *lockst;
+    DBT objet;
+    unsigned int flags;
+    int lock_mode;
+    VALUE a, b, c, res;
+
+    rb_secure(2);
+    flags = 0;
+    if (rb_scan_args(argc, argv, "21", &a, &b, &c) == 3) {
+	if (c == Qtrue) {
+	    flags = DB_LOCK_NOWAIT;
+	}
+	else {
+	    flags = NUM2UINT(c);
+	}
+    }
+    SafeStringValue(a);
+    MEMZERO(&objet, DBT, 1);
+    objet.data = StringValuePtr(a);
+    objet.size = RSTRING(a)->len;
+    lock_mode = NUM2INT(b);
+    GetLockid(obj, lockid, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lk_info) {
+	rb_raise(bdb_eLock, "lock region not open");
+    }
+    bdb_test_error(lock_get(envst->envp->lk_info, lockid->lock, flags,
+			    &objet, lock_mode, &lock));
+#else
+#if BDB_VERSION >= 40000
+
+    bdb_test_error(envst->envp->lock_get(envst->envp, lockid->lock,
+					 flags, &objet, lock_mode, &lock));
+#else
+    bdb_test_error(lock_get(envst->envp, lockid->lock, flags,
+			    &objet, lock_mode, &lock));
+#endif
+#endif
+    res = Data_Make_Struct(bdb_cLock, bdb_LOCK, lock_mark, lock_free, lockst);
+#if BDB_VERSION < 30000
+    lockst->lock = lock;
+#else
+    lockst->lock = ALLOC(DB_LOCK);
+    MEMCPY(lockst->lock, &lock, DB_LOCK, 1);
+#endif
+    lockst->env = lockid->env;
+    return res;
+}
+
+
+#if BDB_VERSION < 30000
+#define GetLock(obj, lock, envst)		\
+{						\
+    Data_Get_Struct(obj, bdb_LOCK, lock);	\
+    GetEnvDB(lock->env, envst);			\
+    if (envst->envp->lk_info == 0)		\
+        rb_raise(bdb_eLock, "closed lock");	\
+}
+#else
+#define GetLock(obj, lock, envst)		\
+{						\
+    Data_Get_Struct(obj, bdb_LOCK, lock);	\
+    GetEnvDB(lock->env, envst);			\
+}
+#endif
+
+struct lockreq {
+    DB_LOCKREQ *list;
+};
+
+static VALUE
+bdb_lockid_each(VALUE obj, VALUE listobj)
+{
+    VALUE key, value;
+    DB_LOCKREQ *list;
+    bdb_ENV *envst;
+    struct lockreq *listst;
+    char *options;
+
+    Data_Get_Struct(listobj, struct lockreq, listst);
+    list = listst->list;
+    key = rb_ary_entry(obj, 0);
+    value = rb_ary_entry(obj, 1);
+    key = rb_obj_as_string(key);
+    options = StringValuePtr(key);
+    if (strcmp(options, "op") == 0) {
+	list->op = NUM2INT(value);
+    }
+    else if (strcmp(options, "obj") == 0) {
+	Check_Type(value, T_STRING);
+	list->obj = ALLOC(DBT);
+	MEMZERO(list->obj, DBT, 1);
+	list->obj->data = StringValuePtr(value);
+	list->obj->size = RSTRING(value)->len;
+    }
+    else if (strcmp(options, "mode") == 0) {
+	list->mode = NUM2INT(value);
+    }
+    else if (strcmp(options, "lock") == 0) {
+	bdb_LOCK *lockst;
+
+	if (!rb_obj_is_kind_of(value, bdb_cLock)) {
+	    rb_raise(bdb_eFatal, "BDB::Lock expected");
+	}
+	GetLock(value, lockst, envst);
+#if BDB_VERSION < 30000
+	list->lock = lockst->lock;
+#else
+	MEMCPY(&list->lock, lockst->lock, DB_LOCK, 1);
+#endif
+    }
+#if BDB_VERSION >= 40000
+    else if (strcmp(options, "timeout") == 0) {
+	list->timeout = rb_Integer(value);
+    }
+#endif
+    return Qnil;
+}
+
+static VALUE
+bdb_lockid_vec(int argc, VALUE *argv, VALUE obj)
+{
+    DB_LOCKREQ *list;
+    bdb_LOCKID *lockid;
+    bdb_LOCK *lockst;
+    bdb_ENV *envst;
+    unsigned int flags;
+    VALUE a, b, c, res;
+    int i, n, err;
+    VALUE listobj;
+    struct lockreq *listst;
+
+    flags = 0;
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+	if (b == Qtrue) {
+	    flags = DB_LOCK_NOWAIT;
+	}
+	else {
+	    flags = NUM2UINT(b);
+	}
+    }
+    Check_Type(a, T_ARRAY);
+    list = ALLOCA_N(DB_LOCKREQ, RARRAY(a)->len);
+    MEMZERO(list, DB_LOCKREQ, RARRAY(a)->len);
+    listobj = Data_Make_Struct(obj, struct lockreq, 0, free, listst);
+    for (i = 0; i < RARRAY(a)->len; i++) {
+	b = RARRAY(a)->ptr[i];
+	Check_Type(b, T_HASH);
+	listst->list = &list[i];
+	rb_iterate(rb_each, b, bdb_lockid_each, listobj);
+    }
+    GetLockid(obj, lockid, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lk_info) {
+	rb_raise(bdb_eLock, "lock region not open");
+    }
+    err = lock_vec(envst->envp->lk_info, lockid->lock, flags,
+		   list, RARRAY(a)->len, NULL);
+#else
+#if BDB_VERSION >= 40000
+    err = envst->envp->lock_vec(envst->envp, lockid->lock, flags,
+				list, RARRAY(a)->len, NULL);
+#else    
+    err = lock_vec(envst->envp, lockid->lock, flags,
+		   list, RARRAY(a)->len, NULL);
+#endif
+#endif
+    if (err != 0) {
+	for (i = 0; i < RARRAY(a)->len; i++) {
+	    if (list[i].obj)
+		free(list[i].obj);
+	}
+	res = (err == DB_LOCK_DEADLOCK)?bdb_eLock:bdb_eFatal;
+        if (bdb_errcall) {
+            bdb_errcall = 0;
+            rb_raise(res, "%s -- %s", StringValuePtr(bdb_errstr), db_strerror(err));
+        }
+        else
+            rb_raise(res, "%s", db_strerror(err));
+    }			
+    res = rb_ary_new2(RARRAY(a)->len);
+    n = 0;
+    for (i = 0; i < RARRAY(a)->len; i++) {
+	if (list[i].op == DB_LOCK_GET) {
+	    c = Data_Make_Struct(bdb_cLock, bdb_LOCK, lock_mark, lock_free, lockst);
+#if BDB_VERSION < 30000
+	    lockst->lock = list[i].lock;
+#else
+	    lockst->lock = ALLOC(DB_LOCK);
+	    MEMCPY(lockst->lock, &list[i].lock, DB_LOCK, 1);
+#endif
+	    lockst->env = lockid->env;
+	    rb_ary_push(res, c);
+	    n++;
+	}
+	else {
+	    rb_ary_push(res, Qnil);
+	}
+    }
+    return res;
+}
+
+static VALUE
+bdb_lock_put(VALUE obj)
+{
+    bdb_LOCK *lockst;
+    bdb_ENV *envst;
+
+    GetLock(obj, lockst, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lk_info) {
+	rb_raise(bdb_eLock, "lock region not open");
+    }
+    bdb_test_error(lock_put(envst->envp->lk_info, lockst->lock));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->lock_put(envst->envp, lockst->lock));
+#else
+    bdb_test_error(lock_put(envst->envp, lockst->lock));
+#endif
+#endif
+    return Qnil;
+} 
+
+void bdb_init_lock()
+{
+    rb_define_method(bdb_cEnv, "lock_id", bdb_env_lockid, 0);
+    rb_define_method(bdb_cEnv, "lock", bdb_env_lockid, 0);
+    rb_define_method(bdb_cEnv, "lock_stat", bdb_env_lockstat, -1);
+    rb_define_method(bdb_cEnv, "lock_detect", bdb_env_lockdetect, -1);
+    bdb_cLockid = rb_define_class_under(bdb_mDb, "Lockid", rb_cObject);
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+    rb_undef_alloc_func(bdb_cLockid);
+#else
+    rb_undef_method(CLASS_OF(bdb_cLockid), "allocate");
+#endif
+    rb_undef_method(CLASS_OF(bdb_cLockid), "new");
+    rb_define_method(bdb_cLockid, "lock_get", bdb_lockid_get, -1);
+    rb_define_method(bdb_cLockid, "get", bdb_lockid_get, -1);
+    rb_define_method(bdb_cLockid, "lock_vec", bdb_lockid_vec, -1);
+    rb_define_method(bdb_cLockid, "vec", bdb_lockid_vec, -1);
+    rb_define_method(bdb_cLockid, "close", bdb_env_lockid_close, 0);
+    bdb_cLock = rb_define_class_under(bdb_mDb, "Lock", rb_cObject);
+    rb_undef_method(CLASS_OF(bdb_cLock), "allocate");
+    rb_undef_method(CLASS_OF(bdb_cLock), "new");
+    rb_define_method(bdb_cLock, "put", bdb_lock_put, 0);
+    rb_define_method(bdb_cLock, "lock_put", bdb_lock_put, 0);
+    rb_define_method(bdb_cLock, "release", bdb_lock_put, 0);
+    rb_define_method(bdb_cLock, "delete", bdb_lock_put, 0);
+}

Added: packages/libdb4.3-ruby/branches/upstream/current/src/log.c
===================================================================
--- packages/libdb4.3-ruby/branches/upstream/current/src/log.c	                        (rev 0)
+++ packages/libdb4.3-ruby/branches/upstream/current/src/log.c	2007-12-08 12:00:45 UTC (rev 2142)
@@ -0,0 +1,745 @@
+#include "bdb.h"
+
+static void
+mark_lsn(struct dblsnst *lsnst)
+{
+    rb_gc_mark(lsnst->env);
+}
+
+static void
+free_lsn(struct dblsnst *lsnst)
+{
+    if (BDB_VALID(lsnst->env, T_DATA)) {
+	bdb_clean_env(lsnst->env, lsnst->self);
+    }
+#if BDB_VERSION >= 40000
+    if (lsnst->cursor && BDB_VALID(lsnst->env, T_DATA)) {
+	bdb_ENV *envst;
+
+	Data_Get_Struct(lsnst->env, bdb_ENV, envst);
+	if (envst->envp) {
+	    lsnst->cursor->close(lsnst->cursor, 0);
+	}
+	lsnst->cursor = 0;
+    }
+#endif
+    if (lsnst->lsn) free(lsnst->lsn);
+    free(lsnst);
+}
+
+VALUE
+bdb_makelsn(VALUE env)
+{
+    bdb_ENV *envst;
+    struct dblsnst *lsnst;
+    VALUE res;
+
+    GetEnvDB(env, envst);
+    res = Data_Make_Struct(bdb_cLsn, struct dblsnst, mark_lsn, free_lsn, lsnst);
+    lsnst->env = env;
+    lsnst->lsn = ALLOC(DB_LSN);
+    lsnst->self = res;
+    return res;
+}
+
+static VALUE
+bdb_s_log_put_internal(VALUE obj, VALUE a, int flag)
+{
+    bdb_ENV *envst;
+    VALUE ret;
+    DBT data;
+    struct dblsnst *lsnst;
+
+    GetEnvDB(obj, envst);
+    if (TYPE(a) != T_STRING) a = rb_str_to_str(a);
+    ret = bdb_makelsn(obj);
+    Data_Get_Struct(ret, struct dblsnst, lsnst);
+    data.data = StringValuePtr(a);
+    data.size = RSTRING(a)->len;
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    bdb_test_error(log_put(envst->envp->lg_info, lsnst->lsn, &data, flag));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->log_put(envst->envp, lsnst->lsn, &data, flag));
+#else
+    bdb_test_error(log_put(envst->envp, lsnst->lsn, &data, flag));
+#endif
+#endif
+    return ret;
+}
+
+static VALUE
+bdb_s_log_put(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE a, b;
+    int flag = 0;
+    
+    if (argc == 0 || argc > 2) {
+	rb_raise(bdb_eFatal, "Invalid number of arguments");
+    }
+#ifdef DB_CHECKPOINT
+    flag = DB_CHECKPOINT;
+#endif
+    if (rb_scan_args(argc, argv, "11", &a, &b) == 2) {
+	flag = NUM2INT(b);
+    }
+    return bdb_s_log_put_internal(obj, a, flag);
+}
+
+static VALUE
+bdb_s_log_checkpoint(VALUE obj, VALUE a)
+{
+#ifdef DB_CHECKPOINT
+    return bdb_s_log_put_internal(obj, a, DB_CHECKPOINT);
+#else
+    rb_warning("BDB::CHECKPOINT is obsolete");
+    return bdb_s_log_put_internal(obj, a, 0);
+#endif
+}
+
+static VALUE
+bdb_s_log_flush(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_ENV *envst;
+
+    if (argc == 0) {
+	GetEnvDB(obj, envst);
+#if BDB_VERSION < 30000
+	if (!envst->envp->lg_info) {
+	    rb_raise(bdb_eFatal, "log region not open");
+	}
+	bdb_test_error(log_flush(envst->envp->lg_info, NULL));
+#else
+#if BDB_VERSION >= 40000
+	bdb_test_error(envst->envp->log_flush(envst->envp, NULL));
+#else
+	bdb_test_error(log_flush(envst->envp, NULL));
+#endif
+#endif
+	return obj;
+    }
+    else if (argc == 1) {
+	return bdb_s_log_put_internal(obj, argv[0], DB_FLUSH);
+    }
+    else {
+	rb_raise(bdb_eFatal, "Invalid number of arguments");
+    }
+}
+
+static VALUE
+bdb_s_log_curlsn(VALUE obj, VALUE a)
+{
+#ifdef DB_CURSLN
+    return bdb_s_log_put_internal(obj, a, DB_CURLSN);
+#else
+    rb_warning("BDB::CURLSN is obsolete");
+    return bdb_s_log_put_internal(obj, a, 0);
+#endif
+}
+  
+
+static VALUE
+bdb_env_log_stat(int argc, VALUE *argv, VALUE obj)
+{
+    DB_LOG_STAT *bdb_stat;
+    bdb_ENV *envst;
+    VALUE res, b;
+    int flags;
+
+    GetEnvDB(obj, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    if (argc != 0) {
+	rb_raise(rb_eArgError, "invalid number of arguments (%d for 0)", argc);
+    }
+    bdb_test_error(log_stat(envst->envp->lg_info, &bdb_stat, 0));
+#else
+#if BDB_VERSION >= 40000
+    flags = 0;
+    if (rb_scan_args(argc, argv, "01", &b) == 1) {
+	flags = NUM2INT(b);
+    }
+    bdb_test_error(envst->envp->log_stat(envst->envp, &bdb_stat, flags));
+#else
+    if (argc != 0) {
+	rb_raise(rb_eArgError, "invalid number of arguments (%d for 0)", argc);
+    }
+#if BDB_VERSION < 30300
+    bdb_test_error(log_stat(envst->envp, &bdb_stat, 0));
+#else
+    bdb_test_error(log_stat(envst->envp, &bdb_stat));
+#endif
+#endif
+#endif
+    res = rb_hash_new();
+    rb_hash_aset(res, rb_tainted_str_new2("st_magic"), INT2NUM(bdb_stat->st_magic));
+    rb_hash_aset(res, rb_tainted_str_new2("st_version"), INT2NUM(bdb_stat->st_version));
+    rb_hash_aset(res, rb_tainted_str_new2("st_regsize"), INT2NUM(bdb_stat->st_regsize));
+    rb_hash_aset(res, rb_tainted_str_new2("st_mode"), INT2NUM(bdb_stat->st_mode));
+#if BDB_VERSION < 30000
+    rb_hash_aset(res, rb_tainted_str_new2("st_refcnt"), INT2NUM(bdb_stat->st_refcnt));
+#else
+    rb_hash_aset(res, rb_tainted_str_new2("st_lg_bsize"), INT2NUM(bdb_stat->st_lg_bsize));
+#endif
+#if BDB_VERSION >= 40100
+    rb_hash_aset(res, rb_tainted_str_new2("st_lg_size"), INT2NUM(bdb_stat->st_lg_size));
+    rb_hash_aset(res, rb_tainted_str_new2("st_lg_max"), INT2NUM(bdb_stat->st_lg_size));
+#else
+    rb_hash_aset(res, rb_tainted_str_new2("st_lg_max"), INT2NUM(bdb_stat->st_lg_max));
+#endif
+    rb_hash_aset(res, rb_tainted_str_new2("st_w_mbytes"), INT2NUM(bdb_stat->st_w_mbytes));
+    rb_hash_aset(res, rb_tainted_str_new2("st_w_bytes"), INT2NUM(bdb_stat->st_w_bytes));
+    rb_hash_aset(res, rb_tainted_str_new2("st_wc_mbytes"), INT2NUM(bdb_stat->st_wc_mbytes));
+    rb_hash_aset(res, rb_tainted_str_new2("st_wc_bytes"), INT2NUM(bdb_stat->st_wc_bytes));
+    rb_hash_aset(res, rb_tainted_str_new2("st_wcount"), INT2NUM(bdb_stat->st_wcount));
+#if BDB_VERSION >= 30000
+    rb_hash_aset(res, rb_tainted_str_new2("st_wcount_fill"), INT2NUM(bdb_stat->st_wcount_fill));
+#endif
+    rb_hash_aset(res, rb_tainted_str_new2("st_scount"), INT2NUM(bdb_stat->st_scount));
+    rb_hash_aset(res, rb_tainted_str_new2("st_cur_file"), INT2NUM(bdb_stat->st_cur_file));
+    rb_hash_aset(res, rb_tainted_str_new2("st_cur_offset"), INT2NUM(bdb_stat->st_cur_offset));
+    rb_hash_aset(res, rb_tainted_str_new2("st_region_wait"), INT2NUM(bdb_stat->st_region_wait));
+    rb_hash_aset(res, rb_tainted_str_new2("st_region_nowait"), INT2NUM(bdb_stat->st_region_nowait));
+#if BDB_VERSION >= 40000
+    rb_hash_aset(res, rb_tainted_str_new2("st_disk_file"), INT2NUM(bdb_stat->st_disk_file));
+    rb_hash_aset(res, rb_tainted_str_new2("st_disk_offset"), INT2NUM(bdb_stat->st_disk_offset));
+#if BDB_VERSION < 40100
+    rb_hash_aset(res, rb_tainted_str_new2("st_flushcommit"), INT2NUM(bdb_stat->st_flushcommit));
+#endif
+    rb_hash_aset(res, rb_tainted_str_new2("st_maxcommitperflush"), INT2NUM(bdb_stat->st_maxcommitperflush));
+    rb_hash_aset(res, rb_tainted_str_new2("st_mincommitperflush"), INT2NUM(bdb_stat->st_mincommitperflush));
+#endif
+    free(bdb_stat);
+    return res;
+}
+
+#if BDB_VERSION < 40000
+
+static VALUE
+bdb_env_log_get(VALUE obj, VALUE a)
+{
+    bdb_ENV *envst;
+    DBT data;
+    struct dblsnst *lsnst;
+    VALUE res, lsn;
+    int ret, flag;
+
+    GetEnvDB(obj, envst);
+    flag = NUM2INT(a);
+    MEMZERO(&data, DBT, 1);
+    data.flags |= DB_DBT_MALLOC;
+    lsn = bdb_makelsn(obj);
+    Data_Get_Struct(lsn, struct dblsnst, lsnst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    ret = bdb_test_error(log_get(envst->envp->lg_info, lsnst->lsn, &data, flag));
+#else
+    ret = bdb_test_error(log_get(envst->envp, lsnst->lsn, &data, flag));
+#endif
+    if (ret == DB_NOTFOUND) {
+	return Qnil;
+    }
+    res = rb_tainted_str_new(data.data, data.size);
+    free(data.data);
+    return rb_assoc_new(res, lsn);
+}
+
+#endif
+
+#if BDB_VERSION >= 40000
+static VALUE bdb_log_cursor _((VALUE));
+#endif
+
+#define BDB_LOG_INIT 0
+#define BDB_LOG_SET 1
+#define BDB_LOG_NEXT 2
+
+static VALUE
+bdb_i_each_log_get(VALUE obj, int flag)
+{
+#if BDB_VERSION < 40000
+    bdb_ENV *envst;
+#endif
+    struct dblsnst *lsnst, *lsnst1;
+    DBT data;
+    VALUE lsn, res, lsn1;
+    int ret, init, flags;
+
+    init = BDB_LOG_INIT;
+#if BDB_VERSION < 40000
+    GetEnvDB(obj, envst);
+#else
+    lsn = obj;
+    Data_Get_Struct(obj, struct dblsnst, lsnst);
+    flag = lsnst->flags;
+    if (lsnst->cursor == 0) {
+	DB_LSN *lsn1;
+
+	init = BDB_LOG_SET;
+	lsn1 = lsnst->lsn;
+	lsn = bdb_makelsn(lsnst->env);
+	Data_Get_Struct(lsn, struct dblsnst, lsnst);
+	MEMCPY(lsnst->lsn, lsn1, DB_LSN, 1);
+	bdb_log_cursor(lsn);
+    }
+#endif
+
+    do {
+#if BDB_VERSION < 40000
+	lsn = bdb_makelsn(obj);
+	Data_Get_Struct(lsn, struct dblsnst, lsnst);
+#endif
+	MEMZERO(&data, DBT, 1);
+	data.flags |= DB_DBT_MALLOC;
+	switch (init) {
+	case BDB_LOG_INIT:
+	    flags = (flag == DB_NEXT)?DB_FIRST:DB_LAST;
+	    break;
+	case BDB_LOG_SET:
+	    flags = DB_SET;
+	    break;
+	default:
+	    flags = flag;
+	    break;
+	}
+	init = BDB_LOG_NEXT;
+#if BDB_VERSION < 30000
+	if (!envst->envp->lg_info) {
+	    rb_raise(bdb_eFatal, "log region not open");
+	}
+	ret = bdb_test_error(log_get(envst->envp->lg_info, lsnst->lsn, &data, flags));
+#else
+#if BDB_VERSION >= 40000
+	ret = bdb_test_error(lsnst->cursor->get(lsnst->cursor, lsnst->lsn, &data, flags));
+	lsn1 = bdb_makelsn(lsnst->env);
+	Data_Get_Struct(lsn1, struct dblsnst, lsnst1);
+	MEMCPY(lsnst1->lsn, lsnst->lsn, DB_LSN, 1);
+#else
+	ret = bdb_test_error(log_get(envst->envp, lsnst->lsn, &data, flags));
+	lsn1 = lsn;
+#endif
+#endif
+	if (ret == DB_NOTFOUND) {
+	    return Qnil;
+	}
+	res = rb_tainted_str_new(data.data, data.size);
+	free(data.data);
+	rb_yield(rb_assoc_new(res, lsn));
+    } while (1);
+    return Qnil;
+}
+
+#if BDB_VERSION < 40000
+ 
+static VALUE
+bdb_env_log_each(VALUE obj) 
+{ 
+    return bdb_i_each_log_get(obj, DB_NEXT);
+}
+
+static VALUE
+bdb_env_log_hcae(VALUE obj)
+{ 
+    return bdb_i_each_log_get(obj, DB_PREV);
+}
+
+#else
+
+static VALUE
+log_cursor_close(VALUE obj)
+{
+    struct dblsnst *lsnst;
+
+    Data_Get_Struct(obj, struct dblsnst, lsnst);
+    if (lsnst->cursor) {
+	bdb_test_error(lsnst->cursor->close(lsnst->cursor, 0));
+	lsnst->cursor = 0;
+    }
+    return Qnil;
+}
+
+static VALUE
+bdb_log_cursor_close(VALUE obj)
+{
+    struct dblsnst *lsnst;
+
+    Data_Get_Struct(obj, struct dblsnst, lsnst);
+    bdb_clean_env(lsnst->env, obj);
+    
+    return log_cursor_close(obj);
+}
+
+static VALUE
+bdb_log_cursor(VALUE lsn)
+{
+    bdb_ENV *envst;
+    struct dblsnst *lsnst;
+
+    Data_Get_Struct(lsn, struct dblsnst, lsnst);
+    if (!lsnst->cursor) {
+	GetEnvDB(lsnst->env, envst);
+	bdb_test_error(envst->envp->log_cursor(envst->envp, &lsnst->cursor, 0));
+	bdb_ary_push(&envst->db_ary, lsn);
+    }
+    return lsn;
+}
+    
+static VALUE
+bdb_env_log_cursor(VALUE obj)
+{
+    return bdb_log_cursor(bdb_makelsn(obj));
+}
+
+static VALUE
+bdb_env_i_get(VALUE obj)
+{
+    bdb_ENV *envst;
+    struct dblsnst *lsnst;
+
+    log_cursor_close(obj);
+    Data_Get_Struct(obj, struct dblsnst, lsnst);
+    GetEnvDB(lsnst->env, envst);
+    bdb_test_error(envst->envp->log_cursor(envst->envp, &lsnst->cursor, 0));
+    return bdb_i_each_log_get(obj, lsnst->flags);
+}
+
+
+static VALUE
+bdb_env_log_each(VALUE obj)
+{
+    VALUE lsn;
+    struct dblsnst *lsnst;
+
+    lsn = bdb_makelsn(obj);
+    Data_Get_Struct(lsn, struct dblsnst, lsnst);
+    lsnst->flags = DB_NEXT;
+    return rb_ensure(bdb_env_i_get, lsn, bdb_log_cursor_close, lsn);
+}
+
+static VALUE
+bdb_env_log_hcae(VALUE obj)
+{
+    VALUE lsn;
+    struct dblsnst *lsnst;
+
+    lsn = bdb_makelsn(obj);
+    Data_Get_Struct(lsn, struct dblsnst, lsnst);
+    lsnst->flags = DB_PREV;
+    return rb_ensure(bdb_env_i_get, lsn, bdb_log_cursor_close, lsn);
+}
+
+static VALUE
+bdb_log_i_get(VALUE obj)
+{
+    struct dblsnst *lsnst;
+
+    log_cursor_close(obj);
+    Data_Get_Struct(obj, struct dblsnst, lsnst);
+    return bdb_i_each_log_get(obj, lsnst->flags);
+}
+
+static VALUE
+bdb_log_each(VALUE lsn)
+{
+    struct dblsnst *lsnst;
+
+    Data_Get_Struct(lsn, struct dblsnst, lsnst);
+    lsnst->flags = DB_NEXT;
+    return rb_ensure(bdb_log_i_get, lsn, bdb_log_cursor_close, lsn);
+}
+
+static VALUE
+bdb_log_hcae(VALUE lsn)
+{
+    struct dblsnst *lsnst;
+
+    Data_Get_Struct(lsn, struct dblsnst, lsnst);
+    lsnst->flags = DB_PREV;
+    return rb_ensure(bdb_log_i_get, lsn, bdb_log_cursor_close, lsn);
+ }
+ 
+
+#endif
+ 
+static VALUE
+bdb_env_log_archive(int argc, VALUE *argv, VALUE obj)
+{
+    char **list, **file;
+    bdb_ENV *envst;
+    int flag;
+    VALUE res;
+
+    GetEnvDB(obj, envst);
+    flag = 0;
+    list = NULL;
+    if (rb_scan_args(argc, argv, "01", &res)) {
+	flag = NUM2INT(res);
+    }
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    bdb_test_error(log_archive(envst->envp->lg_info, &list, flag, NULL));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->log_archive(envst->envp, &list, flag));
+#else
+#if BDB_VERSION < 30300
+    bdb_test_error(log_archive(envst->envp, &list, flag, NULL));
+#else
+    bdb_test_error(log_archive(envst->envp, &list, flag));
+#endif
+#endif
+#endif
+    res = rb_ary_new();
+    for (file = list; file != NULL && *file != NULL; file++) {
+	rb_ary_push(res, rb_tainted_str_new2(*file));
+    }
+    if (list != NULL) free(list);
+    return res;
+}
+
+#define GetLsn(obj, lsnst, envst)			\
+{							\
+    Data_Get_Struct(obj, struct dblsnst, lsnst);	\
+    GetEnvDB(lsnst->env, envst);			\
+}
+
+static VALUE
+bdb_lsn_env(VALUE obj)
+{
+    struct dblsnst *lsnst;
+    bdb_ENV *envst;
+    GetLsn(obj, lsnst, envst);
+    return lsnst->env;
+}
+
+static VALUE
+bdb_lsn_log_file(VALUE obj)
+{
+    struct dblsnst *lsnst;
+    bdb_ENV *envst;
+    char name[2048];
+
+    GetLsn(obj, lsnst, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    bdb_test_error(log_file(envst->envp->lg_info, lsnst->lsn, name, 2048));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->log_file(envst->envp, lsnst->lsn, name, 2048));
+#else
+    bdb_test_error(log_file(envst->envp, lsnst->lsn, name, 2048));
+#endif
+#endif
+    return rb_tainted_str_new2(name);
+}
+
+static VALUE
+bdb_lsn_log_flush(VALUE obj)
+{
+    struct dblsnst *lsnst;
+    bdb_ENV *envst;
+
+    GetLsn(obj, lsnst, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    bdb_test_error(log_flush(envst->envp->lg_info, lsnst->lsn));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->log_flush(envst->envp, lsnst->lsn));
+#else
+    bdb_test_error(log_flush(envst->envp, lsnst->lsn));
+#endif
+#endif
+    return obj;
+}
+
+static VALUE
+bdb_lsn_log_compare(VALUE obj, VALUE a)
+{
+    struct dblsnst *lsnst1, *lsnst2;
+    bdb_ENV *envst1, *envst2;
+
+    if (!rb_obj_is_kind_of(a, bdb_cLsn)) {
+	rb_raise(bdb_eFatal, "invalid argument for <=>");
+    }
+    GetLsn(obj, lsnst1, envst1);
+    GetLsn(a, lsnst2, envst2);
+    return INT2NUM(log_compare(lsnst1->lsn, lsnst2->lsn));
+}
+
+static VALUE
+bdb_lsn_log_get(int argc, VALUE *argv, VALUE obj)
+{
+    struct dblsnst *lsnst;
+    DBT data;
+    VALUE res, a;
+    int ret, flags;
+    bdb_ENV *envst;
+#if BDB_VERSION >= 40000
+    DB_LOGC *cursor;
+#endif
+
+    flags = DB_SET;
+    if (rb_scan_args(argc, argv, "01", &a) == 1) {
+	flags = NUM2INT(a);
+    }
+    GetLsn(obj, lsnst, envst);
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->log_cursor(envst->envp, &cursor, 0));
+#endif
+    MEMZERO(&data, DBT, 1);
+    data.flags |= DB_DBT_MALLOC;
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    ret = bdb_test_error(log_get(envst->envp->lg_info, lsnst->lsn, &data, flags));
+#else
+#if BDB_VERSION >= 40000
+    ret = cursor->get(cursor, lsnst->lsn, &data, flags);
+    cursor->close(cursor, 0);
+    ret = bdb_test_error(ret);
+#else
+    ret = bdb_test_error(log_get(envst->envp, lsnst->lsn, &data, flags));
+#endif
+#endif
+    if (ret == DB_NOTFOUND) {
+	return Qnil;
+    }
+    res = rb_tainted_str_new(data.data, data.size);
+    free(data.data);
+    return res;
+}
+
+static VALUE
+bdb_log_register(VALUE obj, VALUE a)
+{
+#if BDB_VERSION >= 40100
+    rb_warn("log_register is obsolete");
+    return Qnil;
+#else
+    bdb_DB *dbst;
+    bdb_ENV *envst;
+
+    if (TYPE(a) != T_STRING) {
+	rb_raise(bdb_eFatal, "Need a filename");
+    }
+    if (bdb_env_p(obj) == Qfalse) {
+	rb_raise(bdb_eFatal, "Database must be open in an Env");
+    }
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    Data_Get_Struct(dbst->env, bdb_ENV, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    bdb_test_error(log_register(envst->envp->lg_info, dbst->dbp, StringValuePtr(a), dbst->type, &envst->fidp));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->log_register(envst->envp, dbst->dbp, StringValuePtr(a)));
+#else
+#if BDB_VERSION <= 30105
+    bdb_test_error(log_register(envst->envp, dbst->dbp, StringValuePtr(a), &envst->fidp));
+#else
+    bdb_test_error(log_register(envst->envp, dbst->dbp, StringValuePtr(a)));
+#endif
+#endif
+#endif
+    return obj;
+#endif
+}
+
+static VALUE
+bdb_log_unregister(VALUE obj)
+{
+#if BDB_VERSION >= 40100
+    rb_warn("log_unregister is obsolete");
+    return Qnil;
+#else
+    bdb_DB *dbst;
+    bdb_ENV *envst;
+
+    if (bdb_env_p(obj) == Qfalse) {
+	rb_raise(bdb_eFatal, "Database must be open in an Env");
+    }
+    Data_Get_Struct(obj, bdb_DB, dbst);
+    Data_Get_Struct(dbst->env, bdb_ENV, envst);
+#if BDB_VERSION < 30000
+    if (!envst->envp->lg_info) {
+	rb_raise(bdb_eFatal, "log region not open");
+    }
+    bdb_test_error(log_unregister(envst->envp->lg_info, envst->fidp));
+#else
+#if BDB_VERSION >= 40000
+    bdb_test_error(envst->envp->log_unregister(envst->envp, dbst->dbp));
+#else
+#if BDB_VERSION <= 30105
+    bdb_test_error(log_unregister(envst->envp, envst->fidp));
+#else
+    bdb_test_error(log_unregister(envst->envp, dbst->dbp));
+#endif
+#endif
+#endif
+    return obj;
+#endif
+}
+
+void bdb_init_log()
+{
+    rb_define_method(bdb_cEnv, "log_put", bdb_s_log_put, -1);
+    rb_define_method(bdb_cEnv, "log_curlsn", bdb_s_log_curlsn, 0);
+    rb_define_method(bdb_cEnv, "log_checkpoint", bdb_s_log_checkpoint, 1);
+    rb_define_method(bdb_cEnv, "log_flush", bdb_s_log_flush, -1);
+    rb_define_method(bdb_cEnv, "log_stat", bdb_env_log_stat, -1);
+    rb_define_method(bdb_cEnv, "log_archive", bdb_env_log_archive, -1);
+#if BDB_VERSION < 40000 
+    rb_define_method(bdb_cEnv, "log_get", bdb_env_log_get, 1);
+#else
+    rb_define_method(bdb_cEnv, "log_cursor", bdb_env_log_cursor, 0);
+#endif
+    rb_define_method(bdb_cEnv, "log_each", bdb_env_log_each, 0);
+    rb_define_method(bdb_cEnv, "log_reverse_each", bdb_env_log_hcae, 0);
+    rb_define_method(bdb_cCommon, "log_register", bdb_log_register, 1);
+    rb_define_method(bdb_cCommon, "log_unregister", bdb_log_unregister, 0);
+    bdb_cLsn = rb_define_class_under(bdb_mDb, "Lsn", rb_cObject);
+    rb_include_module(bdb_cLsn, rb_mComparable);
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+    rb_undef_alloc_func(bdb_cLsn);
+#else
+    rb_undef_method(CLASS_OF(bdb_cLsn), "allocate");
+#endif
+    rb_undef_method(CLASS_OF(bdb_cLsn), "new");
+    rb_define_method(bdb_cLsn, "env", bdb_lsn_env, 0);
+#if BDB_VERSION >= 40000
+    rb_define_method(bdb_cLsn, "log_cursor", bdb_log_cursor, 0);
+    rb_define_method(bdb_cLsn, "cursor", bdb_log_cursor, 0);
+    rb_define_method(bdb_cLsn, "log_close", bdb_log_cursor_close, 0);
+    rb_define_method(bdb_cLsn, "close", bdb_log_cursor_close, 0);
+    rb_define_method(bdb_cLsn, "log_each", bdb_log_each, 0);
+    rb_define_method(bdb_cLsn, "each", bdb_log_each, 0);
+    rb_define_method(bdb_cLsn, "log_reverse_each", bdb_log_hcae, 0);
+    rb_define_method(bdb_cLsn, "reverse_each", bdb_log_hcae, 0);
+#endif
+     rb_define_method(bdb_cLsn, "log_get", bdb_lsn_log_get, -1);
+    rb_define_method(bdb_cLsn, "get", bdb_lsn_log_get, -1);
+    rb_define_method(bdb_cLsn, "log_compare", bdb_lsn_log_compare, 1);
+    rb_define_method(bdb_cLsn, "compare", bdb_lsn_log_compare, 1);
+    rb_define_method(bdb_cLsn, "<=>", bdb_lsn_log_compare, 1);
+    rb_define_method(bdb_cLsn, "log_file", bdb_lsn_log_file, 0);
+    rb_define_method(bdb_cLsn, "file", bdb_lsn_log_file, 0);
+    rb_define_method(bdb_cLsn, "log_flush", bdb_lsn_log_flush, 0);
+    rb_define_method(bdb_cLsn, "flush", bdb_lsn_log_flush, 0);
+}

Added: packages/libdb4.3-ruby/branches/upstream/current/src/recnum.c
===================================================================
--- packages/libdb4.3-ruby/branches/upstream/current/src/recnum.c	                        (rev 0)
+++ packages/libdb4.3-ruby/branches/upstream/current/src/recnum.c	2007-12-08 12:00:45 UTC (rev 2142)
@@ -0,0 +1,999 @@
+#include "bdb.h"
+
+static ID id_cmp;
+
+static VALUE
+bdb_recnum_init(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE *nargv;
+    VALUE array = rb_str_new2("array_base");
+    VALUE sarray = rb_str_new2("set_array_base");
+
+    if (!argc || TYPE(argv[argc - 1]) != T_HASH) {
+	nargv = ALLOCA_N(VALUE, argc + 1);
+	MEMCPY(nargv, argv, VALUE, argc);
+	nargv[argc] = rb_hash_new();
+	argv = nargv;
+	argc++;
+    }
+    rb_hash_aset(argv[argc - 1], array, INT2FIX(0));
+    if (rb_hash_aref(argv[argc - 1], sarray) != RHASH(argv[argc - 1])->ifnone) {
+	rb_hash_aset(argv[argc - 1], sarray, INT2FIX(0));
+    }
+    rb_hash_aset(argv[argc - 1], rb_str_new2("set_flags"), INT2FIX(DB_RENUMBER));
+    return bdb_init(argc, argv, obj);
+} 
+
+static VALUE
+bdb_sary_subseq(VALUE obj, long beg, long len)
+{
+    VALUE ary2, a;
+    bdb_DB *dbst;
+    long i;
+
+    GetDB(obj, dbst);
+    if (beg > dbst->len) return Qnil;
+    if (beg < 0 || len < 0) return Qnil;
+
+    if (beg + len > dbst->len) {
+	len = dbst->len - beg;
+    }
+    if (len <= 0) return rb_ary_new2(0);
+
+    ary2 = rb_ary_new2(len);
+    for (i = 0; i < len; i++) {
+	a = INT2NUM(i + beg);
+	rb_ary_push(ary2, bdb_get(1, &a, obj));
+    }
+    return ary2;
+}
+
+static VALUE
+bdb_sary_entry(VALUE obj, VALUE position)
+{
+    bdb_DB *dbst;
+    long offset;
+
+    GetDB(obj, dbst);
+    if (dbst->len == 0) return Qnil;
+    offset = NUM2LONG(position);
+    if (offset < 0) {
+	offset += dbst->len;
+    }
+    if (offset < 0 || dbst->len <= offset) return Qnil;
+    position = INT2NUM(offset);
+    return bdb_get(1, &position, obj);
+}
+
+static VALUE
+bdb_sary_aref(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE arg1, arg2;
+    long beg, len;
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) {
+	beg = NUM2LONG(arg1);
+	len = NUM2LONG(arg2);
+	if (beg < 0) {
+	    beg = dbst->len + beg;
+	}
+	return bdb_sary_subseq(obj, beg, len);
+    }
+
+    if (FIXNUM_P(arg1)) {
+	return bdb_sary_entry(obj, arg1);
+    }
+    else if (TYPE(arg1) == T_BIGNUM) {
+	rb_raise(rb_eIndexError, "index too big");
+    }
+    else {
+	switch (rb_range_beg_len(arg1, &beg, &len, dbst->len, 0)) {
+	  case Qfalse:
+	    break;
+	  case Qnil:
+	    return Qnil;
+	  default:
+	    return bdb_sary_subseq(obj, beg, len);
+	}
+    }
+    return bdb_sary_entry(obj, arg1);
+}
+
+static VALUE
+bdb_intern_shift_pop(VALUE obj, int depart, int len)
+{
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBC *dbcp;
+    DBT key, data;
+    int i, ret, flags;
+    db_recno_t recno;
+    VALUE res;
+
+    rb_secure(4);
+    INIT_TXN(txnid, obj, dbst);
+    MEMZERO(&key, DBT, 1);
+    INIT_RECNO(dbst, key, recno);
+    MEMZERO(&data, DBT, 1);
+    data.flags = DB_DBT_MALLOC;
+#if BDB_VERSION < 20600
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp));
+#else
+    bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbcp, 0));
+#endif
+    SET_PARTIAL(dbst, data);
+    flags = TEST_INIT_LOCK(dbst);
+    res = rb_ary_new2(len);
+    for (i = 0; i < len; i++) {
+	bdb_cache_error(dbcp->c_get(dbcp, &key, &data, depart | flags),
+			dbcp->c_close(dbcp), ret);
+	if (ret == DB_NOTFOUND) break;
+	rb_ary_push(res, bdb_test_load(obj, &data, FILTER_VALUE));
+	bdb_cache_error(dbcp->c_del(dbcp, 0), dbcp->c_close(dbcp), ret);
+	if (dbst->len > 0) dbst->len--;
+    }
+    dbcp->c_close(dbcp);
+    if (RARRAY(res)->len == 0) return Qnil;
+    else if (RARRAY(res)->len == 1) return RARRAY(res)->ptr[0];
+    else return res;
+}
+
+static void
+bdb_sary_replace(VALUE obj, VALUE beg, long len, long rpl)
+{
+    long i, j, rlen;
+    VALUE tmp[2];
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (len < 0) rb_raise(rb_eIndexError, "negative length %d", len);
+    if (beg < 0) {
+	beg += dbst->len;
+    }
+    if (beg < 0) {
+	beg -= dbst->len;
+	rb_raise(rb_eIndexError, "index %d out of array", beg);
+    }
+    if (beg + len > dbst->len) {
+	len = dbst->len - beg;
+    }
+
+    if (NIL_P(rpl)) {
+	rpl = rb_ary_new2(0);
+    }
+    else if (TYPE(rpl) != T_ARRAY) {
+	rpl = rb_ary_new3(1, rpl);
+    }
+    rlen = RARRAY(rpl)->len;
+
+    tmp[1] = Qnil;
+    if (beg >= dbst->len) {
+	for (i = dbst->len; i < beg; i++) {
+	    tmp[0] = INT2NUM(i);
+	    bdb_put(2, tmp, obj);
+	    dbst->len++;
+	}
+	for (i = beg, j = 0; j < RARRAY(rpl)->len; i++, j++) {
+	    tmp[0] = INT2NUM(i);
+	    tmp[1] = RARRAY(rpl)->ptr[j];
+	    bdb_put(2, tmp, obj);
+	    dbst->len++;
+	}
+    }
+    else {
+	if (len < rlen) {
+	    tmp[1] = Qnil;
+	    for (i = dbst->len - 1; i >= (beg + len); i--) {
+		tmp[0] = INT2NUM(i);
+		tmp[1] = bdb_get(1, tmp, obj);
+		tmp[0] = INT2NUM(i + rlen - len);
+		bdb_put(2, tmp, obj);
+	    }
+	    dbst->len += rlen - len;
+	}
+	for (i = beg, j = 0; j < rlen; i++, j++) {
+	    tmp[0] = INT2NUM(i);
+	    tmp[1] = RARRAY(rpl)->ptr[j];
+	    bdb_put(2, tmp, obj);
+	}
+	if (len > rlen) {
+	    for (i = beg + len; i < dbst->len; i++) {
+		tmp[0] = INT2NUM(i);
+		tmp[1] = bdb_get(1, tmp, obj);
+		tmp[0] = INT2NUM(i + rlen - len);
+		bdb_put(2, tmp, obj);
+	    }
+	    bdb_intern_shift_pop(obj, DB_LAST, len - rlen);
+	}
+    }
+}
+
+static VALUE
+bdb_sary_aset(int argc, VALUE *argv, VALUE obj)
+{
+    long  beg, len;
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (argc == 3) {
+	bdb_sary_replace(obj, NUM2LONG(argv[0]), NUM2LONG(argv[1]), argv[2]);
+	return argv[2];
+    }
+    if (argc != 2) {
+	rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc);
+    }
+    if (FIXNUM_P(argv[0])) {
+	beg = FIX2LONG(argv[0]);
+	goto fixnum;
+    }
+    else if (rb_range_beg_len(argv[0], &beg, &len, dbst->len, 1)) {
+	bdb_sary_replace(obj, beg, len, argv[1]);
+	return argv[1];
+    }
+    if (TYPE(argv[0]) == T_BIGNUM) {
+	rb_raise(rb_eIndexError, "index too big");
+    }
+
+    beg = NUM2LONG(argv[0]);
+  fixnum:
+    if (beg < 0) {
+	beg += dbst->len;
+	if (beg < 0) {
+	    rb_raise(rb_eIndexError, "index %d out of array",
+		     beg - dbst->len);
+	}
+    }
+    if (beg > dbst->len) {
+	VALUE nargv[2];
+	int i;
+
+	nargv[1] = Qnil;
+	for (i = dbst->len; i < beg; i++) {
+	    nargv[0] = INT2NUM(i);
+	    bdb_put(2, nargv, obj);
+	    dbst->len++;
+	}
+    }
+    argv[0] = INT2NUM(beg);
+    bdb_put(2, argv, obj);
+    dbst->len++;
+    return argv[1];
+}
+
+#if RUBY_VERSION_CODE >= 172
+
+static VALUE
+bdb_sary_insert(int argc, VALUE *argv, VALUE obj)
+{
+    long pos;
+
+    if (argc < 2) {
+	rb_raise(rb_eArgError, "wrong number of arguments(at least 2)");
+    }
+    pos = NUM2LONG(argv[0]);
+    if (pos == -1) {
+	bdb_DB *dbst;
+
+	GetDB(obj, dbst);
+ 	pos = dbst->len;
+    }
+    else if (pos < 0) {
+	pos++;
+    }
+
+    bdb_sary_replace(obj, pos, 0, rb_ary_new4(argc-1, argv+1));
+    return obj;
+}
+
+#endif
+
+static VALUE
+bdb_sary_at(VALUE obj, VALUE pos)
+{
+    return bdb_sary_entry(obj, pos);
+}
+
+static VALUE
+bdb_sary_first(VALUE obj)
+{
+    bdb_DB *dbst;
+    VALUE tmp;
+
+    GetDB(obj, dbst);
+    tmp = INT2NUM(0);
+    return bdb_get(1, &tmp, obj);
+}
+
+static VALUE
+bdb_sary_last(VALUE obj)
+{
+    bdb_DB *dbst;
+    VALUE tmp;
+
+    GetDB(obj, dbst);
+    if (!dbst->len) return Qnil;
+    tmp = INT2NUM(dbst->len);
+    return bdb_get(1, &tmp, obj);
+}
+
+static VALUE
+bdb_sary_fetch(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE pos, ifnone;
+    bdb_DB *dbst;
+    long idx;
+
+    GetDB(obj, dbst);
+    rb_scan_args(argc, argv, "11", &pos, &ifnone);
+    idx = NUM2LONG(pos);
+
+    if (idx < 0) {
+	idx +=  dbst->len;
+    }
+    if (idx < 0 || dbst->len <= idx) {
+	return ifnone;
+    }
+    pos = INT2NUM(idx);
+    return bdb_get(1, &pos, obj);
+}
+    
+
+static VALUE
+bdb_sary_concat(VALUE obj, VALUE y)
+{
+    bdb_DB *dbst;
+    long i;
+    VALUE tmp[2];
+
+    y = rb_convert_type(y, T_ARRAY, "Array", "to_ary");
+    GetDB(obj, dbst);
+    for (i = 0; i < RARRAY(y)->len; i++) {
+	tmp[0] = INT2NUM(dbst->len);
+	tmp[1] = RARRAY(y)->ptr[i];
+	bdb_put(2, tmp, obj);
+	dbst->len++;
+    }
+    return obj;
+}
+    
+static VALUE
+bdb_sary_push(VALUE obj, VALUE y)
+{
+    bdb_DB *dbst;
+    VALUE tmp[2];
+
+    GetDB(obj, dbst);
+    tmp[0] = INT2NUM(dbst->len);
+    tmp[1] = y;
+    bdb_put(2, tmp, obj);
+    dbst->len++;
+    return obj;
+}
+
+static VALUE
+bdb_sary_push_m(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    long i;
+    VALUE tmp[2];
+
+    if (argc == 0) {
+	rb_raise(rb_eArgError, "wrong # of arguments(at least 1)");
+    }
+    if (argc > 0) {
+	GetDB(obj, dbst);
+	for (i = 0; i < argc; i++) {
+	    tmp[0] = INT2NUM(dbst->len);
+	    tmp[1] = argv[i];
+	    bdb_put(2, tmp, obj);
+	    dbst->len++;
+	}
+    }
+    return obj;
+}
+
+static VALUE
+bdb_sary_s_create(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE res;
+
+    res = rb_funcall2(obj, rb_intern("new"), 0, 0);
+    if (argc < 0) {
+        rb_raise(rb_eArgError, "negative number of arguments");
+    }
+    if (argc > 0) {
+	bdb_sary_push_m(argc, argv, res);
+    }
+    return res;
+}
+    
+static VALUE
+bdb_sary_shift(VALUE obj)
+{
+    VALUE res;
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (dbst->len == 0) return Qnil;
+    res = bdb_intern_shift_pop(obj, DB_FIRST, 1);
+    return res;
+}
+
+static VALUE
+bdb_sary_pop(VALUE obj)
+{
+    VALUE res;
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (dbst->len == 0) return Qnil;
+    res = bdb_intern_shift_pop(obj, DB_LAST, 1);
+    return res;
+}
+
+static VALUE
+bdb_sary_unshift_m(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    VALUE tmp[2];
+    long i;
+
+    if (argc == 0) {
+	rb_raise(rb_eArgError, "wrong # of arguments(at least 1)");
+    }
+    if (argc > 0) {
+/* ++ */
+	GetDB(obj, dbst);
+	for (i = dbst->len - 1; i >= 0; i++) {
+	    tmp[0] = INT2NUM(i);
+	    tmp[1] = bdb_get(1, tmp, obj);
+	    tmp[0] = INT2NUM(i + argc);
+	    bdb_put(2, tmp, obj);
+	}
+	for (i = 0; i < argc; i++) {
+	    tmp[0] = INT2NUM(i);
+	    tmp[1] = argv[i];
+	    bdb_put(2, tmp, obj);
+	    dbst->len++;
+	}
+    }
+    return obj;
+}
+
+static VALUE
+bdb_sary_length(VALUE obj)
+{
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (dbst->len < 0) rb_raise(bdb_eFatal, "Invalid BDB::Recnum");
+    return INT2NUM(dbst->len);
+}
+
+static VALUE
+bdb_sary_empty_p(VALUE obj)
+{
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (dbst->len < 0) rb_raise(bdb_eFatal, "Invalid BDB::Recnum");
+    return (dbst->len)?Qfalse:Qtrue;
+}
+
+static VALUE
+bdb_sary_rindex(VALUE obj, VALUE a)
+{
+    return bdb_internal_value(obj, a, Qtrue, DB_PREV);
+}
+
+static VALUE
+bdb_sary_to_a(VALUE obj)
+{
+    return bdb_to_type(obj, rb_ary_new(), Qfalse);
+}
+
+static VALUE
+bdb_sary_reverse_m(VALUE obj)
+{
+    return bdb_to_type(obj, rb_ary_new(), Qnil);
+}
+
+static VALUE
+bdb_sary_reverse_bang(VALUE obj)
+{
+    long i, j;
+    bdb_DB *dbst;
+    VALUE tmp[2], interm;
+
+    GetDB(obj, dbst);
+    if (dbst->len <= 1) return obj;
+    i = 0;
+    j = dbst->len - 1;
+    while (i < j) {
+	tmp[0] = INT2NUM(i);
+	interm = bdb_get(1, tmp, obj);
+	tmp[0] = INT2NUM(j);
+	tmp[1] = bdb_get(1, tmp, obj);
+	tmp[0] = INT2NUM(i);
+	bdb_put(2, tmp, obj);
+	tmp[0] = INT2NUM(j);
+	tmp[1] = interm;
+	bdb_put(2, tmp, obj);
+	i++; j--;
+    }
+    return obj;
+}
+
+static VALUE
+bdb_sary_collect_bang(int argc, VALUE *argv, VALUE obj)
+{
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT, Qtrue, BDB_ST_VALUE);
+}
+
+static VALUE
+bdb_sary_collect(int argc, VALUE *argv, VALUE obj)
+{
+    if (!rb_block_given_p()) {
+	return bdb_sary_to_a(obj);
+    }
+    return bdb_each_kvc(argc, argv, obj, DB_NEXT, rb_ary_new(), BDB_ST_VALUE);
+}
+
+static VALUE
+bdb_sary_select(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE result;
+    long i;
+
+    if (rb_block_given_p()) {
+	if (argc > 0) {
+	    rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
+	}
+	return bdb_each_kvc(argc, argv, obj, DB_NEXT, rb_ary_new(), BDB_ST_SELECT);
+    }
+#if RUBY_VERSION_CODE >= 180
+	rb_warn("Recnum#select(index..) is deprecated; use Recnum#values_at");
+#endif
+    result = rb_ary_new();
+    for (i = 0; i < argc; i++) {
+	rb_ary_push(result, bdb_sary_fetch(1, argv + i, obj));
+    }
+    return result;
+}
+
+static VALUE
+bdb_sary_values_at(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE result;
+    long i;
+
+    result = rb_ary_new();
+    for (i = 0; i < argc; i++) {
+	rb_ary_push(result, bdb_sary_fetch(1, argv + i, obj));
+    }
+    return result;
+}
+
+static VALUE
+bdb_sary_indexes(int argc, VALUE *argv, VALUE obj)
+{
+#if RUBY_VERSION_CODE >= 172
+    rb_warn("Recnum#%s is deprecated; use Recnum#values_at",
+	    rb_id2name(rb_frame_last_func()));
+#endif
+    return bdb_sary_values_at(argc, argv, obj);
+}
+
+static VALUE
+bdb_sary_filter(int argc, VALUE *argv, VALUE obj)
+{
+    rb_warn("BDB::Recnum#filter is deprecated; use BDB::Recnum#collect!");
+    return bdb_sary_collect_bang(argc, argv, obj);
+}
+
+static VALUE
+bdb_sary_delete(VALUE obj, VALUE item)
+{
+    bdb_DB *dbst;
+    long i1, i2;
+    VALUE tmp, a;
+
+    GetDB(obj, dbst);
+    i2 = dbst->len;
+    for (i1 = 0; i1 < dbst->len;) {
+	tmp = INT2NUM(i1);
+	a = bdb_get(1, &tmp, obj);
+	if (rb_equal(a, item)) {
+	    bdb_del(obj, INT2NUM(i1));
+	    dbst->len--;
+	}
+	else {
+	    i1++;
+	}
+    }
+    if (dbst->len == i2) {
+	if (rb_block_given_p()) {
+	    return rb_yield(item);
+	}
+	return Qnil;
+    }
+    return item;
+}
+
+static VALUE
+bdb_sary_delete_at_m(VALUE obj, VALUE a)
+{
+    bdb_DB *dbst;
+    long pos;
+    VALUE tmp;
+    VALUE del = Qnil;
+
+    GetDB(obj, dbst);
+    pos = NUM2INT(a);
+    if (pos >= dbst->len) return Qnil;
+    if (pos < 0) pos += dbst->len;
+    if (pos < 0) return Qnil;
+
+    tmp = INT2NUM(pos);
+    del = bdb_get(1, &tmp, obj);
+    bdb_del(obj, tmp);
+    dbst->len--;
+    return del;
+}
+
+static VALUE
+bdb_sary_reject_bang(VALUE obj)
+{
+    bdb_DB *dbst;
+    long i1, i2;
+    VALUE tmp, a;
+
+    GetDB(obj, dbst);
+    i2 = dbst->len;
+    for (i1 = 0; i1 < dbst->len;) {
+	tmp = INT2NUM(i1);
+	a = bdb_get(1, &tmp, obj);
+	if (!RTEST(rb_yield(a))) {
+	    i1++;
+	    continue;
+	}
+	bdb_del(obj, tmp);
+	dbst->len--;
+    }
+    if (dbst->len == i2) return Qnil;
+    return obj;
+}
+
+static VALUE
+bdb_sary_delete_if(VALUE obj)
+{
+    bdb_sary_reject_bang(obj);
+    return obj;
+}
+
+static VALUE
+bdb_sary_replace_m(VALUE obj, VALUE obj2)
+{
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    obj2 = rb_convert_type(obj2, T_ARRAY, "Array", "to_ary");
+    bdb_sary_replace(obj, 0, dbst->len, obj2);
+    return obj;
+}
+
+static VALUE
+bdb_sary_clear(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_DB *dbst;
+    VALUE g;
+    int flags = 0;
+
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+	VALUE f = argv[argc - 1];
+	if ((g = rb_hash_aref(f, rb_intern("flags"))) != RHASH(f)->ifnone ||
+	    (g = rb_hash_aref(f, rb_str_new2("flags"))) != RHASH(f)->ifnone) {
+	    flags = NUM2INT(g);
+	}
+	argc--;
+    }
+    if (argc == 1) {
+	flags = NUM2INT(argv[0]);
+    }
+    g = INT2FIX(flags);
+    bdb_clear(1, &g, obj);
+    GetDB(obj, dbst);
+    dbst->len = 0;
+    return obj;
+}
+
+static VALUE
+bdb_sary_fill(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE item, arg1, arg2, tmp[2];
+    long beg, len, i;
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    rb_scan_args(argc, argv, "12", &item, &arg1, &arg2);
+    switch (argc) {
+      case 1:
+	  len = dbst->len;
+	  beg = 0;
+	  break;
+      case 2:
+	if (rb_range_beg_len(arg1, &beg, &len, dbst->len, 1)) {
+	    break;
+	}
+	/* fall through */
+      case 3:
+	beg = NIL_P(arg1)?0:NUM2LONG(arg1);
+	if (beg < 0) {
+	    beg += dbst->len;
+	    if (beg < 0) beg = 0;
+	}
+	len = NIL_P(arg2)?dbst->len - beg:NUM2LONG(arg2);
+	break;
+    }
+    tmp[1] = item;
+    for (i = 0; i < len; i++) {
+	tmp[0] = INT2NUM(i + beg);
+	bdb_put(2, tmp, obj);
+	if ((i + beg) >= dbst->len) dbst->len++;
+    }
+    return obj;
+}
+
+static VALUE
+bdb_sary_cmp(VALUE obj, VALUE obj2)
+{
+    bdb_DB *dbst, *dbst2 = 0;
+    VALUE a, a2, tmp, ary;
+    long i, len;
+
+    if (obj == obj2) return INT2FIX(0);
+    GetDB(obj, dbst);
+    len = dbst->len;
+    if (!rb_obj_is_kind_of(obj2, bdb_cRecnum)) {
+	obj2 = rb_convert_type(obj2, T_ARRAY, "Array", "to_ary");
+	if (len > RARRAY(obj2)->len) {
+	    len = RARRAY(obj2)->len;
+	}
+	ary = Qtrue;
+    }
+    else {
+	GetDB(obj2, dbst2);
+	len = dbst->len;
+	if (len > dbst2->len) {
+	    len = dbst2->len;
+	}
+	ary = Qfalse;
+    }
+    for (i = 0; i < len; i++) {
+	tmp = INT2NUM(i);
+	a = bdb_get(1, &tmp, obj);
+	if (ary) {
+	    a2 = RARRAY(obj2)->ptr[i];
+	}
+	else {
+	    a2 = bdb_get(1, &tmp, obj2);
+	}
+	tmp = rb_funcall(a, id_cmp, 1, a2);
+	if (tmp != INT2FIX(0)) {
+	    return tmp;
+	}
+    }
+    len = dbst->len - ary?RARRAY(obj2)->len:dbst2->len;
+    if (len == 0) return INT2FIX(0);
+    if (len > 0) return INT2FIX(1);
+    return INT2FIX(-1);
+}
+
+static VALUE
+bdb_sary_slice_bang(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE arg1, arg2;
+    long pos, len;
+    bdb_DB *dbst;
+
+    GetDB(obj, dbst);
+    if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) {
+	pos = NUM2LONG(arg1);
+	len = NUM2LONG(arg2);
+      delete_pos_len:
+	if (pos < 0) {
+	    pos = dbst->len + pos;
+	}
+	arg2 = bdb_sary_subseq(obj, pos, len);
+	bdb_sary_replace(obj, pos, len, Qnil);
+	return arg2;
+    }
+
+    if (!FIXNUM_P(arg1) && rb_range_beg_len(arg1, &pos, &len, dbst->len, 1)) {
+	goto delete_pos_len;
+    }
+
+    pos = NUM2LONG(arg1);
+    if (pos >= dbst->len) return Qnil;
+    if (pos < 0) pos += dbst->len;
+    if (pos < 0) return Qnil;
+
+    arg1 = INT2NUM(pos);
+    arg2 = bdb_sary_at(obj, arg1);
+    if (bdb_del(obj, arg1) != Qnil) dbst->len--;
+    return arg2;
+}
+
+static VALUE
+bdb_sary_plus(VALUE obj, VALUE y)
+{
+    return rb_ary_plus(bdb_sary_to_a(obj), y);
+}
+
+static VALUE
+bdb_sary_times(VALUE obj, VALUE y)
+{
+    return rb_funcall(bdb_sary_to_a(obj), rb_intern("*"), 1, y);
+}
+
+static VALUE
+bdb_sary_diff(VALUE obj, VALUE y)
+{
+    return rb_funcall(bdb_sary_to_a(obj), rb_intern("-"), 1, y);
+}
+
+static VALUE
+bdb_sary_and(VALUE obj, VALUE y)
+{
+    return rb_funcall(bdb_sary_to_a(obj), rb_intern("&"), 1, y);
+}
+
+static VALUE
+bdb_sary_or(VALUE obj, VALUE y)
+{
+    return rb_funcall(bdb_sary_to_a(obj), rb_intern("|"), 1, y);
+}
+
+static VALUE
+bdb_sary_compact(VALUE obj)
+{
+    return rb_funcall(bdb_sary_to_a(obj), rb_intern("compact!"), 0, 0);
+}
+
+static VALUE
+bdb_sary_compact_bang(VALUE obj)
+{
+    bdb_DB *dbst;
+    long i, j;
+    VALUE tmp;
+
+    GetDB(obj, dbst);
+    j = dbst->len;
+    for (i = 0; i < dbst->len; ) {
+	tmp = INT2NUM(i);
+	tmp = bdb_get(1, &tmp, obj);
+	if (NIL_P(tmp)) {
+	    bdb_del(obj, INT2NUM(i));
+	    dbst->len--;
+	}
+	else {
+	    i++;
+	}
+    }
+    if (dbst->len == j) return Qnil;
+    return obj;
+}
+
+static VALUE
+bdb_sary_nitems(VALUE obj)
+{
+    bdb_DB *dbst;
+    long i, j;
+    VALUE tmp;
+
+    GetDB(obj, dbst);
+    j = 0;
+    for (i = 0; i < dbst->len; ) {
+	tmp = INT2NUM(i);
+	tmp = bdb_get(1, &tmp, obj);
+	if (!NIL_P(tmp)) j++;
+    }
+    return INT2NUM(j);
+}
+
+void bdb_init_recnum()
+{
+    id_cmp = rb_intern("<=>");
+    bdb_cRecnum = rb_define_class_under(bdb_mDb, "Recnum", bdb_cCommon);
+    rb_define_singleton_method(bdb_cRecnum, "[]", bdb_sary_s_create, -1);
+    rb_define_private_method(bdb_cRecnum, "initialize", bdb_recnum_init, -1);
+    rb_define_method(bdb_cRecnum, "[]", bdb_sary_aref, -1);
+    rb_define_method(bdb_cRecnum, "get", bdb_sary_aref, -1);
+    rb_define_method(bdb_cRecnum, "db_get", bdb_sary_aref, -1);
+    rb_define_method(bdb_cRecnum, "[]=", bdb_sary_aset, -1);
+    rb_define_method(bdb_cRecnum, "put", bdb_sary_aset, -1);
+    rb_define_method(bdb_cRecnum, "db_put", bdb_sary_aset, -1);
+    rb_define_method(bdb_cRecnum, "store", bdb_sary_aset, -1);
+    rb_define_method(bdb_cRecnum, "at", bdb_sary_at, 1);
+    rb_define_method(bdb_cRecnum, "fetch", bdb_sary_fetch, -1);
+    rb_define_method(bdb_cRecnum, "first", bdb_sary_first, 0);
+    rb_define_method(bdb_cRecnum, "last", bdb_sary_last, 0);
+    rb_define_method(bdb_cRecnum, "concat", bdb_sary_concat, 1);
+    rb_define_method(bdb_cRecnum, "<<", bdb_sary_push, 1);
+    rb_define_method(bdb_cRecnum, "push", bdb_sary_push_m, -1);
+    rb_define_method(bdb_cRecnum, "pop", bdb_sary_pop, 0);
+    rb_define_method(bdb_cRecnum, "shift", bdb_sary_shift, 0);
+    rb_define_method(bdb_cRecnum, "unshift", bdb_sary_unshift_m, -1);
+#if RUBY_VERSION_CODE >= 172
+    rb_define_method(bdb_cRecnum, "insert", bdb_sary_insert, -1);
+#endif
+    rb_define_method(bdb_cRecnum, "each", bdb_each_value, -1);
+    rb_define_method(bdb_cRecnum, "each_index", bdb_each_key, -1);
+    rb_define_method(bdb_cRecnum, "reverse_each", bdb_each_eulav, -1);
+    rb_define_method(bdb_cRecnum, "length", bdb_sary_length, 0);
+    rb_define_alias(bdb_cRecnum,  "size", "length");
+    rb_define_method(bdb_cRecnum, "empty?", bdb_sary_empty_p, 0);
+    rb_define_method(bdb_cRecnum, "index", bdb_index, 1);
+    rb_define_method(bdb_cRecnum, "rindex", bdb_sary_rindex, 1);
+    rb_define_method(bdb_cRecnum, "indexes", bdb_sary_indexes, -1);
+    rb_define_method(bdb_cRecnum, "indices", bdb_sary_indexes, -1);
+    rb_define_method(bdb_cRecnum, "reverse", bdb_sary_reverse_m, 0);
+    rb_define_method(bdb_cRecnum, "reverse!", bdb_sary_reverse_bang, 0);
+    rb_define_method(bdb_cRecnum, "collect", bdb_sary_collect, -1);
+    rb_define_method(bdb_cRecnum, "collect!", bdb_sary_collect_bang, -1);
+#if RUBY_VERSION_CODE >= 172
+    rb_define_method(bdb_cRecnum, "map", bdb_sary_collect, 0);
+    rb_define_method(bdb_cRecnum, "select", bdb_sary_select, -1);
+    rb_define_method(bdb_cRecnum, "values_at", bdb_sary_values_at, -1);
+#endif
+    rb_define_method(bdb_cRecnum, "map!", bdb_sary_collect_bang, -1);
+    rb_define_method(bdb_cRecnum, "filter", bdb_sary_filter, -1);
+    rb_define_method(bdb_cRecnum, "delete", bdb_sary_delete, 1);
+    rb_define_method(bdb_cRecnum, "delete_at", bdb_sary_delete_at_m, 1);
+    rb_define_method(bdb_cRecnum, "delete_if", bdb_sary_delete_if, 0);
+    rb_define_method(bdb_cRecnum, "reject!", bdb_sary_reject_bang, 0);
+    rb_define_method(bdb_cRecnum, "replace", bdb_sary_replace_m, 1);
+    rb_define_method(bdb_cRecnum, "clear", bdb_sary_clear, -1);
+    rb_define_method(bdb_cRecnum, "fill", bdb_sary_fill, -1);
+    rb_define_method(bdb_cRecnum, "include?", bdb_has_value, 1);
+    rb_define_method(bdb_cRecnum, "<=>", bdb_sary_cmp, 1);
+    rb_define_method(bdb_cRecnum, "slice", bdb_sary_aref, -1);
+    rb_define_method(bdb_cRecnum, "slice!", bdb_sary_slice_bang, -1);
+/*
+    rb_define_method(bdb_cRecnum, "assoc", bdb_sary_assoc, 1);
+    rb_define_method(bdb_cRecnum, "rassoc", bdb_sary_rassoc, 1);
+*/
+    rb_define_method(bdb_cRecnum, "+", bdb_sary_plus, 1);
+    rb_define_method(bdb_cRecnum, "*", bdb_sary_times, 1);
+
+    rb_define_method(bdb_cRecnum, "-", bdb_sary_diff, 1);
+    rb_define_method(bdb_cRecnum, "&", bdb_sary_and, 1);
+    rb_define_method(bdb_cRecnum, "|", bdb_sary_or, 1);
+
+/*
+    rb_define_method(bdb_cRecnum, "uniq", bdb_sary_uniq, 0);
+    rb_define_method(bdb_cRecnum, "uniq!", bdb_sary_uniq_bang, 0);
+*/
+    rb_define_method(bdb_cRecnum, "compact", bdb_sary_compact, 0);
+    rb_define_method(bdb_cRecnum, "compact!", bdb_sary_compact_bang, 0);
+/*
+    rb_define_method(bdb_cRecnum, "flatten", bdb_sary_flatten, 0);
+    rb_define_method(bdb_cRecnum, "flatten!", bdb_sary_flatten_bang, 0);
+*/
+    rb_define_method(bdb_cRecnum, "nitems", bdb_sary_nitems, 0);
+    rb_define_method(bdb_cRecnum, "stat", bdb_tree_stat, -1);
+    rb_define_method(bdb_cRecnum, "to_a", bdb_sary_to_a, 0);
+    rb_define_method(bdb_cRecnum, "to_ary", bdb_sary_to_a, 0);
+    /* RECNO */
+    rb_define_method(bdb_cRecno, "shift", bdb_sary_shift, 0);
+    rb_define_method(bdb_cRecno, "to_a", bdb_sary_to_a, 0);
+    rb_define_method(bdb_cRecno, "to_ary", bdb_sary_to_a, 0);
+    rb_define_method(bdb_cRecno, "pop", bdb_sary_pop, 0);
+    /* QUEUE */
+#if BDB_VERSION >= 30000
+    rb_define_method(bdb_cQueue, "to_a", bdb_sary_to_a, 0);
+    rb_define_method(bdb_cQueue, "to_ary", bdb_sary_to_a, 0);
+#endif
+}

Added: packages/libdb4.3-ruby/branches/upstream/current/src/sequence.c
===================================================================
--- packages/libdb4.3-ruby/branches/upstream/current/src/sequence.c	                        (rev 0)
+++ packages/libdb4.3-ruby/branches/upstream/current/src/sequence.c	2007-12-08 12:00:45 UTC (rev 2142)
@@ -0,0 +1,332 @@
+#include "bdb.h"
+
+#if BDB_VERSION >= 40300
+
+static VALUE bdb_cSeq;
+
+static void
+bdb_seq_free(bdb_SEQ *seqst)
+{
+    if (seqst->seqp) {
+        seqst->seqp->close(seqst->seqp, 0);
+        seqst->seqp = NULL;
+    }
+    free(seqst);
+}
+
+static void
+bdb_seq_mark(bdb_SEQ *seqst)
+{
+    rb_gc_mark(seqst->db);
+    rb_gc_mark(seqst->txn);
+    rb_gc_mark(seqst->orig);
+}
+
+static VALUE
+bdb_seq_close(VALUE obj)
+{
+    bdb_SEQ *seqst;
+
+    GetSEQ(obj, seqst);
+    seqst->seqp->close(seqst->seqp, 0);
+    seqst->seqp = NULL;
+    return Qnil;
+}
+
+static VALUE
+bdb_seq_txn_dup(VALUE obj, VALUE a)
+{
+    bdb_SEQ *seq0, *seq1;
+    bdb_TXN *txnst;
+    VALUE res;
+
+    GetSEQ(obj, seq0);
+    GetTxnDB(a, txnst);
+    res = Data_Make_Struct(obj, bdb_SEQ, bdb_seq_mark, bdb_seq_free, seq1);
+    MEMCPY(seq1, seq0, bdb_SEQ, 1);
+    seq1->txn = a;
+    seq1->txnid = txnst->txnid;
+    seq1->orig = obj;
+    return res;
+}
+
+static VALUE
+bdb_seq_txn_close(VALUE obj, VALUE commit, VALUE real)
+{
+    bdb_SEQ *seqst;
+
+    if (!real) {
+        Data_Get_Struct(obj, bdb_SEQ, seqst);
+        seqst->seqp = NULL;
+    }
+    else {
+        bdb_seq_close(obj);
+    }
+    return Qnil;
+}
+
+static VALUE
+bdb_seq_i_options(VALUE obj, VALUE seqobj)
+{
+    VALUE key, value;
+    bdb_SEQ *seqst;
+    char *options;
+
+    key = rb_ary_entry(obj, 0);
+    value = rb_ary_entry(obj, 1);
+    key = rb_obj_as_string(key);
+    options = StringValuePtr(key);
+    Data_Get_Struct(seqobj, bdb_SEQ, seqst);
+    if (strcmp(options, "set_cachesize") == 0) {
+        if (seqst->seqp->set_cachesize(seqst->seqp, NUM2INT(value))) {
+            seqst->seqp->remove(seqst->seqp, 0, 0);
+            rb_raise(rb_eArgError, "Invalid value (%d) for set_cachesize",
+                     NUM2INT(value));
+        }
+    }
+    else if (strcmp(options, "set_flags") == 0) {
+        if (seqst->seqp->set_flags(seqst->seqp, NUM2INT(value))) {
+            seqst->seqp->remove(seqst->seqp, 0, 0);
+            rb_raise(rb_eArgError, "Invalid value (%d) for set_flags",
+                     NUM2INT(value));
+        }
+    }
+    else if (strcmp(options, "set_range") == 0) {
+        Check_Type(value, T_ARRAY);
+        if (RARRAY(value)->len != 2) { 
+            rb_raise(bdb_eFatal, "expected 2 values for range");
+        }
+        if (seqst->seqp->set_range(seqst->seqp, 
+                                   NUM2LONG(RARRAY(value)->ptr[0]),
+                                   NUM2LONG(RARRAY(value)->ptr[1]))) {
+            seqst->seqp->remove(seqst->seqp, 0, 0);
+            rb_raise(rb_eArgError, "Invalid value (%d, %d) for set_range",
+                     NUM2LONG(RARRAY(value)->ptr[0]),
+                     NUM2LONG(RARRAY(value)->ptr[1]));
+        }
+    }
+    else {
+        rb_warning("Unknown option %s", options);
+    }
+    return Qnil;
+}
+
+static VALUE
+bdb_seq_open(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE a, b, c, res;
+    int flags = 0, count;
+    bdb_DB *dbst;
+    DB_TXN *txnid;
+    DBT key;
+    db_seq_t value;
+    db_recno_t recno;
+    bdb_SEQ *seqst;
+    VALUE options = Qnil;
+
+    INIT_TXN(txnid, obj, dbst);
+    res = Data_Make_Struct(bdb_cSeq, bdb_SEQ, bdb_seq_mark, bdb_seq_free, seqst);
+    seqst->db = obj;
+    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
+        options = argv[argc - 1];
+        argc--;
+    }
+    count = rb_scan_args(argc, argv, "12", &a, &b, &c);
+    bdb_test_error(db_sequence_create(&seqst->seqp, dbst->dbp, 0));
+    switch (count) {
+    case 3:
+        value = NUM2LONG(c);
+        if (seqst->seqp->initial_value(seqst->seqp, value)) {
+            seqst->seqp->remove(seqst->seqp, 0, 0);
+            rb_raise(rb_eArgError, "invalid initial value");
+        }
+        /* ... */
+    case 2:
+        if (!NIL_P(flags)) {
+            flags = NUM2INT(b);
+        }
+        break;
+    }
+    if (!NIL_P(options)) {
+        rb_iterate(rb_each, options, bdb_seq_i_options, res);
+    }
+    a = bdb_test_recno(obj, &key, &recno, a);
+    if (seqst->seqp->open(seqst->seqp, txnid, &key, flags)) {
+        seqst->seqp->remove(seqst->seqp, txnid, 0);
+        rb_raise(rb_eArgError, "can't open the sequence");
+    }
+    seqst->txn = dbst->txn;
+    seqst->txnid = txnid;
+    if (rb_block_given_p()) {
+        return rb_ensure(rb_yield, res, bdb_seq_close, res);
+    }
+    return res;
+}
+
+static VALUE
+bdb_seq_s_open(int argc, VALUE *argv, VALUE obj)
+{
+    VALUE args[4];
+
+    if (argc <= 0 || argc > 3) {
+        rb_raise(rb_eArgError, "Invalid number of arguments %d", argc);
+    }
+    args[0] = argv[0];
+    args[1] = INT2NUM(DB_CREATE | DB_EXCL);
+    if (argc > 1) {
+        args[2] = argv[1];
+        if (argc > 2) {
+            args[3] = argv[2];
+        }
+    }
+    return bdb_seq_open(argc + 1, args, obj);
+}
+
+        
+static VALUE
+bdb_seq_remove(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_SEQ *seqst;
+    VALUE a;
+    int flags = 0;
+
+    GetSEQ(obj, seqst);
+    if (rb_scan_args(argc, argv, "01", &a)) {
+        flags = NUM2INT(a);
+    }
+    if (seqst->seqp->remove(seqst->seqp, seqst->txnid, flags)) {
+        rb_raise(rb_eArgError, "invalid argument");
+    }
+    seqst->seqp = NULL;
+    return Qnil;
+}
+
+static VALUE
+bdb_seq_get(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_SEQ *seqst;
+    int delta = 1, flags = 0;
+    VALUE a, b;
+    db_seq_t val;
+
+    GetSEQ(obj, seqst);
+    switch (rb_scan_args(argc, argv, "02", &a, &b)) {
+    case 2:
+        flags = NUM2INT(b);
+        /* ... */
+    case 1:
+        delta = NUM2INT(a);
+    }
+    bdb_test_error(seqst->seqp->get(seqst->seqp, seqst->txnid, delta, &val, flags));
+    return LONG2NUM(val);
+}
+
+static VALUE
+bdb_seq_cachesize(VALUE obj)
+{
+
+    bdb_SEQ *seqst;
+    int size;
+
+    GetSEQ(obj, seqst);
+    bdb_test_error(seqst->seqp->get_cachesize(seqst->seqp, &size));
+    return INT2NUM(size);
+}
+
+static VALUE
+bdb_seq_flags(VALUE obj)
+{
+
+    bdb_SEQ *seqst;
+    unsigned int flags;
+
+    GetSEQ(obj, seqst);
+    bdb_test_error(seqst->seqp->get_flags(seqst->seqp, &flags));
+    return INT2NUM(flags);
+}
+
+static VALUE
+bdb_seq_range(VALUE obj)
+{
+    bdb_SEQ *seqst;
+    db_seq_t deb, fin;
+
+    GetSEQ(obj, seqst);
+    bdb_test_error(seqst->seqp->get_range(seqst->seqp, &deb, &fin));
+    return rb_assoc_new(LONG2NUM(deb), LONG2NUM(fin));
+}
+
+static VALUE
+bdb_seq_stat(int argc, VALUE *argv, VALUE obj)
+{
+    bdb_SEQ *seqst;
+    int  flags = 0;
+    VALUE a, res;
+    DB_SEQUENCE_STAT sta;
+
+    GetSEQ(obj, seqst);
+    if (rb_scan_args(argc, argv, "01", &a)) {
+        flags = NUM2INT(a);
+    }
+    bdb_test_error(seqst->seqp->stat(seqst->seqp, (void *)&sta, flags));
+    res = rb_hash_new();
+    rb_hash_aset(res, rb_str_new2("wait"), INT2NUM(sta.st_wait));
+    rb_hash_aset(res, rb_str_new2("nowait"), INT2NUM(sta.st_nowait));
+    rb_hash_aset(res, rb_str_new2("current"), INT2NUM(sta.st_current));
+    rb_hash_aset(res, rb_str_new2("value"), INT2NUM(sta.st_value));
+    rb_hash_aset(res, rb_str_new2("last_value"), INT2NUM(sta.st_last_value));
+    rb_hash_aset(res, rb_str_new2("min"), INT2NUM(sta.st_min));
+    rb_hash_aset(res, rb_str_new2("max"), INT2NUM(sta.st_max));
+    rb_hash_aset(res, rb_str_new2("cache_size"), INT2NUM(sta.st_cache_size));
+    rb_hash_aset(res, rb_str_new2("flags"), INT2NUM(sta.st_flags));
+    return res;
+}
+
+static VALUE
+bdb_seq_db(VALUE obj)
+{
+    bdb_SEQ *seqst;
+
+    GetSEQ(obj, seqst);
+    return seqst->db;
+}
+
+static VALUE
+bdb_seq_key(VALUE obj)
+{
+    bdb_SEQ *seqst;
+    DBT key;
+
+    GetSEQ(obj, seqst);
+    bdb_test_error(seqst->seqp->get_key(seqst->seqp, &key));
+    return bdb_test_load_key(seqst->db, &key);
+}
+
+#endif
+
+void bdb_init_sequence()
+{
+#if BDB_VERSION >= 40300
+    bdb_cSeq = rb_define_class_under(bdb_mDb, "Sequence", rb_cObject);
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+    rb_undef_alloc_func(bdb_cSeq);
+#else
+    rb_undef_method(CLASS_OF(bdb_cSeq), "allocate");
+#endif
+    rb_undef_method(CLASS_OF(bdb_cSeq), "new");
+    rb_define_method(bdb_cCommon, "open_sequence", bdb_seq_open, -1);
+    rb_define_method(bdb_cCommon, "create_sequence", bdb_seq_s_open, -1);
+    rb_define_method(bdb_cSeq, "get", bdb_seq_get, -1);
+    rb_define_method(bdb_cSeq, "stat", bdb_seq_stat, -1);
+    rb_define_method(bdb_cSeq, "close", bdb_seq_close, 0);
+    rb_define_method(bdb_cSeq, "remove", bdb_seq_remove, -1);
+    rb_define_method(bdb_cSeq, "range", bdb_seq_range, 0);
+    rb_define_method(bdb_cSeq, "cachesize", bdb_seq_cachesize, 0);
+    rb_define_method(bdb_cSeq, "flags", bdb_seq_flags, 0);
+    rb_define_method(bdb_cSeq, "db", bdb_seq_db, 0);
+    rb_define_method(bdb_cSeq, "key", bdb_seq_key, 0);
+    rb_define_private_method(bdb_cSeq, "__txn_close__", bdb_seq_txn_close, 2);
+    rb_define_private_method(bdb_cSeq, "__txn_dup__", bdb_seq_txn_dup, 1);
+#endif
+}
+




More information about the Pkg-ruby-extras-commits mailing list