%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/rs/ruby/1.8/lib/ruby/gems/1.8/gems/bdb1-0.2.5/ext/bdb1/
Upload File :
Create Path :
Current File : /home/rs/ruby/1.8/lib/ruby/gems/1.8/gems/bdb1-0.2.5/ext/bdb1/bdb1.c

#include "bdb1.h"

VALUE bdb1_eFatal;
VALUE bdb1_mDb, bdb1_mMarshal, bdb1_cCommon, bdb1_cRecnum;
static ID id_dump, id_load;

ID bdb1_id_current_db;

static VALUE bdb1_cBtree, bdb1_cHash, bdb1_cUnknown;

static ID id_bt_compare, id_bt_prefix, id_h_hash, bdb1_id_call;

static VALUE bdb1_errstr;
static int bdb1_errcall = 0;

static const char *
db_strerror(int err)
{
    if (err == 0)
        return "" ;

    if (err > 0)
        return strerror(err) ;

    return "Unknown Error" ;
}

int
bdb1_test_error(int comm)
{
    VALUE error;

    switch (comm) {
    case 0:
    case 1:
        break;
    default:
        error = bdb1_eFatal;
        if (bdb1_errcall) {
            bdb1_errcall = 0;
            rb_raise(error, "%s -- %s", StringValueCStr(bdb1_errstr), db_strerror(comm));
        }
        else
            rb_raise(error, "%s", db_strerror(errno));
    }
    return comm;
}

static VALUE
test_dump(VALUE obj, DBT *key, VALUE a, int type_kv)
{
    bdb1_DB *dbst;
    VALUE tmp = a;

    Data_Get_Struct(obj, bdb1_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], bdb1_id_call, 1, a);
 	}
    }
    if (dbst->marshal != Qundef) {
        if (rb_obj_is_kind_of(a, bdb1_cDelegate)) {
	    tmp = bdb1_deleg_to_orig(tmp);
	}
	tmp = rb_funcall(dbst->marshal, 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 (NIL_P(a)) {
	    key->data = StringValueCStr(tmp);
	    key->size = RSTRING_LEN(tmp) + 1;
	    return tmp;
	}
    }
    key->data = StringValuePtr(tmp);
    key->size = RSTRING_LEN(tmp);
    return tmp;
}

static VALUE
test_ret(VALUE obj, VALUE tmp, VALUE a, int type_kv)
{
    bdb1_DB *dbst;
    Data_Get_Struct(obj, bdb1_DB, dbst);
    if (dbst->marshal != Qundef || NIL_P(a)) {
	return a;
    }
    if (dbst->filter[type_kv]) {
	return rb_obj_as_string(a);
    }
    return tmp;
}

static VALUE
test_recno(VALUE obj, DBT *key, db_recno_t *recno, VALUE a)
{
    bdb1_DB *dbst;
    Data_Get_Struct(obj, bdb1_DB, dbst);
    if (dbst->type == DB_RECNO) {
        *recno = NUM2INT(a) + dbst->array_base;
        key->data = recno;
        key->size = sizeof(db_recno_t);
	return a;
    }
    return test_dump(obj, key, a, FILTER_KEY);
}

VALUE
bdb1_test_load(VALUE obj, const DBT *a, int type_kv)
{
    VALUE res;
    int i;
    bdb1_DB *dbst;

    Data_Get_Struct(obj, bdb1_DB, dbst);
    if (dbst->marshal != Qundef) {
	res = rb_str_new(a->data, a->size);
	if (dbst->filter[2 + type_kv]) {
	    if (FIXNUM_P(dbst->filter[2 + type_kv])) {
		res = rb_funcall(obj,
				 NUM2INT(dbst->filter[2 + type_kv]), 1, res);
	    }
	    else {
		res = rb_funcall(dbst->filter[2 + type_kv],
				 bdb1_id_call, 1, res);
	    }
	}
	res = rb_funcall(dbst->marshal, id_load, 1, res);
    }
    else {
	if (a->size == 1 && ((char *)a->data)[0] == '\000') {
	    res = Qnil;
	}
	else {
	    res = rb_tainted_str_new(a->data, a->size);
	    if (dbst->filter[2 + type_kv]) {
		if (FIXNUM_P(dbst->filter[2 + type_kv])) {
		    res = rb_funcall(obj,
				     NUM2INT(dbst->filter[2 + type_kv]),
				     1, res);
		}
		else {
		    res = rb_funcall(dbst->filter[2 + type_kv],
				     bdb1_id_call, 1, res);
		}
	    }
	}
    }
    return res;
}

static VALUE
test_load_dyna(VALUE obj, DBT *key, DBT *val)
{
    bdb1_DB *dbst;
    VALUE del, res, tmp;
    struct deleg_class *delegst;

    Data_Get_Struct(obj, bdb1_DB, dbst);
    res = bdb1_test_load(obj, val, FILTER_VALUE);
    if (dbst->marshal != Qundef && !SPECIAL_CONST_P(res)) {
	del = Data_Make_Struct(bdb1_cDelegate, struct deleg_class,
			       bdb1_deleg_mark, bdb1_deleg_free, delegst);
	delegst->db = obj;
	if (dbst->type == DB_RECNO) {
	    tmp = INT2NUM((*(db_recno_t *)key->data) - dbst->array_base);
	}
	else {
	    tmp = rb_str_new(key->data, key->size);
	}
	delegst->key = rb_funcall(dbst->marshal, id_load, 1, tmp);
	delegst->obj = res;
	res = del;
    }
    return res;
}


static int
bdb1_bt_compare(const DBT *a, const DBT *b)
{
    VALUE obj, av, bv, res;
    bdb1_DB *dbst;

    if (NIL_P(obj = rb_thread_local_aref(rb_thread_current(), bdb1_id_current_db))) {
	rb_raise(bdb1_eFatal, "BUG : current_db not set");
    }
    Data_Get_Struct(obj, bdb1_DB, dbst);
    av = bdb1_test_load(obj, a, FILTER_VALUE);
    bv = bdb1_test_load(obj, b, FILTER_VALUE);
    if (dbst->bt_compare == 0)
	res = rb_funcall(obj, id_bt_compare, 2, av, bv);
    else
	res = rb_funcall(dbst->bt_compare, bdb1_id_call, 2, av, bv);
    return NUM2INT(res);
}

static size_t
bdb1_bt_prefix(const DBT *a, const DBT *b)
{
    VALUE obj, av, bv, res;
    bdb1_DB *dbst;

    if (NIL_P(obj = rb_thread_local_aref(rb_thread_current(), bdb1_id_current_db))) {
	rb_raise(bdb1_eFatal, "BUG : current_db not set");
    }
    Data_Get_Struct(obj, bdb1_DB, dbst);
    av = bdb1_test_load(obj, a, FILTER_VALUE);
    bv = bdb1_test_load(obj, b, FILTER_VALUE);
    if (dbst->bt_prefix == 0)
	res = rb_funcall(obj, id_bt_prefix, 2, av, bv);
    else
	res = rb_funcall(dbst->bt_prefix, bdb1_id_call, 2, av, bv);
    return NUM2INT(res);
}

static u_int32_t
bdb1_h_hash(const void *bytes, size_t length)
{
    VALUE obj, st, res;
    bdb1_DB *dbst;

    if (NIL_P(obj = rb_thread_local_aref(rb_thread_current(), bdb1_id_current_db))) {
	rb_raise(bdb1_eFatal, "BUG : current_db not set");
    }
    Data_Get_Struct(obj, bdb1_DB, 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, bdb1_id_call, 1, st);
    return (u_int32_t)NUM2LONG(res);
}

static void
bdb1_i_close(bdb1_DB *dbst)
{
    if (dbst->dbp != NULL && !(dbst->options & BDB1_NOT_OPEN)) {
	dbst->options |= BDB1_NOT_OPEN;
	bdb1_test_error(dbst->dbp->close(dbst->dbp));
    }
    dbst->dbp = NULL;
}

static void
bdb1_free(bdb1_DB *dbst)
{
    bdb1_i_close(dbst);
    free(dbst);
}

static void
bdb1_mark(bdb1_DB *dbst)
{
    int i;

    rb_gc_mark(dbst->marshal);
    rb_gc_mark(dbst->bt_compare);
    rb_gc_mark(dbst->bt_prefix);
    rb_gc_mark(dbst->h_hash);
    for (i = 0; i < 4; i++) {
	rb_gc_mark(dbst->filter[i]);
    }
}

static VALUE
bdb1_i185_btree(VALUE obj, VALUE dbstobj)
{
    VALUE key, value;
    bdb1_DB *dbst;
    char *options;

    Data_Get_Struct(dbstobj, bdb1_DB, dbst);
    key = rb_ary_entry(obj, 0);
    value = rb_ary_entry(obj, 1);
    key = rb_obj_as_string(key);
    options = StringValueCStr(key);
    if (strcmp(options, "set_flags") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.bi.flags = NUM2INT(value);
    }
    else if (strcmp(options, "set_cachesize") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.bi.cachesize = NUM2INT(value);
    }
    else if (strcmp(options, "set_bt_minkey") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.bi.minkeypage = NUM2INT(value);
    }
    else if (strcmp(options, "set_pagesize") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.bi.psize = NUM2INT(value);
    }
    else if (strcmp(options, "set_bt_compare") == 0) {
        if (!rb_respond_to(value, bdb1_id_call)) {
            rb_raise(bdb1_eFatal, "arg must respond to #call");
        }
	dbst->has_info = Qtrue;
	dbst->options |= BDB1_BT_COMPARE;
	dbst->bt_compare = value;
	dbst->info.bi.compare = bdb1_bt_compare;
    }
    else if (strcmp(options, "set_bt_prefix") == 0) {
        if (!rb_respond_to(value, bdb1_id_call)) {
            rb_raise(bdb1_eFatal, "arg must respond to #call");
        }
	dbst->has_info = Qtrue;
	dbst->options |= BDB1_BT_PREFIX;
	dbst->bt_prefix = value;
	dbst->info.bi.prefix = bdb1_bt_prefix;
    }
    else if (strcmp(options, "set_lorder") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.bi.lorder = NUM2INT(value);
    }
    return Qnil;
}

static VALUE
bdb1_i185_hash(VALUE obj, VALUE dbstobj)
{
    VALUE key, value;
    bdb1_DB *dbst;
    char *options;

    Data_Get_Struct(dbstobj, bdb1_DB, dbst);
    key = rb_ary_entry(obj, 0);
    value = rb_ary_entry(obj, 1);
    key = rb_obj_as_string(key);
    options = StringValueCStr(key);
    if (strcmp(options, "set_h_ffactor") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.hi.ffactor = NUM2INT(value);
    }
    else if (strcmp(options, "set_h_nelem") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.hi.nelem = NUM2INT(value);
    }
    else if (strcmp(options, "set_cachesize") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.hi.cachesize = NUM2INT(value);
    }
    else if (strcmp(options, "set_h_hash") == 0) {
        if (!rb_respond_to(value, bdb1_id_call)) {
            rb_raise(bdb1_eFatal, "arg must respond to #call");
        }
	dbst->has_info = Qtrue;
	dbst->options |= BDB1_H_HASH;
	dbst->h_hash = value;
	dbst->info.hi.hash = bdb1_h_hash;
    }
    else if (strcmp(options, "set_lorder") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.hi.lorder = NUM2INT(value);
    }
    return Qnil;
}

static VALUE
bdb1_i185_recno(VALUE obj, VALUE dbstobj)
{
    VALUE key, value;
    bdb1_DB *dbst;
    char *options, *str;

    Data_Get_Struct(dbstobj, bdb1_DB, dbst);
    key = rb_ary_entry(obj, 0);
    value = rb_ary_entry(obj, 1);
    key = rb_obj_as_string(key);
    options = StringValueCStr(key);
    if (strcmp(options, "set_flags") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.ri.flags = NUM2INT(value);
    }
    else if (strcmp(options, "set_re_delim") == 0) {
	int ch;
	if (TYPE(value) == T_STRING) {
	    str = StringValueCStr(value);
	    dbst->info.ri.bval = str[0];
	}
	else {
	    dbst->info.ri.bval = NUM2INT(value);
	}
	dbst->has_info = Qtrue;
	dbst->info.ri.flags |= R_FIXEDLEN;
    }
    else if (strcmp(options, "set_re_len") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.ri.reclen = NUM2INT(value);
	dbst->info.ri.flags |= R_FIXEDLEN;
    }
    else if (strcmp(options, "set_re_pad") == 0) {
	int ch;
	if (TYPE(value) == T_STRING) {
	    str = StringValueCStr(value);
	    dbst->info.ri.bval = str[0];
	}
	else {
	    dbst->info.ri.bval = NUM2INT(value);
	}
	dbst->has_info = Qtrue;
	dbst->info.ri.flags |= R_FIXEDLEN;
    }
    else if (strcmp(options, "set_cachesize") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.ri.cachesize = NUM2INT(value);
    }
    else if (strcmp(options, "set_pagesize") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.ri.psize = NUM2INT(value);
    }
    else if (strcmp(options, "set_lorder") == 0) {
	dbst->has_info = Qtrue;
	dbst->info.ri.lorder = NUM2INT(value);
    }
    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(bdb1_eFatal, "array base must be 0 or 1");
	}
    }
    return Qnil;
}

static VALUE
bdb1_load_dump(VALUE obj)
{
    VALUE res;

    res = rb_funcall(obj, rb_intern("respond_to?"), 2, ID2SYM(id_load), Qtrue);
    if (RTEST(res)) {
	res = rb_funcall(obj, rb_intern("respond_to?"), 2, ID2SYM(id_dump), Qtrue);
    }
    return res;
}

static VALUE
bdb1_i185_common(VALUE obj, VALUE dbstobj)
{
    VALUE key, value;
    bdb1_DB *dbst;
    char *options;

    Data_Get_Struct(dbstobj, bdb1_DB, dbst);
    key = rb_ary_entry(obj, 0);
    value = rb_ary_entry(obj, 1);
    key = rb_obj_as_string(key);
    options = StringValueCStr(key);
    if (strcmp(options, "marshal") == 0) {
        switch (value) {
        case Qtrue:
	    dbst->marshal = bdb1_mMarshal;
	    dbst->options |= BDB1_MARSHAL;
	    break;
        case Qfalse:
	    dbst->marshal = Qundef;
	    dbst->options &= ~BDB1_MARSHAL;
	    break;
        default:
	    if (!RTEST(bdb1_load_dump(value))) {
		rb_raise(bdb1_eFatal, "marshal value must be true or false");
	    }
	    dbst->marshal = value;
	    dbst->options |= BDB1_MARSHAL;
	    break;
        }
    }
    else if (strcmp(options, "set_store_key") == 0) {
        if (!rb_respond_to(value, bdb1_id_call)) {
            rb_raise(bdb1_eFatal, "arg must respond to #call");
        }
	dbst->filter[FILTER_KEY] = value;
    }
    else if (strcmp(options, "set_fetch_key") == 0) {
        if (!rb_respond_to(value, bdb1_id_call)) {
            rb_raise(bdb1_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, bdb1_id_call)) {
            rb_raise(bdb1_eFatal, "arg must respond to #call");
        }
	dbst->filter[FILTER_VALUE] = value;
    }
    else if (strcmp(options, "set_fetch_value") == 0) {
        if (!rb_respond_to(value, bdb1_id_call)) {
            rb_raise(bdb1_eFatal, "arg must respond to #call");
        }
	dbst->filter[2 + FILTER_VALUE] = value;
    }
    return Qnil;
}

static int
bdb1_hard_count(DB *dbp)
{
    DBT key, data;
    db_recno_t recno;
    int count = 0;

    DATA_ZERO(key);
    key.data = &recno;
    key.size = sizeof(db_recno_t);
    DATA_ZERO(data);
    if (bdb1_test_error(dbp->seq(dbp, &key, &data, DB_LAST)) == 0) {
	count = *(db_recno_t *)key.data;
    }
    return count;
}

/*
 * call-seq:
 *
 *   BDB1::Btree.new(name = nil, flags = "r", mode = 0, options = {})
 *   BDB1::Hash.new(name = nil, flags = "r", mode = 0, options = {})
 *   BDB1::Recnum.new(name = nil, flags = "r", mode = 0, options = {})
 *
 * Open the database.
 *
 * * +name+
 *   The argument name is used as the name of a single physical
 *   file on disk that will be used to back the database.
 *
 *   If +nil+ is given, an on-memory database is created.
 *
 * * +flags+
 *   The flags must be the string "r", "r+", "w", "w+", "a", "a+" or
 *   and integer value.
 *
 *   The flags value must be set to 0 or by bitwise inclusively
 *   OR'ing together one or more of the following values
 *
 *   * +BDB1::CREATE+
 *     Create any underlying files, as necessary. If the files
 *     do not already exist and the DB_CREATE flag is not
 *     specified, the call will fail.
 *
 *   * +BDB1::RDONLY+
 *     Open the database for reading only. Any attempt to
 *     modify items in the database will fail regardless of the
 *     actual permissions of any underlying files.
 *
 *   * +BDB1::TRUNCATE+
 *     Physically truncate the underlying database file,
 *     discarding all previous subdatabases or databases.
 *     Underlying filesystem primitives are used to implement
 *     this flag. For this reason it is only applicable to the
 *     physical database file and cannot be used to discard
 *     subdatabases.
 *
 *     The DB_TRUNCATE flag cannot be transaction protected,
 *     and it is an error to specify it in a transaction
 *     protected environment.
 *
 *   * +BDB1::WRITE+
 *     Open the database for writing. Without this flag, any
 *     attempt to modify items in the database will fail.
 *
 * * +mode+
 *   mode to create the file
 *
 * * +options+
 *   Hash, Possible options are (see the documentation of Berkeley DB
 *   for more informations)
 *
 *   * +set_flags+: general database configuration
 *   * +set_cachesize+: set the database cache size
 *   * +set_pagesize+: set the underlying database page size
 *   * +set_lorder+: set the database byte order
 *   * +set_store_key+: specify a Proc called before a key is stored
 *   * +set_fetch_key+: specify a Proc called after a key is read
 *   * +set_store_value+: specify a Proc called before a value is stored
 *   * +set_fetch_value+: specify a Proc called after a value is read
 *
 * * +options specific to BDB1::Btree+
 *
 *   * +set_bt_compare+: specify a Btree comparison function
 *   * +set_bt_minkey+: set the minimum number of keys per Btree page
 *   * +set_bt_prefix+: specify a Btree prefix comparison function
 *
 * * +options specific to BDB1::Hash+
 *
 *   * +set_h_ffactor+: set the Hash table density
 *   * +set_h_hash+: specify a hashing function
 *   * +set_h_nelem+: set the Hash table size
 *
 * * +options specific to BDB1::Recnum+
 *
 *   * +set_re_delim+: set the variable-length record delimiter
 *   * +set_re_len+: set the fixed-length record length
 *   * +set_re_pad+: set the fixed-length record pad byte
 *
 *   Proc given to +set_bt_compare+, +set_bt_prefix+,
 *   +set_h_hash+, +set_store_key+, +set_fetch_key+,
 *   +set_store_value+ and +set_fetch_value+ can be also
 *   specified as a method (replace the prefix +set_+ with
 *   +bdb1_+)
 *
 *   For example:
 *
 *     module BDB1
 *       class Btreesort < Btree
 *         def bdb1_bt_compare(a, b)
 *           b.downcase <=> a.downcase
 *         end
 *       end
 *     end
 */
VALUE
bdb1_init(int argc, VALUE *argv, VALUE obj)
{
    VALUE b, c, d, f;
    int mode, oflags;
    char *name;
    bdb1_DB *dbst;
    void *openinfo = NULL;

    f = Qnil; name = NULL;
    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    oflags = DB_RDONLY;
    if (argc && TYPE(argv[argc - 1]) == T_HASH) {
	f = argv[argc - 1];
	argc--;
    }
    switch(rb_scan_args(argc, argv, "03", &b, &c, &d)) {
    case 3:
	mode = NUM2INT(d);
	/* ... */
    case 2:
	if (TYPE(c) == T_STRING) {
	    char *m = StringValueCStr(c);
	    if (strcmp(m, "r") == 0) {
		oflags = DB_RDONLY;
	    }
	    else if (strcmp(m, "r+") == 0) {
		oflags = DB_WRITE;
	    }
	    else if (strcmp(m, "w") == 0 || strcmp(m, "w+") == 0) {
		oflags = DB_CREATE | DB_TRUNCATE | DB_WRITE;
	    }
	    else if (strcmp(m, "a") == 0 || strcmp(m, "a+") == 0) {
		oflags = DB_CREATE | DB_WRITE;
	    }
	    else {
		rb_raise(bdb1_eFatal, "flags must be r, r+, w, w+, a or a+");
	    }
	}
	else if (NIL_P(c)) {
	    oflags = DB_RDONLY;
	}
	else {
	    oflags = NUM2INT(c);
	}
	/* ... */
    case 1:
	if (!NIL_P(b)) {
	    SafeStringValue(b);
	    name = StringValueCStr(b);
	}
	else {
	    name = NULL;
	}
	/* ... */
    }
    Data_Get_Struct(obj, bdb1_DB, dbst);
    if (dbst->type < DB_BTREE || dbst->type > DB_RECNO) {
	rb_raise(bdb1_eFatal, "Unknown db185 type %d", dbst->type);
    }
    if (!NIL_P(f)) {
	if (TYPE(f) != T_HASH) {
            rb_raise(bdb1_eFatal, "options must be an hash");
	}
	switch(dbst->type) {
	case 0:
	    rb_iterate(rb_each, f, bdb1_i185_btree, obj);
	    if (dbst->bt_compare == 0 && rb_respond_to(obj, id_bt_compare)) {
		dbst->has_info = Qtrue;
		dbst->options |= BDB1_BT_COMPARE;
		dbst->info.bi.compare = bdb1_bt_compare;
	    }
	    if (dbst->bt_prefix == 0 && rb_respond_to(obj, id_bt_prefix)) {
		dbst->has_info = Qtrue;
		dbst->options |= BDB1_BT_PREFIX;
		dbst->info.bi.prefix = bdb1_bt_prefix;
	    }
	    break;
	case 1:
	    rb_iterate(rb_each, f, bdb1_i185_hash, obj);
	    if (dbst->h_hash == 0 && rb_respond_to(obj, id_h_hash)) {
		dbst->has_info = Qtrue;
		dbst->options |= BDB1_H_HASH;
		dbst->info.hi.hash = bdb1_h_hash;
	    }
	    break;
	case 2:
	    rb_iterate(rb_each, f, bdb1_i185_recno, obj);
	    break;
	}
	rb_iterate(rb_each, f, bdb1_i185_common, obj);
    }
    if (name == NULL) oflags = O_CREAT | O_RDWR;
    if (dbst->has_info) openinfo = &dbst->info;
    dbst->dbp = dbopen(name, oflags, mode, dbst->type, openinfo);
    if (dbst->dbp == NULL) {
	rb_raise(bdb1_eFatal, "Failed `%s'", db_strerror(errno));
    }
    dbst->options &= ~BDB1_NOT_OPEN;
    if (dbst->type == 2) {
	dbst->len = bdb1_hard_count(dbst->dbp);
    }
    return obj;
}

/*
 * call-seq:
 *   db.close(flags = 0)
 *
 * Closes the file.
 */
static VALUE
bdb1_close(VALUE obj)
{
    VALUE opt;
    bdb1_DB *dbst;

    if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4) {
	rb_raise(rb_eSecurityError, "Insecure: can't close the database");
    }
    Data_Get_Struct(obj, bdb1_DB, dbst);
    bdb1_i_close(dbst);
    return Qnil;
}

VALUE
bdb1_s_alloc(VALUE obj)
{
    bdb1_DB *dbst;
    VALUE res, cl;

    res = Data_Make_Struct(obj, bdb1_DB, bdb1_mark, bdb1_free, dbst);
    dbst->marshal = Qundef;
    dbst->options |= BDB1_NOT_OPEN;
    cl = obj;
    while (cl) {
	if (cl == bdb1_cBtree || RCLASS(cl)->m_tbl == RCLASS(bdb1_cBtree)->m_tbl) {
	    dbst->type = DB_BTREE;
	    break;
	}
	else if (cl == bdb1_cHash || RCLASS(cl)->m_tbl == RCLASS(bdb1_cHash)->m_tbl) {
	    dbst->type = DB_HASH;
	    break;
	}
	else if (cl == bdb1_cRecnum || RCLASS(cl)->m_tbl == RCLASS(bdb1_cRecnum)->m_tbl) {
	    dbst->type = DB_RECNO;
	    break;
	}
	cl = RCLASS_SUPER(cl);
    }
    if (!cl) {
	rb_raise(bdb1_eFatal, "unknown database type");
    }
    if (RTEST(bdb1_load_dump(obj))) {
	dbst->marshal = obj;
	dbst->options |= BDB1_MARSHAL;
    }
    if (rb_method_boundp(obj, rb_intern("bdb1_store_key"), 0)) {
	dbst->filter[FILTER_KEY] = INT2FIX(rb_intern("bdb1_store_key"));
    }
    if (rb_method_boundp(obj, rb_intern("bdb1_fetch_key"), 0)) {
	dbst->filter[2 + FILTER_KEY] = INT2FIX(rb_intern("bdb1_fetch_key"));
    }
    if (rb_method_boundp(obj, rb_intern("bdb1_store_value"), 0)) {
	dbst->filter[FILTER_VALUE] = INT2FIX(rb_intern("bdb1_store_value"));
    }
    if (rb_method_boundp(obj, rb_intern("bdb1_fetch_value"), 0)) {
	dbst->filter[2 + FILTER_VALUE] = INT2FIX(rb_intern("bdb1_fetch_value"));
    }
    return res;
}

/*
 * Same as +new+.
 */
static VALUE
bdb1_s_create(int argc, VALUE *argv, VALUE obj)
{
    VALUE st, res;

    res = rb_funcall2(obj, rb_intern("allocate"), 0, 0);
    rb_obj_call_init(res, argc, argv);
    return res;
}

static VALUE
bdb1_i_create(VALUE obj, VALUE db)
{
    VALUE tmp[2];
    tmp[0] = rb_ary_entry(obj, 0);
    tmp[1] = rb_ary_entry(obj, 1);
    bdb1_put(2, tmp, db);
    return Qnil;
}

/*
 * call-seq:
 *   BDB1::Btree[hash]
 *   BDB1::Btree[key1, value1, key2, value2, ...]
 *   BDB1::Hash[hash]
 *   BDB1::Hash[key1, value1, key2, value2, ...]
 *   BDB1::Recnum[hash]
 *   BDB1::Recnum[key1, value1, key2, value2, ...]
 *
 * Creates a new temporary on-memory database, populated with the
 * given hash or pairs of objects.
 */
static VALUE
bdb1_s_aref(int argc, VALUE *argv, VALUE obj)
{
    VALUE res, tmp[2];
    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], bdb1_i_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) {
        bdb1_put(2, argv + i, res);
    }
    return res;
}

/*
 * Same as +new+ except that if a block is given it is called with an
 * initialized object which is automatically closed when done.
 */
static VALUE
bdb1_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, bdb1_close, res);
    }
    return res;
}

/*
 * call-seq:
 *   db.put(key, value, flags = 0)
 *
 * Stores the +value+ associating with +key+ and returns the value
 * stored.
 *
 * +flags+ can have the value +DBD::NOOVERWRITE+, in this case it will
 * return +nil+ if the specified key exist, otherwise +true+.
 */
VALUE
bdb1_put(int argc, VALUE *argv, VALUE obj)
{
    volatile VALUE a0 = Qnil;
    volatile VALUE b0 = Qnil;
    VALUE a, b, c;
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;

    rb_secure(4);
    GetDB(obj, dbst);
    if (rb_scan_args(argc, argv, "21", &a, &b, &c) == 3)
        flags = NUM2INT(c);
    else
	flags = 0;
    DATA_ZERO(key);
    DATA_ZERO(data);
    a0 = test_recno(obj, &key, &recno, a);
    b0 = test_dump(obj, &data, b, FILTER_VALUE);
    ret = bdb1_test_error(dbst->dbp->put(dbst->dbp, &key, &data, flags));
    if (ret == DB_KEYEXIST)
	return Qfalse;
    return test_ret(obj, b0, b, FILTER_VALUE);
}

static VALUE
bdb1_assign(VALUE obj, VALUE a, VALUE b)
{
    VALUE tmp[2];
    tmp[0] = a;
    tmp[1] = b;
    bdb1_put(2, tmp, obj);
    return b;
}

static VALUE
test_load_key(VALUE obj, DBT *key)
{
    bdb1_DB *dbst;
    Data_Get_Struct(obj, bdb1_DB, dbst);
    if (dbst->type == DB_RECNO)
        return INT2NUM((*(db_recno_t *)key->data) - dbst->array_base);
    return bdb1_test_load(obj, key, FILTER_KEY);
}

static VALUE
bdb1_assoc(VALUE obj, DBT *key, DBT *data)
{
    return rb_assoc_new(test_load_key(obj, key),
			bdb1_test_load(obj, data, FILTER_VALUE));
}

static VALUE
bdb1_assoc_dyna(VALUE obj, DBT *key, DBT *data)
{
    return rb_assoc_new(test_load_key(obj, key),
			test_load_dyna(obj, key, data));
}

static VALUE
bdb1_get_internal(int argc, VALUE *argv, VALUE obj, VALUE notfound, int dyna)
{
    volatile VALUE a = Qnil;
    VALUE b, c;
    bdb1_DB *dbst;
    DBT key, data;
    DBT datas;
    int flagss;
    int ret, flags;
    db_recno_t recno;

    flagss = flags = 0;
    GetDB(obj, dbst);
    DATA_ZERO(key);
    DATA_ZERO(data);
    switch(rb_scan_args(argc, argv, "12", &a, &b, &c)) {
    case 3:
        flags = NUM2INT(c);
	break;
    case 2:
	flagss = flags = NUM2INT(b);
	break;
    }
    a = test_recno(obj, &key, &recno, a);
    ret = bdb1_test_error(dbst->dbp->get(dbst->dbp, &key, &data, flags));
    if (ret == DB_NOTFOUND)
        return notfound;
    if (dyna) {
	return test_load_dyna(obj, &key, &data);
    }
    else {
	return bdb1_test_load(obj, &data, FILTER_VALUE);
    }
}

VALUE
bdb1_get(int argc, VALUE *argv, VALUE obj)
{
    return bdb1_get_internal(argc, argv, obj, Qnil, 0);
}

/*
 * call-seq:
 *   db[key]
 *   db[key, flags = 0]
 *
 * Returns the value corresponding to +key+.
 */
static VALUE
bdb1_get_dyna(int argc, VALUE *argv, VALUE obj)
{
    return bdb1_get_internal(argc, argv, obj, Qnil, 1);
}

static VALUE
bdb1_fetch(int argc, VALUE *argv, VALUE obj)
{
    VALUE key, if_none;
    VALUE val;

    rb_scan_args(argc, argv, "11", &key, &if_none);
    val = bdb1_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;
}

static VALUE
bdb1_has_key(VALUE obj, VALUE key)
{
    return bdb1_get_internal(1, &key, obj, Qfalse, 0);
}

/*
 * call-seq:
 *   db.has_both?(key, value)
 *
 * Returns +true+ if the association from +key+ is +value+.
 */
static VALUE
bdb1_has_both(VALUE obj, VALUE a, VALUE b)
{
    bdb1_DB *dbst;
    DBT key, data;
    DBT keys, datas;
    int ret, flags;
    db_recno_t recno;
    volatile VALUE c = Qnil;
    volatile VALUE d = Qnil;

    GetDB(obj, dbst);
    DATA_ZERO(key);
    DATA_ZERO(data);
    c = test_recno(obj, &key, &recno, a);
    d = test_dump(obj, &data, b, FILTER_VALUE);
    MEMCPY(&keys, &key, DBT, 1);
    MEMCPY(&datas, &data, DBT, 1);
    flags = (dbst->type == DB_HASH)?DB_FIRST:R_CURSOR;
    while (1) {
	ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
	if (ret == DB_NOTFOUND) {
	    return Qfalse;
	}
	if (key.size == keys.size &&
	    memcmp(keys.data, key.data, key.size) == 0 &&
	    data.size == datas.size &&
	    memcmp(datas.data, data.data, data.size) == 0) {
	    return Qtrue;
	}
	flags = DB_NEXT;
    }
    return Qnil;
}

/*
 * call-seq:
 *   db.delete(key)
 *
 * Removes the association from the +key+. 
 *
 * It returns the object deleted or +nil+ if the specified key doesn't
 * exist.
 */
VALUE
bdb1_del(VALUE obj, VALUE a)
{
    bdb1_DB *dbst;
    DBT key;
    int ret;
    db_recno_t recno;
    volatile VALUE c = Qnil;

    rb_secure(4);
    GetDB(obj, dbst);
    if (dbst->type == DB_HASH) {
	rb_warning("delete can give strange result with DB_HASH");
    }
    DATA_ZERO(key);
    c = test_recno(obj, &key, &recno, a);
    ret = bdb1_test_error(dbst->dbp->del(dbst->dbp, &key, 0));
    if (ret == DB_NOTFOUND)
        return Qnil;
    else
        return obj;
}

static VALUE
bdb1_empty(VALUE obj)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;

    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, DB_FIRST));
    if (ret == DB_NOTFOUND) {
        return Qtrue;
    }
    FREE_KEY(dbst, key);
    return Qfalse;
}

static VALUE
bdb1_delete_if(VALUE obj)
{
    bdb1_DB *dbst;
    DBT key, data, save;
    int ret, ret1, flags;
    db_recno_t recno;

    rb_secure(4);
    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = DB_FIRST;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return Qnil;
        }
	flags = DB_NEXT;
        if (RTEST(rb_yield(bdb1_assoc(obj, &key, &data)))) {
	    bdb1_test_error(dbst->dbp->del(dbst->dbp, 0, R_CURSOR));
        }
    } while (1);
    return obj;
}

VALUE
bdb1_clear(VALUE obj)
{
    bdb1_DB *dbst;
    DBT key, data, save;
    int ret, value, flags;
    db_recno_t recno;

    rb_secure(4);
    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = DB_FIRST;
    value = 0;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return INT2NUM(value);
        }
	value++;
	bdb1_test_error(dbst->dbp->del(dbst->dbp, 0, R_CURSOR));
    } while (1);
    return INT2NUM(-1);
}

static VALUE
bdb1_length(VALUE obj)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, value, flags;
    db_recno_t recno;

    GetDB(obj, dbst);
    if (dbst->type == DB_RECNO) {
	return INT2NUM(bdb1_hard_count(dbst->dbp));
    }
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    value = 0;
    flags = DB_FIRST;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return INT2NUM(value);
        }
	flags = DB_NEXT;
	FREE_KEY(dbst, key);
        value++;
    } while (1);
    return INT2NUM(value);
}

static VALUE
bdb1_each_valuec(VALUE obj, VALUE sens, VALUE result)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;
    VALUE interm, rest;

    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = (sens == DB_NEXT)?DB_FIRST:DB_LAST;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return result;
        }
	flags = sens;
	FREE_KEY(dbst, key);
	interm = bdb1_test_load(obj, &data, FILTER_VALUE);
        rest = rb_yield(interm);
	if (!NIL_P(result) && RTEST(rest)) {
	    rb_ary_push(result, interm);
	}
    } while (1);
}

VALUE
bdb1_each_value(VALUE obj)
{
    return bdb1_each_valuec(obj, DB_NEXT, Qnil);
}

VALUE
bdb1_each_eulav(VALUE obj)
{
    return bdb1_each_valuec(obj, DB_PREV, Qnil);
}

static VALUE
bdb1_each_keyc(VALUE obj, int sens)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;

    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = (sens == DB_NEXT)?DB_FIRST:DB_LAST;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return Qnil;
        }
	rb_yield(test_load_key(obj, &key));
	flags = sens;
    } while (1);
    return obj;
}

VALUE
bdb1_each_key(VALUE obj)
{
    return bdb1_each_keyc(obj, DB_NEXT);
}

static VALUE
bdb1_each_yek(VALUE obj)
{
    return bdb1_each_keyc(obj, DB_PREV);
}

static VALUE
bdb1_each_common(VALUE obj, int sens)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;

    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = (sens == DB_NEXT)?DB_FIRST:DB_LAST;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return Qnil;
        }
        rb_yield(bdb1_assoc(obj, &key, &data));
	flags = sens;
    } while (1);
    return obj;
}

static VALUE
bdb1_each_pair(VALUE obj)
{
    return bdb1_each_common(obj, DB_NEXT);
}

static VALUE
bdb1_each_riap(VALUE obj)
{
    return bdb1_each_common(obj, DB_PREV);
}

VALUE
bdb1_to_type(VALUE obj, VALUE result, VALUE flag)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;

    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = NIL_P(flag) ? DB_LAST : DB_FIRST;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return result;
        }
	switch (TYPE(result)) {
	case T_ARRAY:
	    if (RTEST(flag)) {
		rb_ary_push(result, bdb1_assoc(obj, &key, &data));
	    }
	    else {
		rb_ary_push(result, bdb1_test_load(obj, &data, FILTER_VALUE));
	    }
	    break;
	case T_HASH:
	    if (RTEST(flag)) {
		rb_hash_aset(result, test_load_key(obj, &key),
			     bdb1_test_load(obj, &data, FILTER_VALUE));
	    }
	    else {
		rb_hash_aset(result, bdb1_test_load(obj, &data, FILTER_VALUE),
			     test_load_key(obj, &key));
	    }
	}
	flags = NIL_P(flag) ? DB_PREV : DB_NEXT;
    } while (1);
    return result;
}

static VALUE
bdb1_to_a(VALUE obj)
{
    return bdb1_to_type(obj, rb_ary_new(), Qtrue);
}

static VALUE
bdb1_invert(VALUE obj)
{
    return bdb1_to_type(obj, rb_hash_new(), Qfalse);
}

static VALUE
bdb1_to_hash(VALUE obj)
{
    return bdb1_to_type(obj, rb_hash_new(), Qtrue);
}

static VALUE
bdb1_each_kv(VALUE obj, VALUE a, VALUE result, VALUE flag)
{
    bdb1_DB *dbst;
    DBT keys, key, data;
    int ret, flags;
    db_recno_t recno;
    VALUE k;
    volatile VALUE b = Qnil;

    GetDB(obj, dbst);
    b = test_recno(obj, &key, &recno, a);
    MEMCPY(&keys, &key, DBT, 1);
    DATA_ZERO(data);
    flags = R_CURSOR;
    while (1) {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
	if (ret == DB_NOTFOUND || keys.size != key.size ||
	    memcmp(keys.data, key.data, key.size) != 0) {
	    return NIL_P(result)?obj:result;
	}
	k =  bdb1_test_load(obj, &data, FILTER_VALUE);
	if (RTEST(flag)) {
	    k = rb_assoc_new(test_load_key(obj, &key), k);
	}
	if (NIL_P(result)) {
	    rb_yield(k);
	}
	else {
	    rb_ary_push(result, k);
	}
	flags = DB_NEXT;
    }
    return Qnil;
}

/*
 * call-seq:
 *   db.duplicates(key, assoc = true)
 *
 * Returns an array of all duplicate associations for the +key+.
 *
 * If +assoc+ is +false+ return only the values.
 */
static VALUE
bdb1_bt_duplicates(int argc, VALUE *argv, VALUE obj)
{
    VALUE a, b;

    if (rb_scan_args(argc, argv, "11", &a, &b) == 1) {
	b = Qtrue;
    }
    return bdb1_each_kv(obj, a, rb_ary_new(), b);
}

/*
 * call-seq:
 *   db.each_dup(key)
 *
 * Iterates over duplicate associations for the +key+.
 */
static VALUE
bdb1_bt_dup(VALUE obj, VALUE a)
{
    return bdb1_each_kv(obj, a, Qnil, Qtrue);
}

/*
 * call-seq:
 *   db.each_dup_value(key)
 *
 * Iterates over duplicate values for the +key+.
 */
static VALUE
bdb1_bt_dupval(VALUE obj, VALUE a)
{
    return bdb1_each_kv(obj, a, Qnil, Qfalse);
}

static VALUE
bdb1_reject(VALUE obj)
{
    return rb_hash_delete_if(bdb1_to_hash(obj));
}

static VALUE
bdb1_values(VALUE obj)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;
    VALUE ary;

    GetDB(obj, dbst);
    ary = rb_ary_new();
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = DB_FIRST;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return ary;
        }
	flags = DB_NEXT;
	FREE_KEY(dbst, key);
        rb_ary_push(ary, bdb1_test_load(obj, &data, FILTER_VALUE));
    } while (1);
    return ary;
}

VALUE
bdb1_internal_value(VALUE obj, VALUE a, VALUE b, int sens)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;

    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = (sens == DB_NEXT)?DB_FIRST:DB_LAST;
    for (;;) {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
	if (ret == DB_NOTFOUND) {
	    return RTEST(b) ? Qnil : Qfalse;
	}
	flags = sens;
	if (RTEST(rb_equal(a, bdb1_test_load(obj, &data, FILTER_VALUE)))) {
	    VALUE d;

	    d = RTEST(b) ? test_load_key(obj, &key) : Qtrue;
	    FREE_KEY(dbst, key);
	    return  d;
	}
	FREE_KEY(dbst, key);
    }
    return RTEST(b) ? Qnil : Qfalse;
}

VALUE
bdb1_key(VALUE obj, VALUE a)
{
    return bdb1_internal_value(obj, a, Qtrue, DB_NEXT);
}

VALUE
bdb1_has_value(VALUE obj, VALUE a)
{
    return bdb1_internal_value(obj, a, Qfalse, DB_NEXT);
}

static VALUE
bdb1_keys(VALUE obj)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;
    VALUE ary;

    GetDB(obj, dbst);
    ary = rb_ary_new();
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = DB_FIRST;
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
            return ary;
        }
	rb_ary_push(ary, test_load_key(obj, &key));
	FREE_KEY(dbst, key);
	flags = DB_NEXT;
    } while (1);
    return ary;
}

static VALUE
bdb1_sync(VALUE obj)
{
    bdb1_DB *dbst;

    if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
	rb_raise(rb_eSecurityError, "Insecure: can't sync the database");
    GetDB(obj, dbst);
    bdb1_test_error(dbst->dbp->sync(dbst->dbp, 0));
    return Qtrue;
}

static VALUE
bdb1_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, bdb1_get(1, argv + i, obj));
    }
    return result;
}

static VALUE
bdb1_select(VALUE obj)
{
    VALUE result = rb_ary_new();

    if (rb_block_given_p()) {
	return bdb1_each_valuec(obj, DB_NEXT, result);
    }
    rb_raise(rb_eArgError, "block is not given");
}

VALUE
bdb1_each_vc(VALUE obj, int replace, int rtest)
{
    bdb1_DB *dbst;
    DBT key, data;
    int ret, flags;
    db_recno_t recno;
    VALUE res, result, val;

    GetDB(obj, dbst);
    INIT_RECNO(dbst, key, recno);
    DATA_ZERO(data);
    flags = DB_FIRST;
    result = rb_ary_new();
    do {
        ret = bdb1_test_error(dbst->dbp->seq(dbst->dbp, &key, &data, flags));
        if (ret == DB_NOTFOUND) {
	    return result;
        }
	flags = DB_NEXT;
	FREE_KEY(dbst, key);
        val = bdb1_test_load(obj, &data, FILTER_VALUE);
        res = rb_yield(val);
	if (rtest) {
	    if (RTEST(res)) {
		rb_ary_push(result, val);
	    }
	}
	else {
	    rb_ary_push(result, res);
	}
	if (RTEST(replace)) {
	    DATA_ZERO(data);
	    res = test_dump(obj, &data, res, FILTER_VALUE);
	    bdb1_test_error(dbst->dbp->put(dbst->dbp, &key, &data, 0));
	}
    } while (1);
    return Qnil;
}

/*
 * This interface if for the version 1.85 and 1.86 of Berkeley DB (for
 * Berkeley version >= 2 see bdb)
 *
 * Developers may choose to store data in any of several different
 * storage structures to satisfy the requirements of a particular
 * application.  In database terminology, these storage structures and
 * the code that operates on them are called access methods.
 *
 * The library includes support for the following access methods:
 *
 * * BDB1::Btree
 *
 *     B+tree: Stores keys in sorted order, using a default function
 *     that does lexicographical ordering of keys.
 *
 * * BDB1::Hash
 *
 *     Hashing: Stores records in a hash table for fast searches based
 *     on strict equality, using a default that hashes on the key as a
 *     bit string.  Extended Linear Hashing modifies the hash function
 *     used by the table as new records are inserted, in order to keep
 *     buckets underfull in the steady state.
 *
 * * BDB1::Recnum
 *
 *     Fixed and Variable-Length Records.  Stores fixed- or
 *     variable-length records in sequential order.
 *
 */
void
Init_bdb1(void)
{
    bdb1_mMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
    id_dump = rb_intern("dump");
    id_load = rb_intern("load");
    bdb1_id_current_db = rb_intern("bdb1_current_db");
    id_bt_compare = rb_intern("bdb1_bt_compare");
    id_bt_prefix = rb_intern("bdb1_bt_prefix");
    id_h_hash = rb_intern("bdb1_h_hash");
    bdb1_id_call = rb_intern("call");
    if (rb_const_defined_at(rb_cObject, rb_intern("BDB1"))) {
	rb_raise(rb_eNameError, "class already defined");
    }
    bdb1_mDb = rb_define_module("BDB1");
    bdb1_eFatal = rb_define_class_under(bdb1_mDb, "Fatal", rb_eStandardError);
/* CONSTANT */
    rb_define_const(bdb1_mDb, "VERSION_MAJOR", INT2FIX(DB_VERSION_MAJOR));
    rb_define_const(bdb1_mDb, "VERSION_MINOR", INT2FIX(DB_VERSION_MINOR));
    rb_define_const(bdb1_mDb, "RELEASE_PATCH", INT2FIX(DB_RELEASE_PATCH));
    rb_define_const(bdb1_mDb, "VERSION", rb_str_new2("1.x.x"));
    rb_define_const(bdb1_mDb, "BTREE", INT2FIX(DB_BTREE));
    rb_define_const(bdb1_mDb, "HASH", INT2FIX(DB_HASH));
    rb_define_const(bdb1_mDb, "RECNO", INT2FIX(DB_RECNO));
    rb_define_const(bdb1_mDb, "AFTER", INT2FIX(DB_AFTER));
    rb_define_const(bdb1_mDb, "BEFORE", INT2FIX(DB_BEFORE));
    rb_define_const(bdb1_mDb, "CREATE", INT2FIX(DB_CREATE));
    rb_define_const(bdb1_mDb, "DUP", INT2FIX(DB_DUP));
    rb_define_const(bdb1_mDb, "FIRST", INT2FIX(DB_FIRST));
    rb_define_const(bdb1_mDb, "LAST", INT2FIX(DB_LAST));
    rb_define_const(bdb1_mDb, "NEXT", INT2FIX(DB_NEXT));
    rb_define_const(bdb1_mDb, "PREV", INT2FIX(DB_PREV));
    rb_define_const(bdb1_mDb, "RDONLY", INT2FIX(DB_RDONLY));
    rb_define_const(bdb1_mDb, "SET_RANGE", INT2FIX(DB_SET_RANGE));
    rb_define_const(bdb1_mDb, "TRUNCATE", INT2FIX(DB_TRUNCATE));
    rb_define_const(bdb1_mDb, "WRITE", INT2FIX(DB_WRITE));
    rb_define_const(bdb1_mDb, "NOOVERWRITE", INT2FIX(DB_NOOVERWRITE));
/* DATABASE */
    bdb1_cCommon = rb_define_class_under(bdb1_mDb, "Common", rb_cObject);
    rb_define_method(bdb1_cCommon, "initialize", bdb1_init, -1);
    rb_include_module(bdb1_cCommon, rb_mEnumerable);
    rb_define_alloc_func(bdb1_cCommon, bdb1_s_alloc);
    rb_define_singleton_method(bdb1_cCommon, "create", bdb1_s_create, -1);
    rb_define_singleton_method(bdb1_cCommon, "open", bdb1_s_open, -1);
    rb_define_singleton_method(bdb1_cCommon, "[]", bdb1_s_aref, -1);
    rb_define_method(bdb1_cCommon, "close", bdb1_close, 0);
    rb_define_method(bdb1_cCommon, "db_close", bdb1_close, 0);
    rb_define_method(bdb1_cCommon, "put", bdb1_put, -1);
    rb_define_method(bdb1_cCommon, "db_put", bdb1_put, -1);
    rb_define_method(bdb1_cCommon, "[]=", bdb1_assign, 2);
    rb_define_method(bdb1_cCommon, "store", bdb1_put, -1);
    rb_define_method(bdb1_cCommon, "get", bdb1_get_dyna, -1);
    rb_define_method(bdb1_cCommon, "db_get", bdb1_get_dyna, -1);
    rb_define_method(bdb1_cCommon, "[]", bdb1_get_dyna, -1);
    rb_define_method(bdb1_cCommon, "fetch", bdb1_fetch, -1);
    rb_define_method(bdb1_cCommon, "delete", bdb1_del, 1);
    rb_define_method(bdb1_cCommon, "del", bdb1_del, 1);
    rb_define_method(bdb1_cCommon, "db_del", bdb1_del, 1);
    rb_define_method(bdb1_cCommon, "sync", bdb1_sync, 0);
    rb_define_method(bdb1_cCommon, "db_sync", bdb1_sync, 0);
    rb_define_method(bdb1_cCommon, "flush", bdb1_sync, 0);
    rb_define_method(bdb1_cCommon, "each", bdb1_each_pair, 0);
    rb_define_method(bdb1_cCommon, "each_value", bdb1_each_value, 0);
    rb_define_method(bdb1_cCommon, "reverse_each_value", bdb1_each_eulav, 0);
    rb_define_method(bdb1_cCommon, "each_key", bdb1_each_key, 0);
    rb_define_method(bdb1_cCommon, "reverse_each_key", bdb1_each_yek, 0);
    rb_define_method(bdb1_cCommon, "each_pair", bdb1_each_pair, 0);
    rb_define_method(bdb1_cCommon, "reverse_each", bdb1_each_riap, 0);
    rb_define_method(bdb1_cCommon, "reverse_each_pair", bdb1_each_riap, 0);
    rb_define_method(bdb1_cCommon, "keys", bdb1_keys, 0);
    rb_define_method(bdb1_cCommon, "values", bdb1_values, 0);
    rb_define_method(bdb1_cCommon, "delete_if", bdb1_delete_if, 0);
    rb_define_method(bdb1_cCommon, "reject!", bdb1_delete_if, 0);
    rb_define_method(bdb1_cCommon, "reject", bdb1_reject, 0);
    rb_define_method(bdb1_cCommon, "clear", bdb1_clear, 0);
    rb_define_method(bdb1_cCommon, "include?", bdb1_has_key, 1);
    rb_define_method(bdb1_cCommon, "has_key?", bdb1_has_key, 1);
    rb_define_method(bdb1_cCommon, "key?", bdb1_has_key, 1);
    rb_define_method(bdb1_cCommon, "member?", bdb1_has_key, 1);
    rb_define_method(bdb1_cCommon, "has_value?", bdb1_has_value, 1);
    rb_define_method(bdb1_cCommon, "value?", bdb1_has_value, 1);
    rb_define_method(bdb1_cCommon, "has_both?", bdb1_has_both, 2);
    rb_define_method(bdb1_cCommon, "both?", bdb1_has_both, 2);
    rb_define_method(bdb1_cCommon, "to_a", bdb1_to_a, 0);
    rb_define_method(bdb1_cCommon, "to_hash", bdb1_to_hash, 0);
    rb_define_method(bdb1_cCommon, "invert", bdb1_invert, 0);
    rb_define_method(bdb1_cCommon, "empty?", bdb1_empty, 0);
    rb_define_method(bdb1_cCommon, "length", bdb1_length, 0);
    rb_define_alias(bdb1_cCommon,  "size", "length");
    rb_define_method(bdb1_cCommon, "key", bdb1_key, 1);
    rb_define_method(bdb1_cCommon, "index", bdb1_key, 1);
    rb_define_method(bdb1_cCommon, "select", bdb1_select, 0);
    rb_define_method(bdb1_cCommon, "values_at", bdb1_values_at, -1);
    bdb1_cBtree = rb_define_class_under(bdb1_mDb, "Btree", bdb1_cCommon);
    rb_define_method(bdb1_cBtree, "duplicates", bdb1_bt_duplicates, -1);
    rb_define_method(bdb1_cBtree, "each_dup", bdb1_bt_dup, 1);
    rb_define_method(bdb1_cBtree, "each_dup_value", bdb1_bt_dupval, 1);
    bdb1_cHash = rb_define_class_under(bdb1_mDb, "Hash", bdb1_cCommon);
    rb_undef_method(bdb1_cHash, "delete_if");
    rb_undef_method(bdb1_cHash, "reverse_each_value");
    rb_undef_method(bdb1_cHash, "reverse_each_key");
    rb_undef_method(bdb1_cHash, "reverse_each_pair");
    rb_undef_method(bdb1_cHash, "reverse_each");
    bdb1_cUnknown = rb_define_class_under(bdb1_mDb, "Unknown", bdb1_cCommon);
    bdb1_errstr = rb_tainted_str_new(0, 0);
    rb_global_variable(&bdb1_errstr);
    bdb1_init_delegator();
    bdb1_init_recnum();
}

Zerion Mini Shell 1.0