dbm-1.1.0/0000755000175000017500000000000014353676074012504 5ustar terceiroterceirodbm-1.1.0/LICENSE.txt0000644000175000017500000000240214353676074014325 0ustar terceiroterceiroCopyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. dbm-1.1.0/ext/0000755000175000017500000000000014353676074013304 5ustar terceiroterceirodbm-1.1.0/ext/dbm/0000755000175000017500000000000014353676074014046 5ustar terceiroterceirodbm-1.1.0/ext/dbm/extconf.rb0000644000175000017500000002021314353676074016037 0ustar terceiroterceiro# frozen_string_literal: true # configure option: # --with-dbm-type=COMMA-SEPARATED-NDBM-TYPES # # ndbm type: # libc ndbm compatible library in libc. # db Berkeley DB (libdb) # db2 Berkeley DB (libdb2) # db1 Berkeley DB (libdb1) # db6 Berkeley DB (libdb6) # db5 Berkeley DB (libdb5) # db4 Berkeley DB (libdb4) # db3 Berkeley DB (libdb3) # gdbm_compat GDBM since 1.8.1 (libgdbm_compat) # gdbm GDBM until 1.8.0 (libgdbm) # qdbm QDBM (libqdbm) # ndbm Some legacy OS may have libndbm. # :stopdoc: require 'mkmf' dir_config("dbm") if dblib = with_config("dbm-type", nil) dblib = dblib.split(/[ ,]+/) else dblib = %w(libc db db2 db1 db6 db5 db4 db3 gdbm_compat gdbm qdbm) end headers = { "libc" => ["ndbm.h"], # 4.3BSD original ndbm, Berkeley DB 1 in 4.4BSD libc. "db" => ["db.h"], "db1" => ["db1/ndbm.h", "db1.h", "ndbm.h"], "db2" => ["db2/db.h", "db2.h", "db.h"], "db3" => ["db3/db.h", "db3.h", "db.h"], "db4" => ["db4/db.h", "db4.h", "db.h"], "db5" => ["db5/db.h", "db5.h", "db.h"], "db6" => ["db6/db.h", "db6.h", "db.h"], "gdbm_compat" => ["gdbm-ndbm.h", "gdbm/ndbm.h", "ndbm.h"], # GDBM since 1.8.1 "gdbm" => ["gdbm-ndbm.h", "gdbm/ndbm.h", "ndbm.h"], # GDBM until 1.8.0 "qdbm" => ["qdbm/relic.h", "relic.h"], } class << headers attr_accessor :found attr_accessor :defs end headers.found = [] headers.defs = nil def headers.db_check(db, hdr) old_libs = $libs.dup old_defs = $defs.dup result = db_check2(db, hdr) if !result $libs = old_libs $defs = old_defs end result end def have_declared_libvar(var, headers = nil, opt = "", &b) checking_for checking_message([*var].compact.join(' '), headers, opt) do try_declared_libvar(var, headers, opt, &b) end end def try_declared_libvar(var, headers = nil, opt = "", &b) if try_link(<<"SRC", opt, &b) #{cpp_include(headers)} /*top*/ int main(int argc, char *argv[]) { void *conftest_var = &#{var}; return 0; } SRC $defs.push(format("-DHAVE_DECLARED_LIBVAR_%s", var.tr_cpp)) true else false end end def have_undeclared_libvar(var, headers = nil, opt = "", &b) checking_for checking_message([*var].compact.join(' '), headers, opt) do try_undeclared_libvar(var, headers, opt, &b) end end def try_undeclared_libvar(var, headers = nil, opt = "", &b) var, type = *var if try_link(<<"SRC", opt, &b) #{cpp_include(headers)} /*top*/ int main(int argc, char *argv[]) { typedef #{type || 'int'} conftest_type; extern conftest_type #{var}; conftest_type *conftest_var = &#{var}; return 0; } SRC $defs.push(format("-DHAVE_UNDECLARED_LIBVAR_%s", var.tr_cpp)) true else false end end def have_empty_macro_dbm_clearerr(headers = nil, opt = "", &b) checking_for checking_message('empty macro of dbm_clearerr(foobarbaz)', headers, opt) do try_toplevel('dbm_clearerr(foobarbaz)', headers, opt, &b) end end def try_toplevel(src, headers = nil, opt = "", &b) if try_compile(<<"SRC", opt, &b) #{cpp_include(headers)} /*top*/ #{src} SRC true else false end end def headers.db_check2(db, hdr) $defs.push(%{-DRUBYDBM_DBM_HEADER='"#{hdr}"'}) $defs.push(%{-DRUBYDBM_DBM_TYPE='"#{db}"'}) hsearch = nil case db when /^db[2-6]?$/ hsearch = "-DDB_DBM_HSEARCH" when "gdbm_compat" have_library("gdbm") or return false end if !have_type("DBM", hdr, hsearch) return false end # 'libc' means ndbm is provided by libc. # 4.3BSD original ndbm is contained in libc. # 4.4BSD (and its derivatives such as NetBSD) contains Berkeley DB 1 in libc. if !(db == 'libc' ? have_func('dbm_open("", 0, 0)', hdr, hsearch) : have_library(db, 'dbm_open("", 0, 0)', hdr, hsearch)) return false end # Skip a mismatch of Berkeley DB's ndbm.h and old GDBM library. # # dbm_clearerr() should be available for any ndbm implementation. # It is available since the original (4.3BSD) ndbm and standardized by POSIX. # # However "can't resolve symbol 'dbm_clearerr'" problem may be caused by # header/library mismatch: Berkeley DB ndbm.h and GDBM library until 1.8.3. # GDBM (until 1.8.3) provides dbm_clearerr() as a empty macro in the header # and the library don't provide dbm_clearerr(). # Berkeley DB provides dbm_clearerr() as a usual function. # So Berkeley DB header with GDBM library causes the problem. # if !have_func('dbm_clearerr((DBM *)0)', hdr, hsearch) return false end # Berkeley DB's ndbm.h (since 1.85 at least) defines DBM_SUFFIX. # Note that _DB_H_ is not defined on Mac OS X because # it uses Berkeley DB 1 but ndbm.h doesn't include db.h. have_db_header = have_macro('DBM_SUFFIX', hdr, hsearch) # Old GDBM's ndbm.h, until 1.8.3, defines dbm_clearerr as a macro which # expands to no tokens. have_gdbm_header1 = have_empty_macro_dbm_clearerr(hdr, hsearch) # Recent GDBM's ndbm.h, since 1.9, includes gdbm.h and it defines _GDBM_H_. # ndbm compatibility layer of GDBM is provided by libgdbm (until 1.8.0) # and libgdbm_compat (since 1.8.1). have_gdbm_header2 = have_macro('_GDBM_H_', hdr, hsearch) # 4.3BSD's ndbm.h defines _DBM_IOERR. # The original ndbm is provided by libc in 4.3BSD. have_ndbm_header = have_macro('_DBM_IOERR', hdr, hsearch) # GDBM provides ndbm functions in libgdbm_compat since GDBM 1.8.1. # GDBM's ndbm.h defines _GDBM_H_ since GDBM 1.9. # If _GDBM_H_ is defined, 'gdbm_compat' is required and reject 'gdbm'. if have_gdbm_header2 && db == 'gdbm' return false end if have_db_header $defs.push('-DRUBYDBM_DB_HEADER') end have_gdbm_header = have_gdbm_header1 | have_gdbm_header2 if have_gdbm_header $defs.push('-DRUBYDBM_GDBM_HEADER') end # ndbm.h is provided by the original (4.3BSD) ndbm, # Berkeley DB 1 in libc of 4.4BSD and # ndbm compatibility layer of GDBM. # So, try to check header/library mismatch. # # Several (possibly historical) distributions provides libndbm. # It may be Berkeley DB, GDBM or 4.3BSD ndbm. # So mismatch check is not performed for that. # Note that libndbm is searched only when --with-dbm-type=ndbm is # given for configure. # if hdr == 'ndbm.h' && db != 'libc' && db != 'ndbm' if /\Adb\d?\z/ !~ db && have_db_header return false end if /\Agdbm/ !~ db && have_gdbm_header return false end if have_ndbm_header return false end end # Berkeley DB have_func('db_version((int *)0, (int *)0, (int *)0)', hdr, hsearch) # GDBM have_gdbm_version = have_declared_libvar("gdbm_version", hdr, hsearch) # gdbm_version is available since very old version (GDBM 1.5 at least). # However it is not declared by ndbm.h until GDBM 1.8.3. # We can't include both ndbm.h and gdbm.h because they both define datum type. # ndbm.h includes gdbm.h and gdbm_version is declared since GDBM 1.9. have_gdbm_version |= have_undeclared_libvar(["gdbm_version", "char *"], hdr, hsearch) # QDBM have_var("dpversion", hdr, hsearch) # detect mismatch between GDBM header and other library. # If GDBM header is included, GDBM library should be linked. if have_gdbm_header && !have_gdbm_version return false end # DBC type is required to disable error messages by Berkeley DB 2 or later. if have_db_header have_type("DBC", hdr, hsearch) end if hsearch $defs << hsearch @defs = hsearch end $defs << '-DDBM_HDR="<'+hdr+'>"' @found << hdr puts "header: #{hdr}" puts "library: #{db}" true end if dblib.any? {|db| headers.fetch(db, ["ndbm.h"]).any? {|hdr| headers.db_check(db, hdr) } } have_header("cdefs.h") have_header("sys/cdefs.h") have_func("dbm_pagfno((DBM *)0)", headers.found, headers.defs) have_func("dbm_dirfno((DBM *)0)", headers.found, headers.defs) convertible_int("datum.dsize", headers.found, headers.defs) checking_for("sizeof(DBM) is available") { if try_compile(< #endif #ifdef HAVE_SYS_CDEFS_H # include #endif #include DBM_HDR const int sizeof_DBM = (int)sizeof(DBM); SRC $defs << '-DDBM_SIZEOF_DBM=sizeof(DBM)' else $defs << '-DDBM_SIZEOF_DBM=0' end } create_makefile("dbm") end # :startdoc: dbm-1.1.0/ext/dbm/dbm.c0000644000175000017500000007010214353676074014754 0ustar terceiroterceiro/************************************************ dbm.c - $Author$ created at: Mon Jan 24 15:59:52 JST 1994 Copyright (C) 1995-2001 Yukihiro Matsumoto ************************************************/ #include "ruby.h" #ifdef HAVE_CDEFS_H # include #endif #ifdef HAVE_SYS_CDEFS_H # include #endif #include DBM_HDR #include #include #define DSIZE_TYPE TYPEOF_DATUM_DSIZE #if SIZEOF_DATUM_DSIZE > SIZEOF_INT # define RSTRING_DSIZE(s) RSTRING_LEN(s) # define TOO_LONG(n) ((void)(n),0) #else # define RSTRING_DSIZE(s) RSTRING_LENINT(s) # define TOO_LONG(n) ((long)(+(DSIZE_TYPE)(n)) != (n)) #endif static VALUE rb_cDBM, rb_eDBMError; #define RUBY_DBM_RW_BIT 0x20000000 struct dbmdata { long di_size; DBM *di_dbm; }; NORETURN(static void closed_dbm(void)); static void closed_dbm(void) { rb_raise(rb_eDBMError, "closed DBM file"); } #define GetDBM(obj, dbmp) do {\ TypedData_Get_Struct((obj), struct dbmdata, &dbm_type, (dbmp));\ if ((dbmp)->di_dbm == 0) closed_dbm();\ } while (0) #define GetDBM2(obj, dbmp, dbm) do {\ GetDBM((obj), (dbmp));\ (dbm) = (dbmp)->di_dbm;\ } while (0) static void free_dbm(void *ptr) { struct dbmdata *dbmp = ptr; if (dbmp->di_dbm) dbm_close(dbmp->di_dbm); xfree(dbmp); } static size_t memsize_dbm(const void *ptr) { const struct dbmdata *dbmp = ptr; size_t size = sizeof(*dbmp); if (dbmp->di_dbm) size += DBM_SIZEOF_DBM; return size; } static const rb_data_type_t dbm_type = { "dbm", {0, free_dbm, memsize_dbm,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * call-seq: * dbm.close * * Closes the database. */ static VALUE fdbm_close(VALUE obj) { struct dbmdata *dbmp; GetDBM(obj, dbmp); dbm_close(dbmp->di_dbm); dbmp->di_dbm = 0; return Qnil; } /* * call-seq: * dbm.closed? -> true or false * * Returns true if the database is closed, false otherwise. */ static VALUE fdbm_closed(VALUE obj) { struct dbmdata *dbmp; TypedData_Get_Struct(obj, struct dbmdata, &dbm_type, dbmp); if (dbmp->di_dbm == 0) return Qtrue; return Qfalse; } static VALUE fdbm_alloc(VALUE klass) { struct dbmdata *dbmp; return TypedData_Make_Struct(klass, struct dbmdata, &dbm_type, dbmp); } /* * call-seq: * DBM.new(filename[, mode[, flags]]) -> dbm * * Open a dbm database with the specified name, which can include a directory * path. Any file extensions needed will be supplied automatically by the dbm * library. For example, Berkeley DB appends '.db', and GNU gdbm uses two * physical files with extensions '.dir' and '.pag'. * * The mode should be an integer, as for Unix chmod. * * Flags should be one of READER, WRITER, WRCREAT or NEWDB. */ static VALUE fdbm_initialize(int argc, VALUE *argv, VALUE obj) { VALUE file, vmode, vflags; DBM *dbm; struct dbmdata *dbmp; int mode, flags = 0; TypedData_Get_Struct(obj, struct dbmdata, &dbm_type, dbmp); if (rb_scan_args(argc, argv, "12", &file, &vmode, &vflags) == 1) { mode = 0666; /* default value */ } else if (NIL_P(vmode)) { mode = -1; /* return nil if DB not exist */ } else { mode = NUM2INT(vmode); } if (!NIL_P(vflags)) flags = NUM2INT(vflags); FilePathValue(file); /* * Note: * gdbm 1.10 works with O_CLOEXEC. gdbm 1.9.1 silently ignore it. */ #ifndef O_CLOEXEC # define O_CLOEXEC 0 #endif if (flags & RUBY_DBM_RW_BIT) { flags &= ~RUBY_DBM_RW_BIT; dbm = dbm_open(RSTRING_PTR(file), flags|O_CLOEXEC, mode); } else { dbm = 0; if (mode >= 0) { dbm = dbm_open(RSTRING_PTR(file), O_RDWR|O_CREAT|O_CLOEXEC, mode); } if (!dbm) { dbm = dbm_open(RSTRING_PTR(file), O_RDWR|O_CLOEXEC, 0); } if (!dbm) { dbm = dbm_open(RSTRING_PTR(file), O_RDONLY|O_CLOEXEC, 0); } } if (dbm) { /* * History of dbm_pagfno() and dbm_dirfno() in ndbm and its compatibles. * (dbm_pagfno() and dbm_dirfno() is not standardized.) * * 1986: 4.3BSD provides ndbm. * It provides dbm_pagfno() and dbm_dirfno() as macros. * 1991: gdbm-1.5 provides them as functions. * They returns a same descriptor. * (Earlier releases may have the functions too.) * 1991: Net/2 provides Berkeley DB. * It doesn't provide dbm_pagfno() and dbm_dirfno(). * 1992: 4.4BSD Alpha provides Berkeley DB with dbm_dirfno() as a function. * dbm_pagfno() is a macro as DBM_PAGFNO_NOT_AVAILABLE. * 1997: Berkeley DB 2.0 is released by Sleepycat Software, Inc. * It defines dbm_pagfno() and dbm_dirfno() as macros. * 2011: gdbm-1.9 creates a separate dir file. * dbm_pagfno() and dbm_dirfno() returns different descriptors. */ #if defined(HAVE_DBM_PAGFNO) rb_fd_fix_cloexec(dbm_pagfno(dbm)); #endif #if defined(HAVE_DBM_DIRFNO) rb_fd_fix_cloexec(dbm_dirfno(dbm)); #endif #if defined(RUBYDBM_DB_HEADER) && defined(HAVE_TYPE_DBC) /* Disable Berkeley DB error messages such as: * DB->put: attempt to modify a read-only database */ ((DBC*)dbm)->dbp->set_errfile(((DBC*)dbm)->dbp, NULL); #endif } if (!dbm) { if (mode == -1) return Qnil; rb_sys_fail_str(file); } if (dbmp->di_dbm) dbm_close(dbmp->di_dbm); dbmp->di_dbm = dbm; dbmp->di_size = -1; return obj; } /* * call-seq: * DBM.open(filename[, mode[, flags]]) -> dbm * DBM.open(filename[, mode[, flags]]) {|dbm| block} * * Open a dbm database and yields it if a block is given. See also * DBM.new. */ static VALUE fdbm_s_open(int argc, VALUE *argv, VALUE klass) { VALUE obj = fdbm_alloc(klass); if (NIL_P(fdbm_initialize(argc, argv, obj))) { return Qnil; } if (rb_block_given_p()) { return rb_ensure(rb_yield, obj, fdbm_close, obj); } return obj; } static VALUE fdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone) { datum key, value; struct dbmdata *dbmp; DBM *dbm; long len; ExportStringValue(keystr); len = RSTRING_LEN(keystr); if (TOO_LONG(len)) goto not_found; key.dptr = RSTRING_PTR(keystr); key.dsize = (DSIZE_TYPE)len; GetDBM2(obj, dbmp, dbm); value = dbm_fetch(dbm, key); if (value.dptr == 0) { not_found: if (NIL_P(ifnone) && rb_block_given_p()) { keystr = rb_str_dup(keystr); return rb_yield(keystr); } return ifnone; } return rb_str_new(value.dptr, value.dsize); } /* * call-seq: * dbm[key] -> string value or nil * * Return a value from the database by locating the key string * provided. If the key is not found, returns nil. */ static VALUE fdbm_aref(VALUE obj, VALUE keystr) { return fdbm_fetch(obj, keystr, Qnil); } /* * call-seq: * dbm.fetch(key[, ifnone]) -> value * * Return a value from the database by locating the key string * provided. If the key is not found, returns +ifnone+. If +ifnone+ * is not given, raises IndexError. */ static VALUE fdbm_fetch_m(int argc, VALUE *argv, VALUE obj) { VALUE keystr, valstr, ifnone; rb_scan_args(argc, argv, "11", &keystr, &ifnone); valstr = fdbm_fetch(obj, keystr, ifnone); if (argc == 1 && !rb_block_given_p() && NIL_P(valstr)) rb_raise(rb_eIndexError, "key not found"); return valstr; } /* * call-seq: * dbm.key(value) -> string * * Returns the key for the specified value. */ static VALUE fdbm_key(VALUE obj, VALUE valstr) { datum key, val; struct dbmdata *dbmp; DBM *dbm; long len; ExportStringValue(valstr); len = RSTRING_LEN(valstr); if (TOO_LONG(len)) return Qnil; GetDBM2(obj, dbmp, dbm); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); if ((long)val.dsize == RSTRING_LEN(valstr) && memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0) { return rb_str_new(key.dptr, key.dsize); } } return Qnil; } /* :nodoc: */ static VALUE fdbm_index(VALUE hash, VALUE value) { rb_warn("DBM#index is deprecated; use DBM#key"); return fdbm_key(hash, value); } /* * call-seq: * dbm.select {|key, value| block} -> array * * Returns a new array consisting of the [key, value] pairs for which the code * block returns true. */ static VALUE fdbm_select(VALUE obj) { VALUE new = rb_ary_new(); datum key, val; DBM *dbm; struct dbmdata *dbmp; GetDBM2(obj, dbmp, dbm); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { VALUE assoc, v; val = dbm_fetch(dbm, key); assoc = rb_assoc_new(rb_str_new(key.dptr, key.dsize), rb_str_new(val.dptr, val.dsize)); v = rb_yield(assoc); if (RTEST(v)) { rb_ary_push(new, assoc); } GetDBM2(obj, dbmp, dbm); } return new; } /* * call-seq: * dbm.values_at(key, ...) -> Array * * Returns an array containing the values associated with the given keys. */ static VALUE fdbm_values_at(int argc, VALUE *argv, VALUE obj) { VALUE new = rb_ary_new2(argc); int i; for (i=0; idi_size = -1; rb_raise(rb_eDBMError, "dbm_delete failed"); } else if (dbmp->di_size >= 0) { dbmp->di_size--; } return valstr; } /* * call-seq: * dbm.shift() -> [key, value] * * Removes a [key, value] pair from the database, and returns it. * If the database is empty, returns nil. * The order in which values are removed/returned is not guaranteed. */ static VALUE fdbm_shift(VALUE obj) { datum key, val; struct dbmdata *dbmp; DBM *dbm; VALUE keystr, valstr; fdbm_modify(obj); GetDBM2(obj, dbmp, dbm); dbmp->di_size = -1; key = dbm_firstkey(dbm); if (!key.dptr) return Qnil; val = dbm_fetch(dbm, key); keystr = rb_str_new(key.dptr, key.dsize); valstr = rb_str_new(val.dptr, val.dsize); dbm_delete(dbm, key); return rb_assoc_new(keystr, valstr); } /* * call-seq: * dbm.reject! {|key, value| block} -> self * dbm.delete_if {|key, value| block} -> self * * Deletes all entries for which the code block returns true. * Returns self. */ static VALUE fdbm_delete_if(VALUE obj) { datum key, val; struct dbmdata *dbmp; DBM *dbm; VALUE keystr, valstr; VALUE ret, ary = rb_ary_tmp_new(0); int status = 0; long i, n; fdbm_modify(obj); GetDBM2(obj, dbmp, dbm); n = dbmp->di_size; dbmp->di_size = -1; for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); keystr = rb_str_new(key.dptr, key.dsize); OBJ_FREEZE(keystr); valstr = rb_str_new(val.dptr, val.dsize); ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status); if (status != 0) break; if (RTEST(ret)) rb_ary_push(ary, keystr); GetDBM2(obj, dbmp, dbm); } for (i = 0; i < RARRAY_LEN(ary); i++) { keystr = RARRAY_AREF(ary, i); key.dptr = RSTRING_PTR(keystr); key.dsize = (DSIZE_TYPE)RSTRING_LEN(keystr); if (dbm_delete(dbm, key)) { rb_raise(rb_eDBMError, "dbm_delete failed"); } } if (status) rb_jump_tag(status); if (n > 0) dbmp->di_size = n - RARRAY_LEN(ary); rb_ary_clear(ary); return obj; } /* * call-seq: * dbm.clear * * Deletes all data from the database. */ static VALUE fdbm_clear(VALUE obj) { datum key; struct dbmdata *dbmp; DBM *dbm; fdbm_modify(obj); GetDBM2(obj, dbmp, dbm); dbmp->di_size = -1; while (key = dbm_firstkey(dbm), key.dptr) { if (dbm_delete(dbm, key)) { rb_raise(rb_eDBMError, "dbm_delete failed"); } } dbmp->di_size = 0; return obj; } /* * call-seq: * dbm.invert -> hash * * Returns a Hash (not a DBM database) created by using each value in the * database as a key, with the corresponding key as its value. */ static VALUE fdbm_invert(VALUE obj) { datum key, val; struct dbmdata *dbmp; DBM *dbm; VALUE keystr, valstr; VALUE hash = rb_hash_new(); GetDBM2(obj, dbmp, dbm); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); keystr = rb_str_new(key.dptr, key.dsize); valstr = rb_str_new(val.dptr, val.dsize); rb_hash_aset(hash, valstr, keystr); } return hash; } static VALUE fdbm_store(VALUE,VALUE,VALUE); static VALUE update_i(RB_BLOCK_CALL_FUNC_ARGLIST(pair, dbm)) { const VALUE *ptr; Check_Type(pair, T_ARRAY); if (RARRAY_LEN(pair) < 2) { rb_raise(rb_eArgError, "pair must be [key, value]"); } ptr = RARRAY_CONST_PTR(pair); fdbm_store(dbm, ptr[0], ptr[1]); return Qnil; } /* * call-seq: * dbm.update(obj) * * Updates the database with multiple values from the specified object. * Takes any object which implements the each_pair method, including * Hash and DBM objects. */ static VALUE fdbm_update(VALUE obj, VALUE other) { rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj); return obj; } /* * call-seq: * dbm.replace(obj) * * Replaces the contents of the database with the contents of the specified * object. Takes any object which implements the each_pair method, including * Hash and DBM objects. */ static VALUE fdbm_replace(VALUE obj, VALUE other) { fdbm_clear(obj); rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj); return obj; } /* * call-seq: * dbm.store(key, value) -> value * dbm[key] = value * * Stores the specified string value in the database, indexed via the * string key provided. */ static VALUE fdbm_store(VALUE obj, VALUE keystr, VALUE valstr) { datum key, val; struct dbmdata *dbmp; DBM *dbm; fdbm_modify(obj); keystr = rb_obj_as_string(keystr); valstr = rb_obj_as_string(valstr); key.dptr = RSTRING_PTR(keystr); key.dsize = RSTRING_DSIZE(keystr); val.dptr = RSTRING_PTR(valstr); val.dsize = RSTRING_DSIZE(valstr); GetDBM2(obj, dbmp, dbm); dbmp->di_size = -1; if (dbm_store(dbm, key, val, DBM_REPLACE)) { dbm_clearerr(dbm); if (errno == EPERM) rb_sys_fail(0); rb_raise(rb_eDBMError, "dbm_store failed"); } return valstr; } /* * call-seq: * dbm.length -> integer * dbm.size -> integer * * Returns the number of entries in the database. */ static VALUE fdbm_length(VALUE obj) { datum key; struct dbmdata *dbmp; DBM *dbm; int i = 0; GetDBM2(obj, dbmp, dbm); if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { i++; } dbmp->di_size = i; return INT2FIX(i); } /* * call-seq: * dbm.empty? * * Returns true if the database is empty, false otherwise. */ static VALUE fdbm_empty_p(VALUE obj) { datum key; struct dbmdata *dbmp; DBM *dbm; GetDBM2(obj, dbmp, dbm); if (dbmp->di_size < 0) { dbm = dbmp->di_dbm; for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { return Qfalse; } } else { if (dbmp->di_size) return Qfalse; } return Qtrue; } /* * call-seq: * dbm.each_value {|value| block} -> self * * Calls the block once for each value string in the database. Returns self. */ static VALUE fdbm_each_value(VALUE obj) { datum key, val; struct dbmdata *dbmp; DBM *dbm; RETURN_ENUMERATOR(obj, 0, 0); GetDBM2(obj, dbmp, dbm); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); rb_yield(rb_str_new(val.dptr, val.dsize)); GetDBM2(obj, dbmp, dbm); } return obj; } /* * call-seq: * dbm.each_key {|key| block} -> self * * Calls the block once for each key string in the database. Returns self. */ static VALUE fdbm_each_key(VALUE obj) { datum key; struct dbmdata *dbmp; DBM *dbm; RETURN_ENUMERATOR(obj, 0, 0); GetDBM2(obj, dbmp, dbm); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { rb_yield(rb_str_new(key.dptr, key.dsize)); GetDBM2(obj, dbmp, dbm); } return obj; } /* * call-seq: * dbm.each_pair {|key,value| block} -> self * * Calls the block once for each [key, value] pair in the database. * Returns self. */ static VALUE fdbm_each_pair(VALUE obj) { datum key, val; DBM *dbm; struct dbmdata *dbmp; VALUE keystr, valstr; RETURN_ENUMERATOR(obj, 0, 0); GetDBM2(obj, dbmp, dbm); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); keystr = rb_str_new(key.dptr, key.dsize); valstr = rb_str_new(val.dptr, val.dsize); rb_yield(rb_assoc_new(keystr, valstr)); GetDBM2(obj, dbmp, dbm); } return obj; } /* * call-seq: * dbm.keys -> array * * Returns an array of all the string keys in the database. */ static VALUE fdbm_keys(VALUE obj) { datum key; struct dbmdata *dbmp; DBM *dbm; VALUE ary; GetDBM2(obj, dbmp, dbm); ary = rb_ary_new(); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { rb_ary_push(ary, rb_str_new(key.dptr, key.dsize)); } return ary; } /* * call-seq: * dbm.values -> array * * Returns an array of all the string values in the database. */ static VALUE fdbm_values(VALUE obj) { datum key, val; struct dbmdata *dbmp; DBM *dbm; VALUE ary; GetDBM2(obj, dbmp, dbm); ary = rb_ary_new(); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); rb_ary_push(ary, rb_str_new(val.dptr, val.dsize)); } return ary; } /* * call-seq: * dbm.include?(key) -> boolean * dbm.has_key?(key) -> boolean * dbm.member?(key) -> boolean * dbm.key?(key) -> boolean * * Returns true if the database contains the specified key, false otherwise. */ static VALUE fdbm_has_key(VALUE obj, VALUE keystr) { datum key, val; struct dbmdata *dbmp; DBM *dbm; long len; ExportStringValue(keystr); len = RSTRING_LEN(keystr); if (TOO_LONG(len)) return Qfalse; key.dptr = RSTRING_PTR(keystr); key.dsize = (DSIZE_TYPE)len; GetDBM2(obj, dbmp, dbm); val = dbm_fetch(dbm, key); if (val.dptr) return Qtrue; return Qfalse; } /* * call-seq: * dbm.has_value?(value) -> boolean * dbm.value?(value) -> boolean * * Returns true if the database contains the specified string value, false * otherwise. */ static VALUE fdbm_has_value(VALUE obj, VALUE valstr) { datum key, val; struct dbmdata *dbmp; DBM *dbm; long len; ExportStringValue(valstr); len = RSTRING_LEN(valstr); if (TOO_LONG(len)) return Qfalse; val.dptr = RSTRING_PTR(valstr); val.dsize = (DSIZE_TYPE)len; GetDBM2(obj, dbmp, dbm); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); if ((DSIZE_TYPE)val.dsize == (DSIZE_TYPE)RSTRING_LEN(valstr) && memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0) return Qtrue; } return Qfalse; } /* * call-seq: * dbm.to_a -> array * * Converts the contents of the database to an array of [key, value] arrays, * and returns it. */ static VALUE fdbm_to_a(VALUE obj) { datum key, val; struct dbmdata *dbmp; DBM *dbm; VALUE ary; GetDBM2(obj, dbmp, dbm); ary = rb_ary_new(); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); rb_ary_push(ary, rb_assoc_new(rb_str_new(key.dptr, key.dsize), rb_str_new(val.dptr, val.dsize))); } return ary; } /* * call-seq: * dbm.to_hash -> hash * * Converts the contents of the database to an in-memory Hash object, and * returns it. */ static VALUE fdbm_to_hash(VALUE obj) { datum key, val; struct dbmdata *dbmp; DBM *dbm; VALUE hash; GetDBM2(obj, dbmp, dbm); hash = rb_hash_new(); for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { val = dbm_fetch(dbm, key); rb_hash_aset(hash, rb_str_new(key.dptr, key.dsize), rb_str_new(val.dptr, val.dsize)); } return hash; } /* * call-seq: * dbm.reject {|key,value| block} -> Hash * * Converts the contents of the database to an in-memory Hash, then calls * Hash#reject with the specified code block, returning a new Hash. */ static VALUE fdbm_reject(VALUE obj) { return rb_hash_delete_if(fdbm_to_hash(obj)); } /* * == Introduction * * The DBM class provides a wrapper to a Unix-style * {dbm}[http://en.wikipedia.org/wiki/Dbm] or Database Manager library. * * Dbm databases do not have tables or columns; they are simple key-value * data stores, like a Ruby Hash except not resident in RAM. Keys and values * must be strings. * * The exact library used depends on how Ruby was compiled. It could be any * of the following: * * - The original ndbm library is released in 4.3BSD. * It is based on dbm library in Unix Version 7 but has different API to * support multiple databases in a process. * - {Berkeley DB}[http://en.wikipedia.org/wiki/Berkeley_DB] versions * 1 thru 6, also known as BDB and Sleepycat DB, now owned by Oracle * Corporation. * - Berkeley DB 1.x, still found in 4.4BSD derivatives (FreeBSD, OpenBSD, etc). * - {gdbm}[http://www.gnu.org/software/gdbm/], the GNU implementation of dbm. * - {qdbm}[http://fallabs.com/qdbm/index.html], another open source * reimplementation of dbm. * * All of these dbm implementations have their own Ruby interfaces * available, which provide richer (but varying) APIs. * * == Cautions * * Before you decide to use DBM, there are some issues you should consider: * * - Each implementation of dbm has its own file format. Generally, dbm * libraries will not read each other's files. This makes dbm files * a bad choice for data exchange. * * - Even running the same OS and the same dbm implementation, the database * file format may depend on the CPU architecture. For example, files may * not be portable between PowerPC and 386, or between 32 and 64 bit Linux. * * - Different versions of Berkeley DB use different file formats. A change to * the OS may therefore break DBM access to existing files. * * - Data size limits vary between implementations. Original Berkeley DB was * limited to 2GB of data. Dbm libraries also sometimes limit the total * size of a key/value pair, and the total size of all the keys that hash * to the same value. These limits can be as little as 512 bytes. That said, * gdbm and recent versions of Berkeley DB do away with these limits. * * Given the above cautions, DBM is not a good choice for long term storage of * important data. It is probably best used as a fast and easy alternative * to a Hash for processing large amounts of data. * * == Example * * require 'dbm' * db = DBM.open('rfcs', 0666, DBM::WRCREAT) * db['822'] = 'Standard for the Format of ARPA Internet Text Messages' * db['1123'] = 'Requirements for Internet Hosts - Application and Support' * db['3068'] = 'An Anycast Prefix for 6to4 Relay Routers' * puts db['822'] */ void Init_dbm(void) { rb_cDBM = rb_define_class("DBM", rb_cObject); /* Document-class: DBMError * Exception class used to return errors from the dbm library. */ rb_eDBMError = rb_define_class("DBMError", rb_eStandardError); rb_include_module(rb_cDBM, rb_mEnumerable); rb_define_alloc_func(rb_cDBM, fdbm_alloc); rb_define_singleton_method(rb_cDBM, "open", fdbm_s_open, -1); rb_define_method(rb_cDBM, "initialize", fdbm_initialize, -1); rb_define_method(rb_cDBM, "close", fdbm_close, 0); rb_define_method(rb_cDBM, "closed?", fdbm_closed, 0); rb_define_method(rb_cDBM, "[]", fdbm_aref, 1); rb_define_method(rb_cDBM, "fetch", fdbm_fetch_m, -1); rb_define_method(rb_cDBM, "[]=", fdbm_store, 2); rb_define_method(rb_cDBM, "store", fdbm_store, 2); rb_define_method(rb_cDBM, "index", fdbm_index, 1); rb_define_method(rb_cDBM, "key", fdbm_key, 1); rb_define_method(rb_cDBM, "select", fdbm_select, 0); rb_define_method(rb_cDBM, "values_at", fdbm_values_at, -1); rb_define_method(rb_cDBM, "length", fdbm_length, 0); rb_define_method(rb_cDBM, "size", fdbm_length, 0); rb_define_method(rb_cDBM, "empty?", fdbm_empty_p, 0); rb_define_method(rb_cDBM, "each", fdbm_each_pair, 0); rb_define_method(rb_cDBM, "each_value", fdbm_each_value, 0); rb_define_method(rb_cDBM, "each_key", fdbm_each_key, 0); rb_define_method(rb_cDBM, "each_pair", fdbm_each_pair, 0); rb_define_method(rb_cDBM, "keys", fdbm_keys, 0); rb_define_method(rb_cDBM, "values", fdbm_values, 0); rb_define_method(rb_cDBM, "shift", fdbm_shift, 0); rb_define_method(rb_cDBM, "delete", fdbm_delete, 1); rb_define_method(rb_cDBM, "delete_if", fdbm_delete_if, 0); rb_define_method(rb_cDBM, "reject!", fdbm_delete_if, 0); rb_define_method(rb_cDBM, "reject", fdbm_reject, 0); rb_define_method(rb_cDBM, "clear", fdbm_clear, 0); rb_define_method(rb_cDBM, "invert", fdbm_invert, 0); rb_define_method(rb_cDBM, "update", fdbm_update, 1); rb_define_method(rb_cDBM, "replace", fdbm_replace, 1); rb_define_method(rb_cDBM, "include?", fdbm_has_key, 1); rb_define_method(rb_cDBM, "has_key?", fdbm_has_key, 1); rb_define_method(rb_cDBM, "member?", fdbm_has_key, 1); rb_define_method(rb_cDBM, "has_value?", fdbm_has_value, 1); rb_define_method(rb_cDBM, "key?", fdbm_has_key, 1); rb_define_method(rb_cDBM, "value?", fdbm_has_value, 1); rb_define_method(rb_cDBM, "to_a", fdbm_to_a, 0); rb_define_method(rb_cDBM, "to_hash", fdbm_to_hash, 0); /* Indicates that dbm_open() should open the database in read-only mode */ rb_define_const(rb_cDBM, "READER", INT2FIX(O_RDONLY|RUBY_DBM_RW_BIT)); /* Indicates that dbm_open() should open the database in read/write mode */ rb_define_const(rb_cDBM, "WRITER", INT2FIX(O_RDWR|RUBY_DBM_RW_BIT)); /* Indicates that dbm_open() should open the database in read/write mode, * and create it if it does not already exist */ rb_define_const(rb_cDBM, "WRCREAT", INT2FIX(O_RDWR|O_CREAT|RUBY_DBM_RW_BIT)); /* Indicates that dbm_open() should open the database in read/write mode, * create it if it does not already exist, and delete all contents if it * does already exist. */ rb_define_const(rb_cDBM, "NEWDB", INT2FIX(O_RDWR|O_CREAT|O_TRUNC|RUBY_DBM_RW_BIT)); { VALUE version; #if defined(_DBM_IOERR) version = rb_str_new2("ndbm (4.3BSD)"); #elif defined(RUBYDBM_GDBM_HEADER) # if defined(HAVE_DECLARED_LIBVAR_GDBM_VERSION) /* since gdbm 1.9 */ version = rb_str_new2(gdbm_version); # elif defined(HAVE_UNDECLARED_LIBVAR_GDBM_VERSION) /* ndbm.h doesn't declare gdbm_version until gdbm 1.8.3. * See extconf.rb for more information. */ RUBY_EXTERN char *gdbm_version; version = rb_str_new2(gdbm_version); # else version = rb_str_new2("GDBM (unknown)"); # endif #elif defined(RUBYDBM_DB_HEADER) # if defined(HAVE_DB_VERSION) /* The version of the dbm library, if using Berkeley DB */ version = rb_str_new2(db_version(NULL, NULL, NULL)); # else version = rb_str_new2("Berkeley DB (unknown)"); # endif #elif defined(_RELIC_H) # if defined(HAVE_DPVERSION) version = rb_sprintf("QDBM %s", dpversion); # else version = rb_str_new2("QDBM (unknown)"); # endif #else version = rb_str_new2("ndbm (unknown)"); #endif /* * Identifies ndbm library version. * * Examples: * * - "ndbm (4.3BSD)" * - "Berkeley DB 4.8.30: (April 9, 2010)" * - "Berkeley DB (unknown)" (4.4BSD, maybe) * - "GDBM version 1.8.3. 10/15/2002 (built Jul 1 2011 12:32:45)" * - "QDBM 1.8.78" * */ rb_define_const(rb_cDBM, "VERSION", version); } } dbm-1.1.0/dbm.gemspec0000644000175000017500000000122314353676074014611 0ustar terceiroterceiro# frozen_string_literal: true Gem::Specification.new do |s| s.name = "dbm" s.version = '1.1.0' s.summary = "Provides a wrapper for the UNIX-style Database Manager Library" s.description = "Provides a wrapper for the UNIX-style Database Manager Library" s.require_path = %w{lib} s.files = %w{ext/dbm/extconf.rb ext/dbm/dbm.c} s.extensions = %w{ext/dbm/extconf.rb} s.required_ruby_version = ">= 2.3.0" s.authors = ["Yukihiro Matsumoto"] s.email = ["matz@ruby-lang.org"] s.homepage = "https://github.com/ruby/dbm" s.license = "BSD-2-Clause" s.add_development_dependency "rake-compiler" s.add_development_dependency "test-unit" end dbm-1.1.0/test/0000755000175000017500000000000014353676074013463 5ustar terceiroterceirodbm-1.1.0/test/dbm/0000755000175000017500000000000014353676074014225 5ustar terceiroterceirodbm-1.1.0/test/dbm/test_dbm.rb0000644000175000017500000003651014353676074016360 0ustar terceiroterceiro# frozen_string_literal: true require 'test/unit' require 'tmpdir' begin require 'dbm' rescue LoadError end if defined? DBM require 'tmpdir' require 'fileutils' class TestDBM_RDONLY < Test::Unit::TestCase def TestDBM_RDONLY.uname_s require 'rbconfig' case RbConfig::CONFIG['target_os'] when 'cygwin' require 'etc' Etc.uname[:sysname] else RbConfig::CONFIG['target_os'] end end SYSTEM = uname_s def setup @tmpdir = Dir.mktmpdir("tmptest_dbm") @prefix = "tmptest_dbm_#{$$}" @path = "#{@tmpdir}/#{@prefix}_" # prepare to make readonly DBM file DBM.open("#{@tmpdir}/#{@prefix}_rdonly") {|dbm| dbm['foo'] = 'FOO' } File.chmod(0400, *Dir.glob("#{@tmpdir}/#{@prefix}_rdonly.*")) assert_instance_of(DBM, @dbm_rdonly = DBM.new("#{@tmpdir}/#{@prefix}_rdonly", nil)) end def teardown assert_nil(@dbm_rdonly.close) ObjectSpace.each_object(DBM) do |obj| obj.close unless obj.closed? end FileUtils.remove_entry_secure @tmpdir end def test_delete_rdonly skip("skipped because root can read anything") if Process.uid == 0 if /^CYGWIN_9/ !~ SYSTEM assert_raise(DBMError) { @dbm_rdonly.delete("foo") } assert_nil(@dbm_rdonly.delete("bar")) end end def test_fetch_not_found notfound = nil result = Object.new assert_same(result, @dbm_rdonly.fetch("bar") {|k| notfound = k; result}) assert_equal("bar", notfound) end end class TestDBM < Test::Unit::TestCase def setup @tmpdir = Dir.mktmpdir("tmptest_dbm") @prefix = "tmptest_dbm_#{$$}" @path = "#{@tmpdir}/#{@prefix}_" assert_instance_of(DBM, @dbm = DBM.new(@path)) end def teardown assert_nil(@dbm.close) unless @dbm.closed? ObjectSpace.each_object(DBM) do |obj| obj.close unless obj.closed? end FileUtils.remove_entry_secure @tmpdir end def check_size(expect, dbm=@dbm) assert_equal(expect, dbm.size) n = 0 dbm.each { n+=1 } assert_equal(expect, n) if expect == 0 assert_equal(true, dbm.empty?) else assert_equal(false, dbm.empty?) end end def test_dbmfile_suffix @dbm.close prefix = File.basename(@path) suffixes = Dir.entries(@tmpdir).grep(/\A#{Regexp.escape prefix}/) { $' }.sort pagname = "#{@path}.pag" dirname = "#{@path}.dir" dbname = "#{@path}.db" case DBM::VERSION when /\bNDBM\b/ assert_equal(%w[.dir .pag], suffixes) assert(File.zero?(pagname)) assert(File.zero?(dirname)) when /\bGDBM\b/ assert_equal(%w[.dir .pag], suffixes) assert(!File.zero?(pagname)) assert(!File.zero?(dirname)) pag = File.binread(pagname, 16) pag_magics = [ 0x13579ace, # GDBM_OMAGIC 0x13579acd, # GDBM_MAGIC32 0x13579acf, # GDBM_MAGIC64 ] assert_operator(pag_magics, :include?, pag.unpack("i")[0]) # native endian, native int. if !File.identical?(pagname, dirname) dir = File.binread(dirname, 16) assert_equal("GDBM", dir[0, 4]) end when /\bBerkeley DB\b/ assert_equal(%w[.db], suffixes) assert(!File.zero?(dbname)) db = File.binread(dbname, 16) assert(db[0,4].unpack("N") == [0x00061561] || # Berkeley DB 1 db[12,4].unpack("L") == [0x00061561]) # Berkeley DBM 2 or later. when /\bQDBM\b/ assert_equal(%w[.dir .pag], suffixes) assert(!File.zero?(pagname)) assert(!File.zero?(dirname)) dir = File.binread(dirname, 16) assert_equal("[depot]\0\v", dir[0, 9]) pag = File.binread(pagname, 16) if [1].pack("s") == "\x00\x01" # big endian assert_equal("[DEPOT]\n\f", pag[0, 9]) else # little endian assert_equal("[depot]\n\f", pag[0, 9]) end end if suffixes == %w[.db] assert_match(/\bBerkeley DB\b/, DBM::VERSION) end end def test_s_new_has_no_block # DBM.new ignore the block foo = true assert_instance_of(DBM, dbm = DBM.new("#{@tmpdir}/#{@prefix}") { foo = false }) assert_equal(foo, true) assert_nil(dbm.close) end def test_s_open_no_create skip "dbm_open() is broken on libgdbm 1.8.0 or prior (#{DBM::VERSION})" if /GDBM version 1\.(?:[0-7]\b|8\.0)/ =~ DBM::VERSION assert_nil(dbm = DBM.open("#{@tmpdir}/#{@prefix}", nil)) ensure dbm.close if dbm end def test_s_open_with_block assert_equal(DBM.open("#{@tmpdir}/#{@prefix}") { :foo }, :foo) end def test_close assert_instance_of(DBM, dbm = DBM.open("#{@tmpdir}/#{@prefix}")) assert_nil(dbm.close) # closed DBM file assert_raise(DBMError) { dbm.close } end def test_aref assert_equal('bar', @dbm['foo'] = 'bar') assert_equal('bar', @dbm['foo']) assert_nil(@dbm['bar']) end def test_fetch assert_equal('bar', @dbm['foo']='bar') assert_equal('bar', @dbm.fetch('foo')) # key not found assert_raise(IndexError) { @dbm.fetch('bar') } # test for `ifnone' arg assert_equal('baz', @dbm.fetch('bar', 'baz')) # test for `ifnone' block assert_equal('foobar', @dbm.fetch('bar') {|key| 'foo' + key }) end def test_aset num = 0 2.times {|i| assert_equal('foo', @dbm['foo'] = 'foo') assert_equal('foo', @dbm['foo']) assert_equal('bar', @dbm['foo'] = 'bar') assert_equal('bar', @dbm['foo']) num += 1 if i == 0 assert_equal(num, @dbm.size) # assign nil assert_equal('', @dbm['bar'] = '') assert_equal('', @dbm['bar']) num += 1 if i == 0 assert_equal(num, @dbm.size) # empty string assert_equal('', @dbm[''] = '') assert_equal('', @dbm['']) num += 1 if i == 0 assert_equal(num, @dbm.size) # Integer assert_equal('200', @dbm['100'] = '200') assert_equal('200', @dbm['100']) num += 1 if i == 0 assert_equal(num, @dbm.size) # Big key and value assert_equal('y' * 100, @dbm['x' * 100] = 'y' * 100) assert_equal('y' * 100, @dbm['x' * 100]) num += 1 if i == 0 assert_equal(num, @dbm.size) } end def test_key assert_equal('bar', @dbm['foo'] = 'bar') assert_equal('foo', @dbm.key('bar')) assert_nil(@dbm['bar']) end def test_values_at keys = %w(foo bar baz) values = %w(FOO BAR BAZ) @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values assert_equal(values.reverse, @dbm.values_at(*keys.reverse)) end def test_select_with_block keys = %w(foo bar baz) values = %w(FOO BAR BAZ) @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values ret = @dbm.select {|k,v| assert_equal(k.upcase, v) k != "bar" } assert_equal([['baz', 'BAZ'], ['foo', 'FOO']], ret.sort) end def test_length num = 10 assert_equal(0, @dbm.size) num.times {|i| i = i.to_s @dbm[i] = i } assert_equal(num, @dbm.size) @dbm.shift assert_equal(num - 1, @dbm.size) end def test_empty? assert_equal(true, @dbm.empty?) @dbm['foo'] = 'FOO' assert_equal(false, @dbm.empty?) end def test_each_pair n = 0 @dbm.each_pair { n += 1 } assert_equal(0, n) keys = %w(foo bar baz) values = %w(FOO BAR BAZ) @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values n = 0 ret = @dbm.each_pair {|key, val| assert_not_nil(i = keys.index(key)) assert_equal(val, values[i]) n += 1 } assert_equal(keys.size, n) assert_equal(@dbm, ret) end def test_each_value n = 0 @dbm.each_value { n += 1 } assert_equal(0, n) keys = %w(foo bar baz) values = %w(FOO BAR BAZ) @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values n = 0 ret = @dbm.each_value {|val| assert_not_nil(key = @dbm.key(val)) assert_not_nil(i = keys.index(key)) assert_equal(val, values[i]) n += 1 } assert_equal(keys.size, n) assert_equal(@dbm, ret) end def test_each_key n = 0 @dbm.each_key { n += 1 } assert_equal(0, n) keys = %w(foo bar baz) values = %w(FOO BAR BAZ) @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values n = 0 ret = @dbm.each_key {|key| assert_not_nil(i = keys.index(key)) assert_equal(@dbm[key], values[i]) n += 1 } assert_equal(keys.size, n) assert_equal(@dbm, ret) end def test_keys assert_equal([], @dbm.keys) keys = %w(foo bar baz) values = %w(FOO BAR BAZ) @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values assert_equal(keys.sort, @dbm.keys.sort) assert_equal(values.sort, @dbm.values.sort) end def test_values test_keys end def test_shift assert_nil(@dbm.shift) assert_equal(0, @dbm.size) keys = %w(foo bar baz) values = %w(FOO BAR BAZ) @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values ret_keys = [] ret_values = [] while ret = @dbm.shift ret_keys.push ret[0] ret_values.push ret[1] assert_equal(keys.size - ret_keys.size, @dbm.size) end assert_equal(keys.sort, ret_keys.sort) assert_equal(values.sort, ret_values.sort) end def test_delete keys = %w(foo bar baz) values = %w(FOO BAR BAZ) key = keys[1] assert_nil(@dbm.delete(key)) assert_equal(0, @dbm.size) @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values assert_equal('BAR', @dbm.delete(key)) assert_nil(@dbm[key]) assert_equal(2, @dbm.size) assert_nil(@dbm.delete(key)) end def test_delete_with_block key = 'no called block' @dbm[key] = 'foo' assert_equal('foo', @dbm.delete(key) {|k| k.replace 'called block'; :blockval}) assert_equal(0, @dbm.size) key = 'no called block'.dup assert_equal(:blockval, @dbm.delete(key) {|k| k.replace 'called block'; :blockval}) assert_equal(0, @dbm.size) end def test_delete_if v = "0" 100.times {@dbm[v] = v; v = v.next} ret = @dbm.delete_if {|key, val| key.to_i < 50} assert_equal(@dbm, ret) check_size(50, @dbm) ret = @dbm.delete_if {|key, val| key.to_i >= 50} assert_equal(@dbm, ret) check_size(0, @dbm) # break v = "0" 100.times {@dbm[v] = v; v = v.next} check_size(100, @dbm) n = 0; @dbm.delete_if {|key, val| break if n > 50 n+=1 true } assert_equal(51, n) check_size(49, @dbm) @dbm.clear # raise v = "0" 100.times {@dbm[v] = v; v = v.next} check_size(100, @dbm) n = 0; begin @dbm.delete_if {|key, val| raise "runtime error" if n > 50 n+=1 true } rescue RuntimeError end assert_equal(51, n) check_size(49, @dbm) end def test_reject v = "0" 100.times {@dbm[v] = v; v = v.next} hash = @dbm.reject {|key, val| key.to_i < 50} assert_instance_of(Hash, hash) assert_equal(100, @dbm.size) assert_equal(50, hash.size) hash.each_pair {|key,val| assert_equal(false, key.to_i < 50) assert_equal(key, val) } hash = @dbm.reject {|key, val| key.to_i < 100} assert_instance_of(Hash, hash) assert_equal(true, hash.empty?) end def test_clear v = "1" 100.times {v = v.next; @dbm[v] = v} assert_equal(@dbm, @dbm.clear) # validate DBM#size i = 0 @dbm.each { i += 1 } assert_equal(@dbm.size, i) assert_equal(0, i) end def test_invert v = "0" 100.times {@dbm[v] = v; v = v.next} hash = @dbm.invert assert_instance_of(Hash, hash) assert_equal(100, hash.size) hash.each_pair {|key, val| assert_equal(key.to_i, val.to_i) } end def test_update hash = {} v = "0" 100.times {v = v.next; hash[v] = v} @dbm["101"] = "101" @dbm.update hash assert_equal(101, @dbm.size) @dbm.each_pair {|key, val| assert_equal(key.to_i, val.to_i) } end def test_replace hash = {} v = "0" 100.times {v = v.next; hash[v] = v} @dbm["101"] = "101" @dbm.replace hash assert_equal(100, @dbm.size) @dbm.each_pair {|key, val| assert_equal(key.to_i, val.to_i) } end def test_haskey? assert_equal('bar', @dbm['foo']='bar') assert_equal(true, @dbm.has_key?('foo')) assert_equal(false, @dbm.has_key?('bar')) end def test_has_value? assert_equal('bar', @dbm['foo']='bar') assert_equal(true, @dbm.has_value?('bar')) assert_equal(false, @dbm.has_value?('foo')) end def test_to_a v = "0" 100.times {v = v.next; @dbm[v] = v} ary = @dbm.to_a assert_instance_of(Array, ary) assert_equal(100, ary.size) ary.each {|key,val| assert_equal(key.to_i, val.to_i) } end def test_to_hash v = "0" 100.times {v = v.next; @dbm[v] = v} hash = @dbm.to_hash assert_instance_of(Hash, hash) assert_equal(100, hash.size) hash.each {|key,val| assert_equal(key.to_i, val.to_i) } end end class TestDBM2 < Test::Unit::TestCase def setup @tmproot = Dir.mktmpdir('ruby-dbm') end def teardown FileUtils.remove_entry_secure @tmproot if File.directory?(@tmproot) end def test_version assert_instance_of(String, DBM::VERSION) end def test_reader_open_notexist assert_raise(Errno::ENOENT) { DBM.open("#{@tmproot}/a", 0666, DBM::READER) } end def test_writer_open_notexist skip "dbm_open() is broken on libgdbm 1.8.0 or prior (#{DBM::VERSION})" if /GDBM version 1\.(?:[0-7]\b|8\.0)/ =~ DBM::VERSION assert_raise(Errno::ENOENT) { DBM.open("#{@tmproot}/a", 0666, DBM::WRITER) } end def test_wrcreat_open_notexist v = DBM.open("#{@tmproot}/a", 0666, DBM::WRCREAT) assert_instance_of(DBM, v) v.close end def test_newdb_open_notexist v = DBM.open("#{@tmproot}/a", 0666, DBM::NEWDB) assert_instance_of(DBM, v) v.close end def test_reader_open DBM.open("#{@tmproot}/a") {} # create a db. v = DBM.open("#{@tmproot}/a", nil, DBM::READER) {|d| # Errno::EPERM is raised on Solaris which use ndbm. # DBMError is raised on Debian which use gdbm. assert_raise(Errno::EPERM, DBMError) { d["k"] = "v" } true } assert(v) end def test_newdb_open DBM.open("#{@tmproot}/a") {|dbm| dbm["k"] = "v" } v = DBM.open("#{@tmproot}/a", nil, DBM::NEWDB) {|d| assert_equal(0, d.length) assert_nil(d["k"]) true } assert(v) end def test_freeze expected_error = defined?(FrozenError) ? FrozenError : RuntimeError DBM.open("#{@tmproot}/a") {|d| d.freeze assert_raise(expected_error) { d["k"] = "v" } } end end end dbm-1.1.0/README.md0000644000175000017500000000454314353676074013771 0ustar terceiroterceiro# DBM [![Build Status](https://travis-ci.org/ruby/dbm.svg?branch=master)](https://travis-ci.org/ruby/dbm) The DBM class provides a wrapper to a Unix-style [dbm](http://en.wikipedia.org/wiki/Dbm) or Database Manager library. Dbm databases do not have tables or columns; they are simple key-value data stores, like a Ruby Hash except not resident in RAM. Keys and values must be strings. The exact library used depends on how Ruby was compiled. It could be any of the following: * The original ndbm library is released in 4.3BSD. It is based on dbm library in Unix Version 7 but has different API to support multiple databases in a process. * [Berkeley DB](http://en.wikipedia.org/wiki/Berkeley_DB) versions 1 thru 5, also known as BDB and Sleepycat DB, now owned by Oracle Corporation. * Berkeley DB 1.x, still found in 4.4BSD derivatives (FreeBSD, OpenBSD, etc). * [gdbm](http://www.gnu.org/software/gdbm/), the GNU implementation of dbm. * [qdbm](http://fallabs.com/qdbm/index.html), another open source reimplementation of dbm. All of these dbm implementations have their own Ruby interfaces available, which provide richer (but varying) APIs. ## Installation Add this line to your application's Gemfile: ```ruby gem 'dbm' ``` And then execute: $ bundle Or install it yourself as: $ gem install dbm ## Usage ```ruby require 'dbm' db = DBM.open('rfcs', 0666, DBM::WRCREAT) db['822'] = 'Standard for the Format of ARPA Internet Text Messages' db['1123'] = 'Requirements for Internet Hosts - Application and Support' db['3068'] = 'An Anycast Prefix for 6to4 Relay Routers' puts db['822'] ``` ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/dbm. ## License The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause). dbm-1.1.0/Gemfile0000644000175000017500000000013014353676074013771 0ustar terceiroterceirosource 'https://rubygems.org' # Specify your gem's dependencies in dbm.gemspec gemspec dbm-1.1.0/.gitignore0000644000175000017500000000014214353676074014471 0ustar terceiroterceiro/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ dbm.bundle dbm-1.1.0/bin/0000755000175000017500000000000014353676074013254 5ustar terceiroterceirodbm-1.1.0/bin/setup0000755000175000017500000000020314353676074014335 0ustar terceiroterceiro#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here dbm-1.1.0/bin/console0000755000175000017500000000052214353676074014643 0ustar terceiroterceiro#!/usr/bin/env ruby require "bundler/setup" require "dbm" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require "irb" IRB.start(__FILE__) dbm-1.1.0/.travis.yml0000644000175000017500000000016014353676074014612 0ustar terceiroterceirosudo: false language: ruby rvm: - 2.3.7 - 2.4.4 - 2.5.1 - ruby-head before_install: gem install bundler dbm-1.1.0/Rakefile0000644000175000017500000000041714353676074014153 0ustar terceiroterceirorequire "bundler/gem_tasks" require "rake/testtask" Rake::TestTask.new(:test) do |t| t.libs << "test" t.libs << "lib" t.test_files = FileList['test/**/test_*.rb'] end require 'rake/extensiontask' Rake::ExtensionTask.new("dbm") task :default => [:compile, :test]