Ruby  2.0.0p247(2013-06-27revision41674)
ext/dbm/dbm.c
Go to the documentation of this file.
00001 /************************************************
00002 
00003   dbm.c -
00004 
00005   $Author: nobu $
00006   created at: Mon Jan 24 15:59:52 JST 1994
00007 
00008   Copyright (C) 1995-2001 Yukihiro Matsumoto
00009 
00010 ************************************************/
00011 
00012 #include "ruby.h"
00013 
00014 #ifdef HAVE_CDEFS_H
00015 # include <cdefs.h>
00016 #endif
00017 #ifdef HAVE_SYS_CDEFS_H
00018 # include <sys/cdefs.h>
00019 #endif
00020 #include DBM_HDR
00021 #include <fcntl.h>
00022 #include <errno.h>
00023 
00024 #define DSIZE_TYPE TYPEOF_DATUM_DSIZE
00025 #if SIZEOF_DATUM_DSIZE > SIZEOF_INT
00026 # define RSTRING_DSIZE(s) RSTRING_LEN(s)
00027 # define TOO_LONG(n) 0
00028 #else
00029 # define RSTRING_DSIZE(s) RSTRING_LENINT(s)
00030 # define TOO_LONG(n) ((long)(+(DSIZE_TYPE)(n)) != (n))
00031 #endif
00032 
00033 static VALUE rb_cDBM, rb_eDBMError;
00034 
00035 #define RUBY_DBM_RW_BIT 0x20000000
00036 
00037 struct dbmdata {
00038     long di_size;
00039     DBM *di_dbm;
00040 };
00041 
00042 static void
00043 closed_dbm(void)
00044 {
00045     rb_raise(rb_eDBMError, "closed DBM file");
00046 }
00047 
00048 #define GetDBM(obj, dbmp) {\
00049     Data_Get_Struct((obj), struct dbmdata, (dbmp));\
00050     if ((dbmp) == 0) closed_dbm();\
00051     if ((dbmp)->di_dbm == 0) closed_dbm();\
00052 }
00053 
00054 #define GetDBM2(obj, data, dbm) {\
00055     GetDBM((obj), (data));\
00056     (dbm) = dbmp->di_dbm;\
00057 }
00058 
00059 static void
00060 free_dbm(struct dbmdata *dbmp)
00061 {
00062     if (dbmp) {
00063         if (dbmp->di_dbm) dbm_close(dbmp->di_dbm);
00064         xfree(dbmp);
00065     }
00066 }
00067 
00068 /*
00069  * call-seq:
00070  *   dbm.close
00071  *
00072  * Closes the database.
00073  */
00074 static VALUE
00075 fdbm_close(VALUE obj)
00076 {
00077     struct dbmdata *dbmp;
00078 
00079     GetDBM(obj, dbmp);
00080     dbm_close(dbmp->di_dbm);
00081     dbmp->di_dbm = 0;
00082 
00083     return Qnil;
00084 }
00085 
00086 /*
00087  * call-seq:
00088  *   dbm.closed? -> true or false
00089  *
00090  * Returns true if the database is closed, false otherwise.
00091  */
00092 static VALUE
00093 fdbm_closed(VALUE obj)
00094 {
00095     struct dbmdata *dbmp;
00096 
00097     Data_Get_Struct(obj, struct dbmdata, dbmp);
00098     if (dbmp == 0)
00099         return Qtrue;
00100     if (dbmp->di_dbm == 0)
00101         return Qtrue;
00102 
00103     return Qfalse;
00104 }
00105 
00106 static VALUE
00107 fdbm_alloc(VALUE klass)
00108 {
00109     return Data_Wrap_Struct(klass, 0, free_dbm, 0);
00110 }
00111 
00112 /*
00113  * call-seq:
00114  *   DBM.new(filename[, mode[, flags]]) -> dbm
00115  *
00116  * Open a dbm database with the specified name, which can include a directory
00117  * path. Any file extensions needed will be supplied automatically by the dbm
00118  * library. For example, Berkeley DB appends '.db', and GNU gdbm uses two
00119  * physical files with extensions '.dir' and '.pag'.
00120  *
00121  * The mode should be an integer, as for Unix chmod.
00122  *
00123  * Flags should be one of READER, WRITER, WRCREAT or NEWDB.
00124  */
00125 static VALUE
00126 fdbm_initialize(int argc, VALUE *argv, VALUE obj)
00127 {
00128     volatile VALUE file;
00129     VALUE vmode, vflags;
00130     DBM *dbm;
00131     struct dbmdata *dbmp;
00132     int mode, flags = 0;
00133 
00134     if (rb_scan_args(argc, argv, "12", &file, &vmode, &vflags) == 1) {
00135         mode = 0666;            /* default value */
00136     }
00137     else if (NIL_P(vmode)) {
00138         mode = -1;              /* return nil if DB not exist */
00139     }
00140     else {
00141         mode = NUM2INT(vmode);
00142     }
00143 
00144     if (!NIL_P(vflags))
00145         flags = NUM2INT(vflags);
00146 
00147     FilePathValue(file);
00148 
00149     /*
00150      * Note:
00151      * gdbm 1.10 works with O_CLOEXEC.  gdbm 1.9.1 silently ignore it.
00152      */
00153 #ifndef O_CLOEXEC
00154 #   define O_CLOEXEC 0
00155 #endif
00156 
00157     if (flags & RUBY_DBM_RW_BIT) {
00158         flags &= ~RUBY_DBM_RW_BIT;
00159         dbm = dbm_open(RSTRING_PTR(file), flags|O_CLOEXEC, mode);
00160     }
00161     else {
00162         dbm = 0;
00163         if (mode >= 0) {
00164             dbm = dbm_open(RSTRING_PTR(file), O_RDWR|O_CREAT|O_CLOEXEC, mode);
00165         }
00166         if (!dbm) {
00167             dbm = dbm_open(RSTRING_PTR(file), O_RDWR|O_CLOEXEC, 0);
00168         }
00169         if (!dbm) {
00170             dbm = dbm_open(RSTRING_PTR(file), O_RDONLY|O_CLOEXEC, 0);
00171         }
00172     }
00173 
00174     if (dbm) {
00175     /*
00176      * History of dbm_pagfno() and dbm_dirfno() in ndbm and its compatibles.
00177      * (dbm_pagfno() and dbm_dirfno() is not standardized.)
00178      *
00179      * 1986: 4.3BSD provides ndbm.
00180      *       It provides dbm_pagfno() and dbm_dirfno() as macros.
00181      * 1991: gdbm-1.5 provides them as functions.
00182      *       They returns a same descriptor.
00183      *       (Earlier releases may have the functions too.)
00184      * 1991: Net/2 provides Berkeley DB.
00185      *       It doesn't provide dbm_pagfno() and dbm_dirfno().
00186      * 1992: 4.4BSD Alpha provides Berkeley DB with dbm_dirfno() as a function.
00187      *       dbm_pagfno() is a macro as DBM_PAGFNO_NOT_AVAILABLE.
00188      * 1997: Berkeley DB 2.0 is released by Sleepycat Software, Inc.
00189      *       It defines dbm_pagfno() and dbm_dirfno() as macros.
00190      * 2011: gdbm-1.9 creates a separate dir file.
00191      *       dbm_pagfno() and dbm_dirfno() returns different descriptors.
00192      */
00193 #if defined(HAVE_DBM_PAGFNO)
00194         rb_fd_fix_cloexec(dbm_pagfno(dbm));
00195 #endif
00196 #if defined(HAVE_DBM_DIRFNO)
00197         rb_fd_fix_cloexec(dbm_dirfno(dbm));
00198 #endif
00199 
00200 #if defined(RUBYDBM_DB_HEADER) && defined(HAVE_TYPE_DBC)
00201     /* Disable Berkeley DB error messages such as:
00202      * DB->put: attempt to modify a read-only database */
00203         ((DBC*)dbm)->dbp->set_errfile(((DBC*)dbm)->dbp, NULL);
00204 #endif
00205     }
00206 
00207     if (!dbm) {
00208         if (mode == -1) return Qnil;
00209         rb_sys_fail_str(file);
00210     }
00211 
00212     dbmp = ALLOC(struct dbmdata);
00213     DATA_PTR(obj) = dbmp;
00214     dbmp->di_dbm = dbm;
00215     dbmp->di_size = -1;
00216 
00217     return obj;
00218 }
00219 
00220 /*
00221  * call-seq:
00222  *   DBM.open(filename[, mode[, flags]]) -> dbm
00223  *   DBM.open(filename[, mode[, flags]]) {|dbm| block}
00224  *
00225  * Open a dbm database and yields it if a block is given. See also
00226  * <code>DBM.new</code>.
00227  */
00228 static VALUE
00229 fdbm_s_open(int argc, VALUE *argv, VALUE klass)
00230 {
00231     VALUE obj = Data_Wrap_Struct(klass, 0, free_dbm, 0);
00232 
00233     if (NIL_P(fdbm_initialize(argc, argv, obj))) {
00234         return Qnil;
00235     }
00236 
00237     if (rb_block_given_p()) {
00238         return rb_ensure(rb_yield, obj, fdbm_close, obj);
00239     }
00240 
00241     return obj;
00242 }
00243 
00244 static VALUE
00245 fdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone)
00246 {
00247     datum key, value;
00248     struct dbmdata *dbmp;
00249     DBM *dbm;
00250     long len;
00251 
00252     ExportStringValue(keystr);
00253     len = RSTRING_LEN(keystr);
00254     if (TOO_LONG(len)) goto not_found;
00255     key.dptr = RSTRING_PTR(keystr);
00256     key.dsize = (DSIZE_TYPE)len;
00257 
00258     GetDBM2(obj, dbmp, dbm);
00259     value = dbm_fetch(dbm, key);
00260     if (value.dptr == 0) {
00261       not_found:
00262         if (ifnone == Qnil && rb_block_given_p())
00263             return rb_yield(rb_tainted_str_new(key.dptr, key.dsize));
00264         return ifnone;
00265     }
00266     return rb_tainted_str_new(value.dptr, value.dsize);
00267 }
00268 
00269 /*
00270  * call-seq:
00271  *   dbm[key] -> string value or nil
00272  *
00273  * Return a value from the database by locating the key string
00274  * provided.  If the key is not found, returns nil.
00275  */
00276 static VALUE
00277 fdbm_aref(VALUE obj, VALUE keystr)
00278 {
00279     return fdbm_fetch(obj, keystr, Qnil);
00280 }
00281 
00282 /*
00283  * call-seq:
00284  *   dbm.fetch(key[, ifnone]) -> value
00285  *
00286  * Return a value from the database by locating the key string
00287  * provided.  If the key is not found, returns +ifnone+. If +ifnone+
00288  * is not given, raises IndexError.
00289  */
00290 static VALUE
00291 fdbm_fetch_m(int argc, VALUE *argv, VALUE obj)
00292 {
00293     VALUE keystr, valstr, ifnone;
00294 
00295     rb_scan_args(argc, argv, "11", &keystr, &ifnone);
00296     valstr = fdbm_fetch(obj, keystr, ifnone);
00297     if (argc == 1 && !rb_block_given_p() && NIL_P(valstr))
00298         rb_raise(rb_eIndexError, "key not found");
00299 
00300     return valstr;
00301 }
00302 
00303 /*
00304  * call-seq:
00305  *   dbm.key(value) -> string
00306  *
00307  * Returns the key for the specified value.
00308  */
00309 static VALUE
00310 fdbm_key(VALUE obj, VALUE valstr)
00311 {
00312     datum key, val;
00313     struct dbmdata *dbmp;
00314     DBM *dbm;
00315     long len;
00316 
00317     ExportStringValue(valstr);
00318     len = RSTRING_LEN(valstr);
00319     if (TOO_LONG(len)) return Qnil;
00320     val.dptr = RSTRING_PTR(valstr);
00321     val.dsize = (DSIZE_TYPE)len;
00322 
00323     GetDBM2(obj, dbmp, dbm);
00324     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00325         val = dbm_fetch(dbm, key);
00326         if ((long)val.dsize == RSTRING_LEN(valstr) &&
00327             memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0) {
00328             return rb_tainted_str_new(key.dptr, key.dsize);
00329         }
00330     }
00331     return Qnil;
00332 }
00333 
00334 /* :nodoc: */
00335 static VALUE
00336 fdbm_index(VALUE hash, VALUE value)
00337 {
00338     rb_warn("DBM#index is deprecated; use DBM#key");
00339     return fdbm_key(hash, value);
00340 }
00341 
00342 /*
00343  * call-seq:
00344  *   dbm.select {|key, value| block} -> array
00345  *
00346  * Returns a new array consisting of the [key, value] pairs for which the code
00347  * block returns true.
00348  */
00349 static VALUE
00350 fdbm_select(VALUE obj)
00351 {
00352     VALUE new = rb_ary_new();
00353     datum key, val;
00354     DBM *dbm;
00355     struct dbmdata *dbmp;
00356 
00357     GetDBM2(obj, dbmp, dbm);
00358     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00359         VALUE assoc, v;
00360         val = dbm_fetch(dbm, key);
00361         assoc = rb_assoc_new(rb_tainted_str_new(key.dptr, key.dsize),
00362                              rb_tainted_str_new(val.dptr, val.dsize));
00363         v = rb_yield(assoc);
00364         if (RTEST(v)) {
00365             rb_ary_push(new, assoc);
00366         }
00367         GetDBM2(obj, dbmp, dbm);
00368     }
00369 
00370     return new;
00371 }
00372 
00373 /*
00374  * call-seq:
00375  *   dbm.values_at(key, ...) -> Array
00376  *
00377  * Returns an array containing the values associated with the given keys.
00378  */
00379 static VALUE
00380 fdbm_values_at(int argc, VALUE *argv, VALUE obj)
00381 {
00382     VALUE new = rb_ary_new2(argc);
00383     int i;
00384 
00385     for (i=0; i<argc; i++) {
00386         rb_ary_push(new, fdbm_fetch(obj, argv[i], Qnil));
00387     }
00388 
00389     return new;
00390 }
00391 
00392 static void
00393 fdbm_modify(VALUE obj)
00394 {
00395     rb_secure(4);
00396     if (OBJ_FROZEN(obj)) rb_error_frozen("DBM");
00397 }
00398 
00399 /*
00400  * call-seq:
00401  *   dbm.delete(key)
00402  *
00403  * Deletes an entry from the database.
00404  */
00405 static VALUE
00406 fdbm_delete(VALUE obj, VALUE keystr)
00407 {
00408     datum key, value;
00409     struct dbmdata *dbmp;
00410     DBM *dbm;
00411     VALUE valstr;
00412     long len;
00413 
00414     fdbm_modify(obj);
00415     ExportStringValue(keystr);
00416     len = RSTRING_LEN(keystr);
00417     if (TOO_LONG(len)) goto not_found;
00418     key.dptr = RSTRING_PTR(keystr);
00419     key.dsize = (DSIZE_TYPE)len;
00420 
00421     GetDBM2(obj, dbmp, dbm);
00422 
00423     value = dbm_fetch(dbm, key);
00424     if (value.dptr == 0) {
00425       not_found:
00426         if (rb_block_given_p()) return rb_yield(keystr);
00427         return Qnil;
00428     }
00429 
00430     /* need to save value before dbm_delete() */
00431     valstr = rb_tainted_str_new(value.dptr, value.dsize);
00432 
00433     if (dbm_delete(dbm, key)) {
00434         dbmp->di_size = -1;
00435         rb_raise(rb_eDBMError, "dbm_delete failed");
00436     }
00437     else if (dbmp->di_size >= 0) {
00438         dbmp->di_size--;
00439     }
00440     return valstr;
00441 }
00442 
00443 /*
00444  * call-seq:
00445  *   dbm.shift() -> [key, value]
00446  *
00447  * Removes a [key, value] pair from the database, and returns it.
00448  * If the database is empty, returns nil.
00449  * The order in which values are removed/returned is not guaranteed.
00450  */
00451 static VALUE
00452 fdbm_shift(VALUE obj)
00453 {
00454     datum key, val;
00455     struct dbmdata *dbmp;
00456     DBM *dbm;
00457     VALUE keystr, valstr;
00458 
00459     fdbm_modify(obj);
00460     GetDBM2(obj, dbmp, dbm);
00461     dbmp->di_size = -1;
00462 
00463     key = dbm_firstkey(dbm);
00464     if (!key.dptr) return Qnil;
00465     val = dbm_fetch(dbm, key);
00466     keystr = rb_tainted_str_new(key.dptr, key.dsize);
00467     valstr = rb_tainted_str_new(val.dptr, val.dsize);
00468     dbm_delete(dbm, key);
00469 
00470     return rb_assoc_new(keystr, valstr);
00471 }
00472 
00473 /*
00474  * call-seq:
00475  *   dbm.reject! {|key, value| block} -> self
00476  *   dbm.delete_if {|key, value| block} -> self
00477  *
00478  * Deletes all entries for which the code block returns true.
00479  * Returns self.
00480  */
00481 static VALUE
00482 fdbm_delete_if(VALUE obj)
00483 {
00484     datum key, val;
00485     struct dbmdata *dbmp;
00486     DBM *dbm;
00487     VALUE keystr, valstr;
00488     VALUE ret, ary = rb_ary_tmp_new(0);
00489     int i, status = 0;
00490     long n;
00491 
00492     fdbm_modify(obj);
00493     GetDBM2(obj, dbmp, dbm);
00494     n = dbmp->di_size;
00495     dbmp->di_size = -1;
00496 
00497     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00498         val = dbm_fetch(dbm, key);
00499         keystr = rb_tainted_str_new(key.dptr, key.dsize);
00500         OBJ_FREEZE(keystr);
00501         valstr = rb_tainted_str_new(val.dptr, val.dsize);
00502         ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status);
00503         if (status != 0) break;
00504         if (RTEST(ret)) rb_ary_push(ary, keystr);
00505         GetDBM2(obj, dbmp, dbm);
00506     }
00507 
00508     for (i = 0; i < RARRAY_LEN(ary); i++) {
00509         keystr = RARRAY_PTR(ary)[i];
00510         key.dptr = RSTRING_PTR(keystr);
00511         key.dsize = (DSIZE_TYPE)RSTRING_LEN(keystr);
00512         if (dbm_delete(dbm, key)) {
00513             rb_raise(rb_eDBMError, "dbm_delete failed");
00514         }
00515     }
00516     if (status) rb_jump_tag(status);
00517     if (n > 0) dbmp->di_size = n - RARRAY_LEN(ary);
00518     rb_ary_clear(ary);
00519 
00520     return obj;
00521 }
00522 
00523 /*
00524  * call-seq:
00525  *   dbm.clear
00526  *
00527  * Deletes all data from the database.
00528  */
00529 static VALUE
00530 fdbm_clear(VALUE obj)
00531 {
00532     datum key;
00533     struct dbmdata *dbmp;
00534     DBM *dbm;
00535 
00536     fdbm_modify(obj);
00537     GetDBM2(obj, dbmp, dbm);
00538     dbmp->di_size = -1;
00539     while (key = dbm_firstkey(dbm), key.dptr) {
00540         if (dbm_delete(dbm, key)) {
00541             rb_raise(rb_eDBMError, "dbm_delete failed");
00542         }
00543     }
00544     dbmp->di_size = 0;
00545 
00546     return obj;
00547 }
00548 
00549 /*
00550  * call-seq:
00551  *   dbm.invert -> hash
00552  *
00553  * Returns a Hash (not a DBM database) created by using each value in the
00554  * database as a key, with the corresponding key as its value.
00555  */
00556 static VALUE
00557 fdbm_invert(VALUE obj)
00558 {
00559     datum key, val;
00560     struct dbmdata *dbmp;
00561     DBM *dbm;
00562     VALUE keystr, valstr;
00563     VALUE hash = rb_hash_new();
00564 
00565     GetDBM2(obj, dbmp, dbm);
00566     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00567         val = dbm_fetch(dbm, key);
00568         keystr = rb_tainted_str_new(key.dptr, key.dsize);
00569         valstr = rb_tainted_str_new(val.dptr, val.dsize);
00570         rb_hash_aset(hash, valstr, keystr);
00571     }
00572     return hash;
00573 }
00574 
00575 static VALUE fdbm_store(VALUE,VALUE,VALUE);
00576 
00577 static VALUE
00578 update_i(VALUE pair, VALUE dbm)
00579 {
00580     Check_Type(pair, T_ARRAY);
00581     if (RARRAY_LEN(pair) < 2) {
00582         rb_raise(rb_eArgError, "pair must be [key, value]");
00583     }
00584     fdbm_store(dbm, RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1]);
00585     return Qnil;
00586 }
00587 
00588 /*
00589  * call-seq:
00590  *   dbm.update(obj)
00591  *
00592  * Updates the database with multiple values from the specified object.
00593  * Takes any object which implements the each_pair method, including
00594  * Hash and DBM objects.
00595  */
00596 static VALUE
00597 fdbm_update(VALUE obj, VALUE other)
00598 {
00599     rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
00600     return obj;
00601 }
00602 
00603 /*
00604  * call-seq:
00605  *   dbm.replace(obj)
00606  *
00607  * Replaces the contents of the database with the contents of the specified
00608  * object. Takes any object which implements the each_pair method, including
00609  * Hash and DBM objects.
00610  */
00611 static VALUE
00612 fdbm_replace(VALUE obj, VALUE other)
00613 {
00614     fdbm_clear(obj);
00615     rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
00616     return obj;
00617 }
00618 
00619 /*
00620  * call-seq:
00621  *   dbm.store(key, value) -> value
00622  *   dbm[key] = value
00623  *
00624  * Stores the specified string value in the database, indexed via the
00625  * string key provided.
00626  */
00627 static VALUE
00628 fdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
00629 {
00630     datum key, val;
00631     struct dbmdata *dbmp;
00632     DBM *dbm;
00633 
00634     fdbm_modify(obj);
00635     keystr = rb_obj_as_string(keystr);
00636     valstr = rb_obj_as_string(valstr);
00637 
00638     key.dptr = RSTRING_PTR(keystr);
00639     key.dsize = RSTRING_DSIZE(keystr);
00640 
00641     val.dptr = RSTRING_PTR(valstr);
00642     val.dsize = RSTRING_DSIZE(valstr);
00643 
00644     GetDBM2(obj, dbmp, dbm);
00645     dbmp->di_size = -1;
00646     if (dbm_store(dbm, key, val, DBM_REPLACE)) {
00647         dbm_clearerr(dbm);
00648         if (errno == EPERM) rb_sys_fail(0);
00649         rb_raise(rb_eDBMError, "dbm_store failed");
00650     }
00651 
00652     return valstr;
00653 }
00654 
00655 /*
00656  * call-seq:
00657  *   dbm.length -> integer
00658  *
00659  * Returns the number of entries in the database.
00660  */
00661 static VALUE
00662 fdbm_length(VALUE obj)
00663 {
00664     datum key;
00665     struct dbmdata *dbmp;
00666     DBM *dbm;
00667     int i = 0;
00668 
00669     GetDBM2(obj, dbmp, dbm);
00670     if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
00671 
00672     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00673         i++;
00674     }
00675     dbmp->di_size = i;
00676 
00677     return INT2FIX(i);
00678 }
00679 
00680 /*
00681  * call-seq:
00682  *   dbm.empty?
00683  *
00684  * Returns true if the database is empty, false otherwise.
00685  */
00686 static VALUE
00687 fdbm_empty_p(VALUE obj)
00688 {
00689     datum key;
00690     struct dbmdata *dbmp;
00691     DBM *dbm;
00692 
00693     GetDBM2(obj, dbmp, dbm);
00694     if (dbmp->di_size < 0) {
00695         dbm = dbmp->di_dbm;
00696 
00697         for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00698             return Qfalse;
00699         }
00700     }
00701     else {
00702         if (dbmp->di_size)
00703             return Qfalse;
00704     }
00705     return Qtrue;
00706 }
00707 
00708 /*
00709  * call-seq:
00710  *   dbm.each_value {|value| block} -> self
00711  *
00712  * Calls the block once for each value string in the database. Returns self.
00713  */
00714 static VALUE
00715 fdbm_each_value(VALUE obj)
00716 {
00717     datum key, val;
00718     struct dbmdata *dbmp;
00719     DBM *dbm;
00720 
00721     RETURN_ENUMERATOR(obj, 0, 0);
00722 
00723     GetDBM2(obj, dbmp, dbm);
00724     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00725         val = dbm_fetch(dbm, key);
00726         rb_yield(rb_tainted_str_new(val.dptr, val.dsize));
00727         GetDBM2(obj, dbmp, dbm);
00728     }
00729     return obj;
00730 }
00731 
00732 /*
00733  * call-seq:
00734  *   dbm.each_key {|key| block} -> self
00735  *
00736  * Calls the block once for each key string in the database. Returns self.
00737  */
00738 static VALUE
00739 fdbm_each_key(VALUE obj)
00740 {
00741     datum key;
00742     struct dbmdata *dbmp;
00743     DBM *dbm;
00744 
00745     RETURN_ENUMERATOR(obj, 0, 0);
00746 
00747     GetDBM2(obj, dbmp, dbm);
00748     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00749         rb_yield(rb_tainted_str_new(key.dptr, key.dsize));
00750         GetDBM2(obj, dbmp, dbm);
00751     }
00752     return obj;
00753 }
00754 
00755 /*
00756  * call-seq:
00757  *   dbm.each_pair {|key,value| block} -> self
00758  *
00759  * Calls the block once for each [key, value] pair in the database.
00760  * Returns self.
00761  */
00762 static VALUE
00763 fdbm_each_pair(VALUE obj)
00764 {
00765     datum key, val;
00766     DBM *dbm;
00767     struct dbmdata *dbmp;
00768     VALUE keystr, valstr;
00769 
00770     RETURN_ENUMERATOR(obj, 0, 0);
00771 
00772     GetDBM2(obj, dbmp, dbm);
00773 
00774     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00775         val = dbm_fetch(dbm, key);
00776         keystr = rb_tainted_str_new(key.dptr, key.dsize);
00777         valstr = rb_tainted_str_new(val.dptr, val.dsize);
00778         rb_yield(rb_assoc_new(keystr, valstr));
00779         GetDBM2(obj, dbmp, dbm);
00780     }
00781 
00782     return obj;
00783 }
00784 
00785 /*
00786  * call-seq:
00787  *   dbm.keys -> array
00788  *
00789  * Returns an array of all the string keys in the database.
00790  */
00791 static VALUE
00792 fdbm_keys(VALUE obj)
00793 {
00794     datum key;
00795     struct dbmdata *dbmp;
00796     DBM *dbm;
00797     VALUE ary;
00798 
00799     GetDBM2(obj, dbmp, dbm);
00800 
00801     ary = rb_ary_new();
00802     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00803         rb_ary_push(ary, rb_tainted_str_new(key.dptr, key.dsize));
00804     }
00805 
00806     return ary;
00807 }
00808 
00809 /*
00810  * call-seq:
00811  *   dbm.values -> array
00812  *
00813  * Returns an array of all the string values in the database.
00814  */
00815 static VALUE
00816 fdbm_values(VALUE obj)
00817 {
00818     datum key, val;
00819     struct dbmdata *dbmp;
00820     DBM *dbm;
00821     VALUE ary;
00822 
00823     GetDBM2(obj, dbmp, dbm);
00824     ary = rb_ary_new();
00825     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00826         val = dbm_fetch(dbm, key);
00827         rb_ary_push(ary, rb_tainted_str_new(val.dptr, val.dsize));
00828     }
00829 
00830     return ary;
00831 }
00832 
00833 /*
00834  * call-seq:
00835  *   dbm.has_key?(key) -> boolean
00836  *
00837  * Returns true if the database contains the specified key, false otherwise.
00838  */
00839 static VALUE
00840 fdbm_has_key(VALUE obj, VALUE keystr)
00841 {
00842     datum key, val;
00843     struct dbmdata *dbmp;
00844     DBM *dbm;
00845     long len;
00846 
00847     ExportStringValue(keystr);
00848     len = RSTRING_LEN(keystr);
00849     if (TOO_LONG(len)) return Qfalse;
00850     key.dptr = RSTRING_PTR(keystr);
00851     key.dsize = (DSIZE_TYPE)len;
00852 
00853     GetDBM2(obj, dbmp, dbm);
00854     val = dbm_fetch(dbm, key);
00855     if (val.dptr) return Qtrue;
00856     return Qfalse;
00857 }
00858 
00859 /*
00860  * call-seq:
00861  *   dbm.has_value?(value) -> boolean
00862  *
00863  * Returns true if the database contains the specified string value, false
00864  * otherwise.
00865  */
00866 static VALUE
00867 fdbm_has_value(VALUE obj, VALUE valstr)
00868 {
00869     datum key, val;
00870     struct dbmdata *dbmp;
00871     DBM *dbm;
00872     long len;
00873 
00874     ExportStringValue(valstr);
00875     len = RSTRING_LEN(valstr);
00876     if (TOO_LONG(len)) return Qfalse;
00877     val.dptr = RSTRING_PTR(valstr);
00878     val.dsize = (DSIZE_TYPE)len;
00879 
00880     GetDBM2(obj, dbmp, dbm);
00881     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00882         val = dbm_fetch(dbm, key);
00883         if ((DSIZE_TYPE)val.dsize == (DSIZE_TYPE)RSTRING_LEN(valstr) &&
00884             memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0)
00885             return Qtrue;
00886     }
00887     return Qfalse;
00888 }
00889 
00890 /*
00891  * call-seq:
00892  *   dbm.to_a -> array
00893  *
00894  * Converts the contents of the database to an array of [key, value] arrays,
00895  * and returns it.
00896  */
00897 static VALUE
00898 fdbm_to_a(VALUE obj)
00899 {
00900     datum key, val;
00901     struct dbmdata *dbmp;
00902     DBM *dbm;
00903     VALUE ary;
00904 
00905     GetDBM2(obj, dbmp, dbm);
00906     ary = rb_ary_new();
00907     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00908         val = dbm_fetch(dbm, key);
00909         rb_ary_push(ary, rb_assoc_new(rb_tainted_str_new(key.dptr, key.dsize),
00910                                       rb_tainted_str_new(val.dptr, val.dsize)));
00911     }
00912 
00913     return ary;
00914 }
00915 
00916 /*
00917  * call-seq:
00918  *   dbm.to_hash -> hash
00919  *
00920  * Converts the contents of the database to an in-memory Hash object, and
00921  * returns it.
00922  */
00923 static VALUE
00924 fdbm_to_hash(VALUE obj)
00925 {
00926     datum key, val;
00927     struct dbmdata *dbmp;
00928     DBM *dbm;
00929     VALUE hash;
00930 
00931     GetDBM2(obj, dbmp, dbm);
00932     hash = rb_hash_new();
00933     for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
00934         val = dbm_fetch(dbm, key);
00935         rb_hash_aset(hash, rb_tainted_str_new(key.dptr, key.dsize),
00936                            rb_tainted_str_new(val.dptr, val.dsize));
00937     }
00938 
00939     return hash;
00940 }
00941 
00942 /*
00943  * call-seq:
00944  *   dbm.reject {|key,value| block} -> Hash
00945  *
00946  * Converts the contents of the database to an in-memory Hash, then calls
00947  * Hash#reject with the specified code block, returning a new Hash.
00948  */
00949 static VALUE
00950 fdbm_reject(VALUE obj)
00951 {
00952     return rb_hash_delete_if(fdbm_to_hash(obj));
00953 }
00954 
00955 /*
00956  * Documented by mathew meta@pobox.com.
00957  * = Introduction
00958  *
00959  * The DBM class provides a wrapper to a Unix-style
00960  * {dbm}[http://en.wikipedia.org/wiki/Dbm] or Database Manager library.
00961  *
00962  * Dbm databases do not have tables or columns; they are simple key-value
00963  * data stores, like a Ruby Hash except not resident in RAM. Keys and values
00964  * must be strings.
00965  *
00966  * The exact library used depends on how Ruby was compiled. It could be any
00967  * of the following:
00968  *
00969  * - The original ndbm library is released in 4.3BSD.
00970  *   It is based on dbm library in Unix Version 7 but has different API to
00971  *   support multiple databases in a process.
00972  * - {Berkeley DB}[http://en.wikipedia.org/wiki/Berkeley_DB] versions
00973  *   1 thru 5, also known as BDB and Sleepycat DB, now owned by Oracle
00974  *   Corporation.
00975  * - Berkeley DB 1.x, still found in 4.4BSD derivatives (FreeBSD, OpenBSD, etc).
00976  * - {gdbm}[http://www.gnu.org/software/gdbm/], the GNU implementation of dbm.
00977  * - {qdbm}[http://fallabs.com/qdbm/index.html], another open source
00978  *   reimplementation of dbm.
00979  *
00980  * All of these dbm implementations have their own Ruby interfaces
00981  * available, which provide richer (but varying) APIs.
00982  *
00983  * = Cautions
00984  *
00985  * Before you decide to use DBM, there are some issues you should consider:
00986  *
00987  * - Each implementation of dbm has its own file format. Generally, dbm
00988  *   libraries will not read each other's files. This makes dbm files
00989  *   a bad choice for data exchange.
00990  *
00991  * - Even running the same OS and the same dbm implementation, the database
00992  *   file format may depend on the CPU architecture. For example, files may
00993  *   not be portable between PowerPC and 386, or between 32 and 64 bit Linux.
00994  *
00995  * - Different versions of Berkeley DB use different file formats. A change to
00996  *   the OS may therefore break DBM access to existing files.
00997  *
00998  * - Data size limits vary between implementations. Original Berkeley DB was
00999  *   limited to 2GB of data. Dbm libraries also sometimes limit the total
01000  *   size of a key/value pair, and the total size of all the keys that hash
01001  *   to the same value. These limits can be as little as 512 bytes. That said,
01002  *   gdbm and recent versions of Berkeley DB do away with these limits.
01003  *
01004  * Given the above cautions, DBM is not a good choice for long term storage of
01005  * important data. It is probably best used as a fast and easy alternative
01006  * to a Hash for processing large amounts of data.
01007  *
01008  * = Example
01009  *
01010  *  require 'dbm'
01011  *  db = DBM.open('rfcs', 666, DBM::CREATRW)
01012  *  db['822'] = 'Standard for the Format of ARPA Internet Text Messages'
01013  *  db['1123'] = 'Requirements for Internet Hosts - Application and Support'
01014  *  db['3068'] = 'An Anycast Prefix for 6to4 Relay Routers'
01015  *  puts db['822']
01016  */
01017 void
01018 Init_dbm(void)
01019 {
01020     rb_cDBM = rb_define_class("DBM", rb_cObject);
01021     /* Document-class: DBMError
01022      * Exception class used to return errors from the dbm library.
01023      */
01024     rb_eDBMError = rb_define_class("DBMError", rb_eStandardError);
01025     rb_include_module(rb_cDBM, rb_mEnumerable);
01026 
01027     rb_define_alloc_func(rb_cDBM, fdbm_alloc);
01028     rb_define_singleton_method(rb_cDBM, "open", fdbm_s_open, -1);
01029 
01030     rb_define_method(rb_cDBM, "initialize", fdbm_initialize, -1);
01031     rb_define_method(rb_cDBM, "close", fdbm_close, 0);
01032     rb_define_method(rb_cDBM, "closed?", fdbm_closed, 0);
01033     rb_define_method(rb_cDBM, "[]", fdbm_aref, 1);
01034     rb_define_method(rb_cDBM, "fetch", fdbm_fetch_m, -1);
01035     rb_define_method(rb_cDBM, "[]=", fdbm_store, 2);
01036     rb_define_method(rb_cDBM, "store", fdbm_store, 2);
01037     rb_define_method(rb_cDBM, "index",  fdbm_index, 1);
01038     rb_define_method(rb_cDBM, "key",  fdbm_key, 1);
01039     rb_define_method(rb_cDBM, "select",  fdbm_select, 0);
01040     rb_define_method(rb_cDBM, "values_at", fdbm_values_at, -1);
01041     rb_define_method(rb_cDBM, "length", fdbm_length, 0);
01042     rb_define_method(rb_cDBM, "size", fdbm_length, 0);
01043     rb_define_method(rb_cDBM, "empty?", fdbm_empty_p, 0);
01044     rb_define_method(rb_cDBM, "each", fdbm_each_pair, 0);
01045     rb_define_method(rb_cDBM, "each_value", fdbm_each_value, 0);
01046     rb_define_method(rb_cDBM, "each_key", fdbm_each_key, 0);
01047     rb_define_method(rb_cDBM, "each_pair", fdbm_each_pair, 0);
01048     rb_define_method(rb_cDBM, "keys", fdbm_keys, 0);
01049     rb_define_method(rb_cDBM, "values", fdbm_values, 0);
01050     rb_define_method(rb_cDBM, "shift", fdbm_shift, 0);
01051     rb_define_method(rb_cDBM, "delete", fdbm_delete, 1);
01052     rb_define_method(rb_cDBM, "delete_if", fdbm_delete_if, 0);
01053     rb_define_method(rb_cDBM, "reject!", fdbm_delete_if, 0);
01054     rb_define_method(rb_cDBM, "reject", fdbm_reject, 0);
01055     rb_define_method(rb_cDBM, "clear", fdbm_clear, 0);
01056     rb_define_method(rb_cDBM, "invert", fdbm_invert, 0);
01057     rb_define_method(rb_cDBM, "update", fdbm_update, 1);
01058     rb_define_method(rb_cDBM, "replace", fdbm_replace, 1);
01059 
01060     rb_define_method(rb_cDBM, "include?", fdbm_has_key, 1);
01061     rb_define_method(rb_cDBM, "has_key?", fdbm_has_key, 1);
01062     rb_define_method(rb_cDBM, "member?", fdbm_has_key, 1);
01063     rb_define_method(rb_cDBM, "has_value?", fdbm_has_value, 1);
01064     rb_define_method(rb_cDBM, "key?", fdbm_has_key, 1);
01065     rb_define_method(rb_cDBM, "value?", fdbm_has_value, 1);
01066 
01067     rb_define_method(rb_cDBM, "to_a", fdbm_to_a, 0);
01068     rb_define_method(rb_cDBM, "to_hash", fdbm_to_hash, 0);
01069 
01070     /* Indicates that dbm_open() should open the database in read-only mode */
01071     rb_define_const(rb_cDBM, "READER",  INT2FIX(O_RDONLY|RUBY_DBM_RW_BIT));
01072 
01073     /* Indicates that dbm_open() should open the database in read/write mode */
01074     rb_define_const(rb_cDBM, "WRITER",  INT2FIX(O_RDWR|RUBY_DBM_RW_BIT));
01075 
01076     /* Indicates that dbm_open() should open the database in read/write mode,
01077      * and create it if it does not already exist
01078      */
01079     rb_define_const(rb_cDBM, "WRCREAT", INT2FIX(O_RDWR|O_CREAT|RUBY_DBM_RW_BIT));
01080 
01081     /* Indicates that dbm_open() should open the database in read/write mode,
01082      * create it if it does not already exist, and delete all contents if it
01083      * does already exist.
01084      */
01085     rb_define_const(rb_cDBM, "NEWDB",   INT2FIX(O_RDWR|O_CREAT|O_TRUNC|RUBY_DBM_RW_BIT));
01086 
01087     {
01088         VALUE version;
01089 #if defined(_DBM_IOERR)
01090         version = rb_str_new2("ndbm (4.3BSD)");
01091 #elif defined(RUBYDBM_GDBM_HEADER)
01092 #  if defined(HAVE_DECLARED_LIBVAR_GDBM_VERSION)
01093         /* since gdbm 1.9 */
01094         version = rb_str_new2(gdbm_version);
01095 #  elif defined(HAVE_UNDECLARED_LIBVAR_GDBM_VERSION)
01096         /* ndbm.h doesn't declare gdbm_version until gdbm 1.8.3.
01097          * See extconf.rb for more information. */
01098         RUBY_EXTERN char *gdbm_version;
01099         version = rb_str_new2(gdbm_version);
01100 #  else
01101         version = rb_str_new2("GDBM (unknown)");
01102 #  endif
01103 #elif defined(RUBYDBM_DB_HEADER)
01104 #  if defined(HAVE_DB_VERSION)
01105         /* The version of the dbm library, if using Berkeley DB */
01106         version = rb_str_new2(db_version(NULL, NULL, NULL));
01107 #  else
01108         version = rb_str_new2("Berkeley DB (unknown)");
01109 #  endif
01110 #elif defined(_RELIC_H)
01111 #  if defined(HAVE_DPVERSION)
01112         version = rb_sprintf("QDBM %s", dpversion);
01113 #  else
01114         version = rb_str_new2("QDBM (unknown)");
01115 #  endif
01116 #else
01117         version = rb_str_new2("ndbm (unknown)");
01118 #endif
01119         /*
01120          * Identifies ndbm library version.
01121          *
01122          * Examples:
01123          *
01124          * - "ndbm (4.3BSD)"
01125          * - "Berkeley DB 4.8.30: (April  9, 2010)"
01126          * - "Berkeley DB (unknown)" (4.4BSD, maybe)
01127          * - "GDBM version 1.8.3. 10/15/2002 (built Jul  1 2011 12:32:45)"
01128          * - "QDBM 1.8.78"
01129          *
01130          */
01131         rb_define_const(rb_cDBM, "VERSION", version);
01132     }
01133 }
01134