Ruby  2.0.0p247(2013-06-27revision41674)
ext/gdbm/gdbm.c
Go to the documentation of this file.
00001 /************************************************
00002 
00003   gdbm.c -
00004 
00005   $Author: nobu $
00006   modified at: Mon Jan 24 15:59:52 JST 1994
00007 
00008   Documentation by Peter Adolphs < futzilogik at users dot sourceforge dot net >
00009 
00010 ************************************************/
00011 
00012 #include "ruby.h"
00013 
00014 #include <gdbm.h>
00015 #include <fcntl.h>
00016 #include <errno.h>
00017 
00018 /*
00019  * Document-class: GDBM
00020  *
00021  * == Summary
00022  *
00023  * Ruby extension for GNU dbm (gdbm) -- a simple database engine for storing
00024  * key-value pairs on disk.
00025  *
00026  * == Description
00027  *
00028  * GNU dbm is a library for simple databases. A database is a file that stores
00029  * key-value pairs. Gdbm allows the user to store, retrieve, and delete data by
00030  * key. It furthermore allows a non-sorted traversal of all key-value pairs.
00031  * A gdbm database thus provides the same functionality as a hash. As
00032  * with objects of the Hash class, elements can be accessed with <tt>[]</tt>.
00033  * Furthermore, GDBM mixes in the Enumerable module, thus providing convenient
00034  * methods such as #find, #collect, #map, etc.
00035  *
00036  * A process is allowed to open several different databases at the same time.
00037  * A process can open a database as a "reader" or a "writer". Whereas a reader
00038  * has only read-access to the database, a writer has read- and write-access.
00039  * A database can be accessed either by any number of readers or by exactly one
00040  * writer at the same time.
00041  *
00042  * == Examples
00043  *
00044  * 1. Opening/creating a database, and filling it with some entries:
00045  *
00046  *      require 'gdbm'
00047  *
00048  *      gdbm = GDBM.new("fruitstore.db")
00049  *      gdbm["ananas"]    = "3"
00050  *      gdbm["banana"]    = "8"
00051  *      gdbm["cranberry"] = "4909"
00052  *      gdbm.close
00053  *
00054  * 2. Reading out a database:
00055  *
00056  *      require 'gdbm'
00057  *
00058  *      gdbm = GDBM.new("fruitstore.db")
00059  *      gdbm.each_pair do |key, value|
00060  *        print "#{key}: #{value}\n"
00061  *      end
00062  *      gdbm.close
00063  *
00064  *    produces
00065  *
00066  *      banana: 8
00067  *      ananas: 3
00068  *      cranberry: 4909
00069  *
00070  * == Links
00071  *
00072  * * http://www.gnu.org/software/gdbm/
00073  */
00074 static VALUE rb_cGDBM, rb_eGDBMError, rb_eGDBMFatalError;
00075 
00076 #if SIZEOF_LONG > SIZEOF_INT
00077 #define TOO_LONG(n) ((long)(+(int)(n)) != (long)(n))
00078 #else
00079 #define TOO_LONG(n) 0
00080 #endif
00081 
00082 #define RUBY_GDBM_RW_BIT 0x20000000
00083 
00084 #define MY_BLOCK_SIZE (2048)
00085 #define MY_FATAL_FUNC rb_gdbm_fatal
00086 static void
00087 rb_gdbm_fatal(const char *msg)
00088 {
00089     rb_raise(rb_eGDBMFatalError, "%s", msg);
00090 }
00091 
00092 struct dbmdata {
00093     int  di_size;
00094     GDBM_FILE di_dbm;
00095 };
00096 
00097 static void
00098 closed_dbm(void)
00099 {
00100     rb_raise(rb_eRuntimeError, "closed GDBM file");
00101 }
00102 
00103 #define GetDBM(obj, dbmp) do {\
00104     Data_Get_Struct((obj), struct dbmdata, (dbmp));\
00105     if ((dbmp) == 0) closed_dbm();\
00106     if ((dbmp)->di_dbm == 0) closed_dbm();\
00107 } while (0)
00108 
00109 #define GetDBM2(obj, data, dbm) {\
00110     GetDBM((obj), (data));\
00111     (dbm) = dbmp->di_dbm;\
00112 }
00113 
00114 static void
00115 free_dbm(struct dbmdata *dbmp)
00116 {
00117     if (dbmp) {
00118         if (dbmp->di_dbm) gdbm_close(dbmp->di_dbm);
00119         xfree(dbmp);
00120     }
00121 }
00122 
00123 /*
00124  * call-seq:
00125  *     gdbm.close -> nil
00126  *
00127  * Closes the associated database file.
00128  */
00129 static VALUE
00130 fgdbm_close(VALUE obj)
00131 {
00132     struct dbmdata *dbmp;
00133 
00134     GetDBM(obj, dbmp);
00135     gdbm_close(dbmp->di_dbm);
00136     dbmp->di_dbm = 0;
00137 
00138     return Qnil;
00139 }
00140 
00141 /*
00142  * call-seq:
00143  *     gdbm.closed?  -> true or false
00144  *
00145  * Returns true if the associated database file has been closed.
00146  */
00147 static VALUE
00148 fgdbm_closed(VALUE obj)
00149 {
00150     struct dbmdata *dbmp;
00151 
00152     Data_Get_Struct(obj, struct dbmdata, dbmp);
00153     if (dbmp == 0)
00154         return Qtrue;
00155     if (dbmp->di_dbm == 0)
00156         return Qtrue;
00157 
00158     return Qfalse;
00159 }
00160 
00161 static VALUE
00162 fgdbm_s_alloc(VALUE klass)
00163 {
00164     return Data_Wrap_Struct(klass, 0, free_dbm, 0);
00165 }
00166 
00167 /*
00168  * call-seq:
00169  *      GDBM.new(filename, mode = 0666, flags = nil)
00170  *
00171  * Creates a new GDBM instance by opening a gdbm file named _filename_.
00172  * If the file does not exist, a new file with file mode _mode_ will be
00173  * created. _flags_ may be one of the following:
00174  * * *READER*  - open as a reader
00175  * * *WRITER*  - open as a writer
00176  * * *WRCREAT* - open as a writer; if the database does not exist, create a new one
00177  * * *NEWDB*   - open as a writer; overwrite any existing databases
00178  *
00179  * The values *WRITER*, *WRCREAT* and *NEWDB* may be combined with the following
00180  * values by bitwise or:
00181  * * *SYNC*    - cause all database operations to be synchronized to the disk
00182  * * *NOLOCK*  - do not lock the database file
00183  *
00184  * If no _flags_ are specified, the GDBM object will try to open the database
00185  * file as a writer and will create it if it does not already exist
00186  * (cf. flag <tt>WRCREAT</tt>). If this fails (for instance, if another process
00187  * has already opened the database as a reader), it will try to open the
00188  * database file as a reader (cf. flag <tt>READER</tt>).
00189  */
00190 static VALUE
00191 fgdbm_initialize(int argc, VALUE *argv, VALUE obj)
00192 {
00193     VALUE file, vmode, vflags;
00194     GDBM_FILE dbm;
00195     struct dbmdata *dbmp;
00196     int mode, flags = 0;
00197 
00198     if (rb_scan_args(argc, argv, "12", &file, &vmode, &vflags) == 1) {
00199         mode = 0666;            /* default value */
00200     }
00201     else if (NIL_P(vmode)) {
00202         mode = -1;              /* return nil if DB does not exist */
00203     }
00204     else {
00205         mode = NUM2INT(vmode);
00206     }
00207 
00208     if (!NIL_P(vflags))
00209         flags = NUM2INT(vflags);
00210 
00211     SafeStringValue(file);
00212 
00213 #ifdef GDBM_CLOEXEC
00214     /* GDBM_CLOEXEC is available since gdbm 1.10. */
00215     flags |= GDBM_CLOEXEC;
00216 #endif
00217 
00218     if (flags & RUBY_GDBM_RW_BIT) {
00219         flags &= ~RUBY_GDBM_RW_BIT;
00220         dbm = gdbm_open(RSTRING_PTR(file), MY_BLOCK_SIZE,
00221                         flags, mode, MY_FATAL_FUNC);
00222     }
00223     else {
00224         dbm = 0;
00225         if (mode >= 0)
00226             dbm = gdbm_open(RSTRING_PTR(file), MY_BLOCK_SIZE,
00227                             GDBM_WRCREAT|flags, mode, MY_FATAL_FUNC);
00228         if (!dbm)
00229             dbm = gdbm_open(RSTRING_PTR(file), MY_BLOCK_SIZE,
00230                             GDBM_WRITER|flags, 0, MY_FATAL_FUNC);
00231         if (!dbm)
00232             dbm = gdbm_open(RSTRING_PTR(file), MY_BLOCK_SIZE,
00233                             GDBM_READER|flags, 0, MY_FATAL_FUNC);
00234     }
00235 
00236     if (dbm) {
00237         rb_fd_fix_cloexec(gdbm_fdesc(dbm));
00238     }
00239 
00240     if (!dbm) {
00241         if (mode == -1) return Qnil;
00242 
00243         if (gdbm_errno == GDBM_FILE_OPEN_ERROR ||
00244             gdbm_errno == GDBM_CANT_BE_READER ||
00245             gdbm_errno == GDBM_CANT_BE_WRITER)
00246             rb_sys_fail_str(file);
00247         else
00248             rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00249     }
00250 
00251     dbmp = ALLOC(struct dbmdata);
00252     free_dbm(DATA_PTR(obj));
00253     DATA_PTR(obj) = dbmp;
00254     dbmp->di_dbm = dbm;
00255     dbmp->di_size = -1;
00256 
00257     return obj;
00258 }
00259 
00260 /*
00261  * call-seq:
00262  *      GDBM.open(filename, mode = 0666, flags = nil)
00263  *      GDBM.open(filename, mode = 0666, flags = nil) { |gdbm| ... }
00264  *
00265  * If called without a block, this is synonymous to GDBM::new.
00266  * If a block is given, the new GDBM instance will be passed to the block
00267  * as a parameter, and the corresponding database file will be closed
00268  * after the execution of the block code has been finished.
00269  *
00270  * Example for an open call with a block:
00271  *
00272  *   require 'gdbm'
00273  *   GDBM.open("fruitstore.db") do |gdbm|
00274  *     gdbm.each_pair do |key, value|
00275  *       print "#{key}: #{value}\n"
00276  *     end
00277  *   end
00278  */
00279 static VALUE
00280 fgdbm_s_open(int argc, VALUE *argv, VALUE klass)
00281 {
00282     VALUE obj = Data_Wrap_Struct(klass, 0, free_dbm, 0);
00283 
00284     if (NIL_P(fgdbm_initialize(argc, argv, obj))) {
00285         return Qnil;
00286     }
00287 
00288     if (rb_block_given_p()) {
00289         return rb_ensure(rb_yield, obj, fgdbm_close, obj);
00290     }
00291 
00292     return obj;
00293 }
00294 
00295 static VALUE
00296 rb_gdbm_fetch(GDBM_FILE dbm, datum key)
00297 {
00298     datum val;
00299     VALUE str;
00300 
00301     val = gdbm_fetch(dbm, key);
00302     if (val.dptr == 0)
00303         return Qnil;
00304 
00305     str = rb_str_new(val.dptr, val.dsize);
00306     free(val.dptr);
00307     OBJ_TAINT(str);
00308     return str;
00309 }
00310 
00311 static VALUE
00312 rb_gdbm_fetch2(GDBM_FILE dbm, VALUE keystr)
00313 {
00314     datum key;
00315     long len;
00316 
00317     StringValue(keystr);
00318     len = RSTRING_LEN(keystr);
00319     if (TOO_LONG(len)) return Qnil;
00320     key.dptr = RSTRING_PTR(keystr);
00321     key.dsize = (int)len;
00322 
00323     return rb_gdbm_fetch(dbm, key);
00324 }
00325 
00326 static VALUE
00327 rb_gdbm_fetch3(VALUE obj, VALUE keystr)
00328 {
00329     struct dbmdata *dbmp;
00330     GDBM_FILE dbm;
00331 
00332     GetDBM2(obj, dbmp, dbm);
00333     return rb_gdbm_fetch2(dbm, keystr);
00334 }
00335 
00336 static VALUE
00337 rb_gdbm_firstkey(GDBM_FILE dbm)
00338 {
00339     datum key;
00340     VALUE str;
00341 
00342     key = gdbm_firstkey(dbm);
00343     if (key.dptr == 0)
00344         return Qnil;
00345 
00346     str = rb_str_new(key.dptr, key.dsize);
00347     free(key.dptr);
00348     OBJ_TAINT(str);
00349     return str;
00350 }
00351 
00352 static VALUE
00353 rb_gdbm_nextkey(GDBM_FILE dbm, VALUE keystr)
00354 {
00355     datum key, key2;
00356     VALUE str;
00357     long len;
00358 
00359     len = RSTRING_LEN(keystr);
00360     if (TOO_LONG(len)) return Qnil;
00361     key.dptr = RSTRING_PTR(keystr);
00362     key.dsize = (int)len;
00363     key2 = gdbm_nextkey(dbm, key);
00364     if (key2.dptr == 0)
00365         return Qnil;
00366 
00367     str = rb_str_new(key2.dptr, key2.dsize);
00368     free(key2.dptr);
00369     OBJ_TAINT(str);
00370     return str;
00371 }
00372 
00373 static VALUE
00374 fgdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone)
00375 {
00376     VALUE valstr;
00377 
00378     valstr = rb_gdbm_fetch3(obj, keystr);
00379     if (NIL_P(valstr)) {
00380         if (ifnone == Qnil && rb_block_given_p())
00381             return rb_yield(keystr);
00382         return ifnone;
00383     }
00384     return valstr;
00385 }
00386 
00387 /*
00388  * call-seq:
00389  *      gdbm[key] -> value
00390  *
00391  * Retrieves the _value_ corresponding to _key_.
00392  */
00393 static VALUE
00394 fgdbm_aref(VALUE obj, VALUE keystr)
00395 {
00396     return rb_gdbm_fetch3(obj, keystr);
00397 }
00398 
00399 /*
00400  * call-seq:
00401  *      gdbm.fetch(key [, default]) -> value
00402  *
00403  * Retrieves the _value_ corresponding to _key_. If there is no value
00404  * associated with _key_, _default_ will be returned instead.
00405  */
00406 static VALUE
00407 fgdbm_fetch_m(int argc, VALUE *argv, VALUE obj)
00408 {
00409     VALUE keystr, valstr, ifnone;
00410 
00411     rb_scan_args(argc, argv, "11", &keystr, &ifnone);
00412     valstr = fgdbm_fetch(obj, keystr, ifnone);
00413     if (argc == 1 && !rb_block_given_p() && NIL_P(valstr))
00414         rb_raise(rb_eIndexError, "key not found");
00415 
00416     return valstr;
00417 }
00418 
00419 /*
00420  * call-seq:
00421  *      gdbm.key(value) -> key
00422  *
00423  * Returns the _key_ for a given _value_. If several keys may map to the
00424  * same value, the key that is found first will be returned.
00425  */
00426 static VALUE
00427 fgdbm_key(VALUE obj, VALUE valstr)
00428 {
00429     struct dbmdata *dbmp;
00430     GDBM_FILE dbm;
00431     VALUE keystr, valstr2;
00432 
00433     StringValue(valstr);
00434     GetDBM2(obj, dbmp, dbm);
00435     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00436          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00437 
00438         valstr2 = rb_gdbm_fetch2(dbm, keystr);
00439         if (!NIL_P(valstr2) &&
00440             (int)RSTRING_LEN(valstr) == (int)RSTRING_LEN(valstr2) &&
00441             memcmp(RSTRING_PTR(valstr), RSTRING_PTR(valstr2),
00442                    (int)RSTRING_LEN(valstr)) == 0) {
00443             return keystr;
00444         }
00445     }
00446     return Qnil;
00447 }
00448 
00449 /* :nodoc: */
00450 static VALUE
00451 fgdbm_index(VALUE obj, VALUE value)
00452 {
00453     rb_warn("GDBM#index is deprecated; use GDBM#key");
00454     return fgdbm_key(obj, value);
00455 }
00456 
00457 /*
00458  * call-seq:
00459  *      gdbm.select { |key, value| block } -> array
00460  *
00461  * Returns a new array of all key-value pairs of the database for which _block_
00462  * evaluates to true.
00463  */
00464 static VALUE
00465 fgdbm_select(VALUE obj)
00466 {
00467     VALUE new = rb_ary_new();
00468     GDBM_FILE dbm;
00469     struct dbmdata *dbmp;
00470     VALUE keystr;
00471 
00472     GetDBM2(obj, dbmp, dbm);
00473     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00474          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00475         VALUE assoc = rb_assoc_new(keystr, rb_gdbm_fetch2(dbm, keystr));
00476         VALUE v = rb_yield(assoc);
00477 
00478         if (RTEST(v)) {
00479             rb_ary_push(new, assoc);
00480         }
00481         GetDBM2(obj, dbmp, dbm);
00482     }
00483 
00484     return new;
00485 }
00486 
00487 /*
00488  * call-seq:
00489  *      gdbm.values_at(key, ...) -> array
00490  *
00491  * Returns an array of the values associated with each specified _key_.
00492  */
00493 static VALUE
00494 fgdbm_values_at(int argc, VALUE *argv, VALUE obj)
00495 {
00496     VALUE new = rb_ary_new2(argc);
00497     int i;
00498 
00499     for (i=0; i<argc; i++) {
00500         rb_ary_push(new, rb_gdbm_fetch3(obj, argv[i]));
00501     }
00502 
00503     return new;
00504 }
00505 
00506 static void
00507 rb_gdbm_modify(VALUE obj)
00508 {
00509     rb_secure(4);
00510     if (OBJ_FROZEN(obj)) rb_error_frozen("GDBM");
00511 }
00512 
00513 static VALUE
00514 rb_gdbm_delete(VALUE obj, VALUE keystr)
00515 {
00516     datum key;
00517     struct dbmdata *dbmp;
00518     GDBM_FILE dbm;
00519     long len;
00520 
00521     rb_gdbm_modify(obj);
00522     StringValue(keystr);
00523     len = RSTRING_LEN(keystr);
00524     if (TOO_LONG(len)) return Qnil;
00525     key.dptr = RSTRING_PTR(keystr);
00526     key.dsize = (int)len;
00527 
00528     GetDBM2(obj, dbmp, dbm);
00529     if (!gdbm_exists(dbm, key)) {
00530         return Qnil;
00531     }
00532 
00533     if (gdbm_delete(dbm, key)) {
00534         dbmp->di_size = -1;
00535         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00536     }
00537     else if (dbmp->di_size >= 0) {
00538         dbmp->di_size--;
00539     }
00540     return obj;
00541 }
00542 
00543 /*
00544  * call-seq:
00545  *      gdbm.delete(key) -> value or nil
00546  *
00547  * Removes the key-value-pair with the specified _key_ from this database and
00548  * returns the corresponding _value_. Returns nil if the database is empty.
00549  */
00550 static VALUE
00551 fgdbm_delete(VALUE obj, VALUE keystr)
00552 {
00553     VALUE valstr;
00554 
00555     valstr = fgdbm_fetch(obj, keystr, Qnil);
00556     rb_gdbm_delete(obj, keystr);
00557     return valstr;
00558 }
00559 
00560 /*
00561  * call-seq:
00562  *      gdbm.shift -> (key, value) or nil
00563  *
00564  * Removes a key-value-pair from this database and returns it as a
00565  * two-item array [ _key_, _value_ ]. Returns nil if the database is empty.
00566  */
00567 static VALUE
00568 fgdbm_shift(VALUE obj)
00569 {
00570     struct dbmdata *dbmp;
00571     GDBM_FILE dbm;
00572     VALUE keystr, valstr;
00573 
00574     rb_gdbm_modify(obj);
00575     GetDBM2(obj, dbmp, dbm);
00576     keystr = rb_gdbm_firstkey(dbm);
00577     if (NIL_P(keystr)) return Qnil;
00578     valstr = rb_gdbm_fetch2(dbm, keystr);
00579     rb_gdbm_delete(obj, keystr);
00580 
00581     return rb_assoc_new(keystr, valstr);
00582 }
00583 
00584 /*
00585  * call-seq:
00586  *      gdbm.delete_if { |key, value| block } -> gdbm
00587  *      gdbm.reject! { |key, value| block } -> gdbm
00588  *
00589  * Deletes every key-value pair from _gdbm_ for which _block_ evaluates to true.
00590  */
00591 static VALUE
00592 fgdbm_delete_if(VALUE obj)
00593 {
00594     struct dbmdata *dbmp;
00595     GDBM_FILE dbm;
00596     VALUE keystr, valstr;
00597     VALUE ret, ary = rb_ary_tmp_new(0);
00598     int i, status = 0, n;
00599 
00600     rb_gdbm_modify(obj);
00601     GetDBM2(obj, dbmp, dbm);
00602     n = dbmp->di_size;
00603     dbmp->di_size = -1;
00604 
00605     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00606          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00607 
00608         OBJ_FREEZE(keystr);
00609         valstr = rb_gdbm_fetch2(dbm, keystr);
00610         ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status);
00611         if (status != 0) break;
00612         if (RTEST(ret)) rb_ary_push(ary, keystr);
00613         GetDBM2(obj, dbmp, dbm);
00614     }
00615 
00616     for (i = 0; i < RARRAY_LEN(ary); i++)
00617         rb_gdbm_delete(obj, RARRAY_PTR(ary)[i]);
00618     if (status) rb_jump_tag(status);
00619     if (n > 0) dbmp->di_size = n - (int)RARRAY_LEN(ary);
00620     rb_ary_clear(ary);
00621 
00622     return obj;
00623 }
00624 
00625 /*
00626  * call-seq:
00627  *      gdbm.clear -> gdbm
00628  *
00629  * Removes all the key-value pairs within _gdbm_.
00630  */
00631 static VALUE
00632 fgdbm_clear(VALUE obj)
00633 {
00634     datum key, nextkey;
00635     struct dbmdata *dbmp;
00636     GDBM_FILE dbm;
00637 
00638     rb_gdbm_modify(obj);
00639     GetDBM2(obj, dbmp, dbm);
00640     dbmp->di_size = -1;
00641 
00642 #if 0
00643     while (key = gdbm_firstkey(dbm), key.dptr) {
00644         if (gdbm_delete(dbm, key)) {
00645             free(key.dptr);
00646             rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00647         }
00648         free(key.dptr);
00649     }
00650 #else
00651     while (key = gdbm_firstkey(dbm), key.dptr) {
00652         for (; key.dptr; key = nextkey) {
00653             nextkey = gdbm_nextkey(dbm, key);
00654             if (gdbm_delete(dbm, key)) {
00655                 free(key.dptr);
00656                 if (nextkey.dptr) free(nextkey.dptr);
00657                 rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00658             }
00659             free(key.dptr);
00660         }
00661     }
00662 #endif
00663     dbmp->di_size = 0;
00664 
00665     return obj;
00666 }
00667 
00668 /*
00669  * call-seq:
00670  *     gdbm.invert  -> hash
00671  *
00672  * Returns a hash created by using _gdbm_'s values as keys, and the keys
00673  * as values.
00674  */
00675 static VALUE
00676 fgdbm_invert(VALUE obj)
00677 {
00678     struct dbmdata *dbmp;
00679     GDBM_FILE dbm;
00680     VALUE keystr, valstr;
00681     VALUE hash = rb_hash_new();
00682 
00683     GetDBM2(obj, dbmp, dbm);
00684     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00685          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00686         valstr = rb_gdbm_fetch2(dbm, keystr);
00687 
00688         rb_hash_aset(hash, valstr, keystr);
00689     }
00690     return hash;
00691 }
00692 
00693 /*
00694  * call-seq:
00695  *      gdbm[key]= value -> value
00696  *      gdbm.store(key, value) -> value
00697  *
00698  * Associates the value _value_ with the specified _key_.
00699  */
00700 static VALUE
00701 fgdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
00702 {
00703     datum key, val;
00704     struct dbmdata *dbmp;
00705     GDBM_FILE dbm;
00706 
00707     rb_gdbm_modify(obj);
00708     StringValue(keystr);
00709     StringValue(valstr);
00710 
00711     key.dptr = RSTRING_PTR(keystr);
00712     key.dsize = RSTRING_LENINT(keystr);
00713 
00714     val.dptr = RSTRING_PTR(valstr);
00715     val.dsize = RSTRING_LENINT(valstr);
00716 
00717     GetDBM2(obj, dbmp, dbm);
00718     dbmp->di_size = -1;
00719     if (gdbm_store(dbm, key, val, GDBM_REPLACE)) {
00720         if (errno == EPERM) rb_sys_fail(0);
00721         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00722     }
00723 
00724     return valstr;
00725 }
00726 
00727 static VALUE
00728 update_i(VALUE pair, VALUE dbm)
00729 {
00730     Check_Type(pair, T_ARRAY);
00731     if (RARRAY_LEN(pair) < 2) {
00732         rb_raise(rb_eArgError, "pair must be [key, value]");
00733     }
00734     fgdbm_store(dbm, RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1]);
00735     return Qnil;
00736 }
00737 
00738 /*
00739  * call-seq:
00740  *     gdbm.update(other) -> gdbm
00741  *
00742  * Adds the key-value pairs of _other_ to _gdbm_, overwriting entries with
00743  * duplicate keys with those from _other_. _other_ must have an each_pair
00744  * method.
00745  */
00746 static VALUE
00747 fgdbm_update(VALUE obj, VALUE other)
00748 {
00749     rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
00750     return obj;
00751 }
00752 
00753 /*
00754  * call-seq:
00755  *     gdbm.replace(other) -> gdbm
00756  *
00757  * Replaces the content of _gdbm_ with the key-value pairs of _other_.
00758  * _other_ must have an each_pair method.
00759  */
00760 static VALUE
00761 fgdbm_replace(VALUE obj, VALUE other)
00762 {
00763     fgdbm_clear(obj);
00764     rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
00765     return obj;
00766 }
00767 
00768 /*
00769  * call-seq:
00770  *      gdbm.length -> fixnum
00771  *      gdbm.size -> fixnum
00772  *
00773  * Returns the number of key-value pairs in this database.
00774  */
00775 static VALUE
00776 fgdbm_length(VALUE obj)
00777 {
00778     datum key, nextkey;
00779     struct dbmdata *dbmp;
00780     GDBM_FILE dbm;
00781     int i = 0;
00782 
00783     GetDBM2(obj, dbmp, dbm);
00784     if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
00785 
00786     for (key = gdbm_firstkey(dbm); key.dptr; key = nextkey) {
00787         nextkey = gdbm_nextkey(dbm, key);
00788         free(key.dptr);
00789         i++;
00790     }
00791     dbmp->di_size = i;
00792 
00793     return INT2FIX(i);
00794 }
00795 
00796 /*
00797  * call-seq:
00798  *      gdbm.empty? -> true or false
00799  *
00800  * Returns true if the database is empty.
00801  */
00802 static VALUE
00803 fgdbm_empty_p(VALUE obj)
00804 {
00805     datum key;
00806     struct dbmdata *dbmp;
00807     GDBM_FILE dbm;
00808 
00809     GetDBM(obj, dbmp);
00810     if (dbmp->di_size < 0) {
00811         dbm = dbmp->di_dbm;
00812 
00813         key = gdbm_firstkey(dbm);
00814         if (key.dptr) {
00815             free(key.dptr);
00816             return Qfalse;
00817         }
00818         return Qtrue;
00819     }
00820 
00821     if (dbmp->di_size == 0) return Qtrue;
00822     return Qfalse;
00823 }
00824 
00825 /*
00826  * call-seq:
00827  *      gdbm.each_value { |value| block } -> gdbm
00828  *
00829  * Executes _block_ for each key in the database, passing the corresponding
00830  * _value_ as a parameter.
00831  */
00832 static VALUE
00833 fgdbm_each_value(VALUE obj)
00834 {
00835     struct dbmdata *dbmp;
00836     GDBM_FILE dbm;
00837     VALUE keystr;
00838 
00839     RETURN_ENUMERATOR(obj, 0, 0);
00840 
00841     GetDBM2(obj, dbmp, dbm);
00842     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00843          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00844 
00845         rb_yield(rb_gdbm_fetch2(dbm, keystr));
00846         GetDBM2(obj, dbmp, dbm);
00847     }
00848     return obj;
00849 }
00850 
00851 /*
00852  * call-seq:
00853  *      gdbm.each_key { |key| block } -> gdbm
00854  *
00855  * Executes _block_ for each key in the database, passing the
00856  * _key_ as a parameter.
00857  */
00858 static VALUE
00859 fgdbm_each_key(VALUE obj)
00860 {
00861     struct dbmdata *dbmp;
00862     GDBM_FILE dbm;
00863     VALUE keystr;
00864 
00865     RETURN_ENUMERATOR(obj, 0, 0);
00866 
00867     GetDBM2(obj, dbmp, dbm);
00868     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00869          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00870 
00871         rb_yield(keystr);
00872         GetDBM2(obj, dbmp, dbm);
00873     }
00874     return obj;
00875 }
00876 
00877 /*
00878  * call-seq:
00879  *      gdbm.each_pair { |key, value| block } -> gdbm
00880  *
00881  * Executes _block_ for each key in the database, passing the _key_ and the
00882  * correspoding _value_ as a parameter.
00883  */
00884 static VALUE
00885 fgdbm_each_pair(VALUE obj)
00886 {
00887     GDBM_FILE dbm;
00888     struct dbmdata *dbmp;
00889     VALUE keystr;
00890 
00891     RETURN_ENUMERATOR(obj, 0, 0);
00892 
00893     GetDBM2(obj, dbmp, dbm);
00894     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00895          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00896 
00897         rb_yield(rb_assoc_new(keystr, rb_gdbm_fetch2(dbm, keystr)));
00898         GetDBM2(obj, dbmp, dbm);
00899     }
00900 
00901     return obj;
00902 }
00903 
00904 /*
00905  * call-seq:
00906  *      gdbm.keys -> array
00907  *
00908  * Returns an array of all keys of this database.
00909  */
00910 static VALUE
00911 fgdbm_keys(VALUE obj)
00912 {
00913     struct dbmdata *dbmp;
00914     GDBM_FILE dbm;
00915     VALUE keystr, ary;
00916 
00917     GetDBM2(obj, dbmp, dbm);
00918     ary = rb_ary_new();
00919     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00920          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00921 
00922         rb_ary_push(ary, keystr);
00923     }
00924 
00925     return ary;
00926 }
00927 
00928 /*
00929  * call-seq:
00930  *      gdbm.values -> array
00931  *
00932  * Returns an array of all values of this database.
00933  */
00934 static VALUE
00935 fgdbm_values(VALUE obj)
00936 {
00937     datum key, nextkey;
00938     struct dbmdata *dbmp;
00939     GDBM_FILE dbm;
00940     VALUE valstr, ary;
00941 
00942     GetDBM2(obj, dbmp, dbm);
00943     ary = rb_ary_new();
00944     for (key = gdbm_firstkey(dbm); key.dptr; key = nextkey) {
00945         nextkey = gdbm_nextkey(dbm, key);
00946         valstr = rb_gdbm_fetch(dbm, key);
00947         free(key.dptr);
00948         rb_ary_push(ary, valstr);
00949     }
00950 
00951     return ary;
00952 }
00953 
00954 /*
00955  * call-seq:
00956  *      gdbm.has_key?(k) -> true or false
00957  *      gdbm.key?(k) -> true or false
00958  *
00959  * Returns true if the given key _k_ exists within the database.
00960  * Returns false otherwise.
00961  */
00962 static VALUE
00963 fgdbm_has_key(VALUE obj, VALUE keystr)
00964 {
00965     datum key;
00966     struct dbmdata *dbmp;
00967     GDBM_FILE dbm;
00968     long len;
00969 
00970     StringValue(keystr);
00971     len = RSTRING_LENINT(keystr);
00972     if (TOO_LONG(len)) return Qfalse;
00973     key.dptr = RSTRING_PTR(keystr);
00974     key.dsize = (int)len;
00975 
00976     GetDBM2(obj, dbmp, dbm);
00977     if (gdbm_exists(dbm, key))
00978         return Qtrue;
00979     return Qfalse;
00980 }
00981 
00982 /*
00983  * call-seq:
00984  *      gdbm.has_value?(v) -> true or false
00985  *      gdbm.value?(v) -> true or false
00986  *
00987  * Returns true if the given value _v_ exists within the database.
00988  * Returns false otherwise.
00989  */
00990 static VALUE
00991 fgdbm_has_value(VALUE obj, VALUE valstr)
00992 {
00993     struct dbmdata *dbmp;
00994     GDBM_FILE dbm;
00995     VALUE keystr, valstr2;
00996 
00997     StringValue(valstr);
00998     GetDBM2(obj, dbmp, dbm);
00999     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
01000          keystr = rb_gdbm_nextkey(dbm, keystr)) {
01001 
01002         valstr2 = rb_gdbm_fetch2(dbm, keystr);
01003 
01004         if (!NIL_P(valstr2) &&
01005             (int)RSTRING_LEN(valstr) == (int)RSTRING_LEN(valstr2) &&
01006             memcmp(RSTRING_PTR(valstr), RSTRING_PTR(valstr2),
01007                    (int)RSTRING_LEN(valstr)) == 0) {
01008             return Qtrue;
01009         }
01010     }
01011     return Qfalse;
01012 }
01013 
01014 /*
01015  * call-seq:
01016  *     gdbm.to_a -> array
01017  *
01018  * Returns an array of all key-value pairs contained in the database.
01019  */
01020 static VALUE
01021 fgdbm_to_a(VALUE obj)
01022 {
01023     struct dbmdata *dbmp;
01024     GDBM_FILE dbm;
01025     VALUE keystr, ary;
01026 
01027     GetDBM2(obj, dbmp, dbm);
01028     ary = rb_ary_new();
01029     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
01030          keystr = rb_gdbm_nextkey(dbm, keystr)) {
01031 
01032         rb_ary_push(ary, rb_assoc_new(keystr, rb_gdbm_fetch2(dbm, keystr)));
01033     }
01034 
01035     return ary;
01036 }
01037 
01038 /*
01039  * call-seq:
01040  *     gdbm.reorganize -> gdbm
01041  *
01042  * Reorganizes the database file. This operation removes reserved space of
01043  * elements that have already been deleted. It is only useful after a lot of
01044  * deletions in the database.
01045  */
01046 static VALUE
01047 fgdbm_reorganize(VALUE obj)
01048 {
01049     struct dbmdata *dbmp;
01050     GDBM_FILE dbm;
01051 
01052     rb_gdbm_modify(obj);
01053     GetDBM2(obj, dbmp, dbm);
01054     gdbm_reorganize(dbm);
01055     rb_fd_fix_cloexec(gdbm_fdesc(dbm));
01056     return obj;
01057 }
01058 
01059 /*
01060  * call-seq:
01061  *     gdbm.sync -> gdbm
01062  *
01063  * Unless the _gdbm_ object has been opened with the *SYNC* flag, it is not
01064  * guarenteed that database modification operations are immediately applied to
01065  * the database file. This method ensures that all recent modifications
01066  * to the database are written to the file. Blocks until all writing operations
01067  * to the disk have been finished.
01068  */
01069 static VALUE
01070 fgdbm_sync(VALUE obj)
01071 {
01072     struct dbmdata *dbmp;
01073     GDBM_FILE dbm;
01074 
01075     rb_gdbm_modify(obj);
01076     GetDBM2(obj, dbmp, dbm);
01077     gdbm_sync(dbm);
01078     return obj;
01079 }
01080 
01081 /*
01082  * call-seq:
01083  *     gdbm.cachesize = size -> size
01084  *
01085  * Sets the size of the internal bucket cache to _size_.
01086  */
01087 static VALUE
01088 fgdbm_set_cachesize(VALUE obj, VALUE val)
01089 {
01090     struct dbmdata *dbmp;
01091     GDBM_FILE dbm;
01092     int optval;
01093 
01094     GetDBM2(obj, dbmp, dbm);
01095     optval = FIX2INT(val);
01096     if (gdbm_setopt(dbm, GDBM_CACHESIZE, &optval, sizeof(optval)) == -1) {
01097         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01098     }
01099     return val;
01100 }
01101 
01102 /*
01103  * call-seq:
01104  *     gdbm.fastmode = boolean -> boolean
01105  *
01106  * Turns the database's fast mode on or off. If fast mode is turned on, gdbm
01107  * does not wait for writes to be flushed to the disk before continuing.
01108  *
01109  * This option is obsolete for gdbm >= 1.8 since fast mode is turned on by
01110  * default. See also: #syncmode=
01111  */
01112 static VALUE
01113 fgdbm_set_fastmode(VALUE obj, VALUE val)
01114 {
01115     struct dbmdata *dbmp;
01116     GDBM_FILE dbm;
01117     int optval;
01118 
01119     GetDBM2(obj, dbmp, dbm);
01120     optval = 0;
01121     if (RTEST(val))
01122         optval = 1;
01123 
01124     if (gdbm_setopt(dbm, GDBM_FASTMODE, &optval, sizeof(optval)) == -1) {
01125         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01126     }
01127     return val;
01128 }
01129 
01130 /*
01131  * call-seq:
01132  *     gdbm.syncmode = boolean -> boolean
01133  *
01134  * Turns the database's synchronization mode on or off. If the synchronization
01135  * mode is turned on, the database's in-memory state will be synchronized to
01136  * disk after every database modification operation. If the synchronization
01137  * mode is turned off, GDBM does not wait for writes to be flushed to the disk
01138  * before continuing.
01139  *
01140  * This option is only available for gdbm >= 1.8 where syncmode is turned off
01141  * by default. See also: #fastmode=
01142  */
01143 static VALUE
01144 fgdbm_set_syncmode(VALUE obj, VALUE val)
01145 {
01146 #if !defined(GDBM_SYNCMODE)
01147     fgdbm_set_fastmode(obj, RTEST(val) ? Qfalse : Qtrue);
01148     return val;
01149 #else
01150     struct dbmdata *dbmp;
01151     GDBM_FILE dbm;
01152     int optval;
01153 
01154     GetDBM2(obj, dbmp, dbm);
01155     optval = 0;
01156     if (RTEST(val))
01157         optval = 1;
01158 
01159     if (gdbm_setopt(dbm, GDBM_FASTMODE, &optval, sizeof(optval)) == -1) {
01160         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01161     }
01162     return val;
01163 #endif
01164 }
01165 
01166 /*
01167  * call-seq:
01168  *     gdbm.to_hash -> hash
01169  *
01170  * Returns a hash of all key-value pairs contained in the database.
01171  */
01172 static VALUE
01173 fgdbm_to_hash(VALUE obj)
01174 {
01175     struct dbmdata *dbmp;
01176     GDBM_FILE dbm;
01177     VALUE keystr, hash;
01178 
01179     GetDBM2(obj, dbmp, dbm);
01180     hash = rb_hash_new();
01181     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
01182          keystr = rb_gdbm_nextkey(dbm, keystr)) {
01183 
01184         rb_hash_aset(hash, keystr, rb_gdbm_fetch2(dbm, keystr));
01185     }
01186 
01187     return hash;
01188 }
01189 
01190 /*
01191  * call-seq:
01192  *      gdbm.reject { |key, value| block } -> hash
01193  *
01194  * Returns a hash copy of _gdbm_ where all key-value pairs from _gdbm_ for
01195  * which _block_ evaluates to true are removed. See also: #delete_if
01196  */
01197 static VALUE
01198 fgdbm_reject(VALUE obj)
01199 {
01200     return rb_hash_delete_if(fgdbm_to_hash(obj));
01201 }
01202 
01203 void
01204 Init_gdbm(void)
01205 {
01206     rb_cGDBM = rb_define_class("GDBM", rb_cObject);
01207     rb_eGDBMError = rb_define_class("GDBMError", rb_eStandardError);
01208     rb_eGDBMFatalError = rb_define_class("GDBMFatalError", rb_eException);
01209     rb_include_module(rb_cGDBM, rb_mEnumerable);
01210 
01211     rb_define_alloc_func(rb_cGDBM, fgdbm_s_alloc);
01212     rb_define_singleton_method(rb_cGDBM, "open", fgdbm_s_open, -1);
01213 
01214     rb_define_method(rb_cGDBM, "initialize", fgdbm_initialize, -1);
01215     rb_define_method(rb_cGDBM, "close", fgdbm_close, 0);
01216     rb_define_method(rb_cGDBM, "closed?", fgdbm_closed, 0);
01217     rb_define_method(rb_cGDBM, "[]", fgdbm_aref, 1);
01218     rb_define_method(rb_cGDBM, "fetch", fgdbm_fetch_m, -1);
01219     rb_define_method(rb_cGDBM, "[]=", fgdbm_store, 2);
01220     rb_define_method(rb_cGDBM, "store", fgdbm_store, 2);
01221     rb_define_method(rb_cGDBM, "index",  fgdbm_index, 1);
01222     rb_define_method(rb_cGDBM, "key",  fgdbm_key, 1);
01223     rb_define_method(rb_cGDBM, "select",  fgdbm_select, 0);
01224     rb_define_method(rb_cGDBM, "values_at",  fgdbm_values_at, -1);
01225     rb_define_method(rb_cGDBM, "length", fgdbm_length, 0);
01226     rb_define_method(rb_cGDBM, "size", fgdbm_length, 0);
01227     rb_define_method(rb_cGDBM, "empty?", fgdbm_empty_p, 0);
01228     rb_define_method(rb_cGDBM, "each", fgdbm_each_pair, 0);
01229     rb_define_method(rb_cGDBM, "each_value", fgdbm_each_value, 0);
01230     rb_define_method(rb_cGDBM, "each_key", fgdbm_each_key, 0);
01231     rb_define_method(rb_cGDBM, "each_pair", fgdbm_each_pair, 0);
01232     rb_define_method(rb_cGDBM, "keys", fgdbm_keys, 0);
01233     rb_define_method(rb_cGDBM, "values", fgdbm_values, 0);
01234     rb_define_method(rb_cGDBM, "shift", fgdbm_shift, 0);
01235     rb_define_method(rb_cGDBM, "delete", fgdbm_delete, 1);
01236     rb_define_method(rb_cGDBM, "delete_if", fgdbm_delete_if, 0);
01237     rb_define_method(rb_cGDBM, "reject!", fgdbm_delete_if, 0);
01238     rb_define_method(rb_cGDBM, "reject", fgdbm_reject, 0);
01239     rb_define_method(rb_cGDBM, "clear", fgdbm_clear, 0);
01240     rb_define_method(rb_cGDBM, "invert", fgdbm_invert, 0);
01241     rb_define_method(rb_cGDBM, "update", fgdbm_update, 1);
01242     rb_define_method(rb_cGDBM, "replace", fgdbm_replace, 1);
01243     rb_define_method(rb_cGDBM, "reorganize", fgdbm_reorganize, 0);
01244     rb_define_method(rb_cGDBM, "sync", fgdbm_sync, 0);
01245     /* rb_define_method(rb_cGDBM, "setopt", fgdbm_setopt, 2); */
01246     rb_define_method(rb_cGDBM, "cachesize=", fgdbm_set_cachesize, 1);
01247     rb_define_method(rb_cGDBM, "fastmode=", fgdbm_set_fastmode, 1);
01248     rb_define_method(rb_cGDBM, "syncmode=", fgdbm_set_syncmode, 1);
01249 
01250     rb_define_method(rb_cGDBM, "include?", fgdbm_has_key, 1);
01251     rb_define_method(rb_cGDBM, "has_key?", fgdbm_has_key, 1);
01252     rb_define_method(rb_cGDBM, "member?", fgdbm_has_key, 1);
01253     rb_define_method(rb_cGDBM, "has_value?", fgdbm_has_value, 1);
01254     rb_define_method(rb_cGDBM, "key?", fgdbm_has_key, 1);
01255     rb_define_method(rb_cGDBM, "value?", fgdbm_has_value, 1);
01256 
01257     rb_define_method(rb_cGDBM, "to_a", fgdbm_to_a, 0);
01258     rb_define_method(rb_cGDBM, "to_hash", fgdbm_to_hash, 0);
01259 
01260     /* flag for #new and #open: open database as a reader */
01261     rb_define_const(rb_cGDBM, "READER",  INT2FIX(GDBM_READER|RUBY_GDBM_RW_BIT));
01262     /* flag for #new and #open: open database as a writer */
01263     rb_define_const(rb_cGDBM, "WRITER",  INT2FIX(GDBM_WRITER|RUBY_GDBM_RW_BIT));
01264     /* flag for #new and #open: open database as a writer; if the database does not exist, create a new one */
01265     rb_define_const(rb_cGDBM, "WRCREAT", INT2FIX(GDBM_WRCREAT|RUBY_GDBM_RW_BIT));
01266     /* flag for #new and #open: open database as a writer; overwrite any existing databases  */
01267     rb_define_const(rb_cGDBM, "NEWDB",   INT2FIX(GDBM_NEWDB|RUBY_GDBM_RW_BIT));
01268 
01269     /* flag for #new and #open. this flag is obsolete for gdbm >= 1.8 */
01270     rb_define_const(rb_cGDBM, "FAST", INT2FIX(GDBM_FAST));
01271     /* this flag is obsolete in gdbm 1.8.
01272        On gdbm 1.8, fast mode is default behavior. */
01273 
01274     /* gdbm version 1.8 specific */
01275 #if defined(GDBM_SYNC)
01276     /* flag for #new and #open. only for gdbm >= 1.8 */
01277     rb_define_const(rb_cGDBM, "SYNC",    INT2FIX(GDBM_SYNC));
01278 #endif
01279 #if defined(GDBM_NOLOCK)
01280     /* flag for #new and #open */
01281     rb_define_const(rb_cGDBM, "NOLOCK",  INT2FIX(GDBM_NOLOCK));
01282 #endif
01283     /* version of the gdbm library*/
01284     rb_define_const(rb_cGDBM, "VERSION",  rb_str_new2(gdbm_version));
01285 }
01286