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     if (OBJ_FROZEN(obj)) rb_error_frozen("GDBM");
00510 }
00511 
00512 static VALUE
00513 rb_gdbm_delete(VALUE obj, VALUE keystr)
00514 {
00515     datum key;
00516     struct dbmdata *dbmp;
00517     GDBM_FILE dbm;
00518     long len;
00519 
00520     rb_gdbm_modify(obj);
00521     StringValue(keystr);
00522     len = RSTRING_LEN(keystr);
00523     if (TOO_LONG(len)) return Qnil;
00524     key.dptr = RSTRING_PTR(keystr);
00525     key.dsize = (int)len;
00526 
00527     GetDBM2(obj, dbmp, dbm);
00528     if (!gdbm_exists(dbm, key)) {
00529         return Qnil;
00530     }
00531 
00532     if (gdbm_delete(dbm, key)) {
00533         dbmp->di_size = -1;
00534         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00535     }
00536     else if (dbmp->di_size >= 0) {
00537         dbmp->di_size--;
00538     }
00539     return obj;
00540 }
00541 
00542 /*
00543  * call-seq:
00544  *      gdbm.delete(key) -> value or nil
00545  *
00546  * Removes the key-value-pair with the specified _key_ from this database and
00547  * returns the corresponding _value_. Returns nil if the database is empty.
00548  */
00549 static VALUE
00550 fgdbm_delete(VALUE obj, VALUE keystr)
00551 {
00552     VALUE valstr;
00553 
00554     valstr = fgdbm_fetch(obj, keystr, Qnil);
00555     rb_gdbm_delete(obj, keystr);
00556     return valstr;
00557 }
00558 
00559 /*
00560  * call-seq:
00561  *      gdbm.shift -> (key, value) or nil
00562  *
00563  * Removes a key-value-pair from this database and returns it as a
00564  * two-item array [ _key_, _value_ ]. Returns nil if the database is empty.
00565  */
00566 static VALUE
00567 fgdbm_shift(VALUE obj)
00568 {
00569     struct dbmdata *dbmp;
00570     GDBM_FILE dbm;
00571     VALUE keystr, valstr;
00572 
00573     rb_gdbm_modify(obj);
00574     GetDBM2(obj, dbmp, dbm);
00575     keystr = rb_gdbm_firstkey(dbm);
00576     if (NIL_P(keystr)) return Qnil;
00577     valstr = rb_gdbm_fetch2(dbm, keystr);
00578     rb_gdbm_delete(obj, keystr);
00579 
00580     return rb_assoc_new(keystr, valstr);
00581 }
00582 
00583 /*
00584  * call-seq:
00585  *      gdbm.delete_if { |key, value| block } -> gdbm
00586  *      gdbm.reject! { |key, value| block } -> gdbm
00587  *
00588  * Deletes every key-value pair from _gdbm_ for which _block_ evaluates to true.
00589  */
00590 static VALUE
00591 fgdbm_delete_if(VALUE obj)
00592 {
00593     struct dbmdata *dbmp;
00594     GDBM_FILE dbm;
00595     VALUE keystr, valstr;
00596     VALUE ret, ary = rb_ary_tmp_new(0);
00597     int i, status = 0, n;
00598 
00599     rb_gdbm_modify(obj);
00600     GetDBM2(obj, dbmp, dbm);
00601     n = dbmp->di_size;
00602     dbmp->di_size = -1;
00603 
00604     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00605          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00606 
00607         OBJ_FREEZE(keystr);
00608         valstr = rb_gdbm_fetch2(dbm, keystr);
00609         ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status);
00610         if (status != 0) break;
00611         if (RTEST(ret)) rb_ary_push(ary, keystr);
00612         GetDBM2(obj, dbmp, dbm);
00613     }
00614 
00615     for (i = 0; i < RARRAY_LEN(ary); i++)
00616         rb_gdbm_delete(obj, RARRAY_PTR(ary)[i]);
00617     if (status) rb_jump_tag(status);
00618     if (n > 0) dbmp->di_size = n - (int)RARRAY_LEN(ary);
00619     rb_ary_clear(ary);
00620 
00621     return obj;
00622 }
00623 
00624 /*
00625  * call-seq:
00626  *      gdbm.clear -> gdbm
00627  *
00628  * Removes all the key-value pairs within _gdbm_.
00629  */
00630 static VALUE
00631 fgdbm_clear(VALUE obj)
00632 {
00633     datum key, nextkey;
00634     struct dbmdata *dbmp;
00635     GDBM_FILE dbm;
00636 
00637     rb_gdbm_modify(obj);
00638     GetDBM2(obj, dbmp, dbm);
00639     dbmp->di_size = -1;
00640 
00641 #if 0
00642     while (key = gdbm_firstkey(dbm), key.dptr) {
00643         if (gdbm_delete(dbm, key)) {
00644             free(key.dptr);
00645             rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00646         }
00647         free(key.dptr);
00648     }
00649 #else
00650     while (key = gdbm_firstkey(dbm), key.dptr) {
00651         for (; key.dptr; key = nextkey) {
00652             nextkey = gdbm_nextkey(dbm, key);
00653             if (gdbm_delete(dbm, key)) {
00654                 free(key.dptr);
00655                 if (nextkey.dptr) free(nextkey.dptr);
00656                 rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00657             }
00658             free(key.dptr);
00659         }
00660     }
00661 #endif
00662     dbmp->di_size = 0;
00663 
00664     return obj;
00665 }
00666 
00667 /*
00668  * call-seq:
00669  *     gdbm.invert  -> hash
00670  *
00671  * Returns a hash created by using _gdbm_'s values as keys, and the keys
00672  * as values.
00673  */
00674 static VALUE
00675 fgdbm_invert(VALUE obj)
00676 {
00677     struct dbmdata *dbmp;
00678     GDBM_FILE dbm;
00679     VALUE keystr, valstr;
00680     VALUE hash = rb_hash_new();
00681 
00682     GetDBM2(obj, dbmp, dbm);
00683     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00684          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00685         valstr = rb_gdbm_fetch2(dbm, keystr);
00686 
00687         rb_hash_aset(hash, valstr, keystr);
00688     }
00689     return hash;
00690 }
00691 
00692 /*
00693  * call-seq:
00694  *      gdbm[key]= value -> value
00695  *      gdbm.store(key, value) -> value
00696  *
00697  * Associates the value _value_ with the specified _key_.
00698  */
00699 static VALUE
00700 fgdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
00701 {
00702     datum key, val;
00703     struct dbmdata *dbmp;
00704     GDBM_FILE dbm;
00705 
00706     rb_gdbm_modify(obj);
00707     StringValue(keystr);
00708     StringValue(valstr);
00709 
00710     key.dptr = RSTRING_PTR(keystr);
00711     key.dsize = RSTRING_LENINT(keystr);
00712 
00713     val.dptr = RSTRING_PTR(valstr);
00714     val.dsize = RSTRING_LENINT(valstr);
00715 
00716     GetDBM2(obj, dbmp, dbm);
00717     dbmp->di_size = -1;
00718     if (gdbm_store(dbm, key, val, GDBM_REPLACE)) {
00719         if (errno == EPERM) rb_sys_fail(0);
00720         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00721     }
00722 
00723     return valstr;
00724 }
00725 
00726 static VALUE
00727 update_i(RB_BLOCK_CALL_FUNC_ARGLIST(pair, dbm))
00728 {
00729     Check_Type(pair, T_ARRAY);
00730     if (RARRAY_LEN(pair) < 2) {
00731         rb_raise(rb_eArgError, "pair must be [key, value]");
00732     }
00733     fgdbm_store(dbm, RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1]);
00734     return Qnil;
00735 }
00736 
00737 /*
00738  * call-seq:
00739  *     gdbm.update(other) -> gdbm
00740  *
00741  * Adds the key-value pairs of _other_ to _gdbm_, overwriting entries with
00742  * duplicate keys with those from _other_. _other_ must have an each_pair
00743  * method.
00744  */
00745 static VALUE
00746 fgdbm_update(VALUE obj, VALUE other)
00747 {
00748     rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
00749     return obj;
00750 }
00751 
00752 /*
00753  * call-seq:
00754  *     gdbm.replace(other) -> gdbm
00755  *
00756  * Replaces the content of _gdbm_ with the key-value pairs of _other_.
00757  * _other_ must have an each_pair method.
00758  */
00759 static VALUE
00760 fgdbm_replace(VALUE obj, VALUE other)
00761 {
00762     fgdbm_clear(obj);
00763     rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
00764     return obj;
00765 }
00766 
00767 /*
00768  * call-seq:
00769  *      gdbm.length -> fixnum
00770  *      gdbm.size -> fixnum
00771  *
00772  * Returns the number of key-value pairs in this database.
00773  */
00774 static VALUE
00775 fgdbm_length(VALUE obj)
00776 {
00777     datum key, nextkey;
00778     struct dbmdata *dbmp;
00779     GDBM_FILE dbm;
00780     int i = 0;
00781 
00782     GetDBM2(obj, dbmp, dbm);
00783     if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
00784 
00785     for (key = gdbm_firstkey(dbm); key.dptr; key = nextkey) {
00786         nextkey = gdbm_nextkey(dbm, key);
00787         free(key.dptr);
00788         i++;
00789     }
00790     dbmp->di_size = i;
00791 
00792     return INT2FIX(i);
00793 }
00794 
00795 /*
00796  * call-seq:
00797  *      gdbm.empty? -> true or false
00798  *
00799  * Returns true if the database is empty.
00800  */
00801 static VALUE
00802 fgdbm_empty_p(VALUE obj)
00803 {
00804     datum key;
00805     struct dbmdata *dbmp;
00806     GDBM_FILE dbm;
00807 
00808     GetDBM(obj, dbmp);
00809     if (dbmp->di_size < 0) {
00810         dbm = dbmp->di_dbm;
00811 
00812         key = gdbm_firstkey(dbm);
00813         if (key.dptr) {
00814             free(key.dptr);
00815             return Qfalse;
00816         }
00817         return Qtrue;
00818     }
00819 
00820     if (dbmp->di_size == 0) return Qtrue;
00821     return Qfalse;
00822 }
00823 
00824 /*
00825  * call-seq:
00826  *      gdbm.each_value { |value| block } -> gdbm
00827  *
00828  * Executes _block_ for each key in the database, passing the corresponding
00829  * _value_ as a parameter.
00830  */
00831 static VALUE
00832 fgdbm_each_value(VALUE obj)
00833 {
00834     struct dbmdata *dbmp;
00835     GDBM_FILE dbm;
00836     VALUE keystr;
00837 
00838     RETURN_ENUMERATOR(obj, 0, 0);
00839 
00840     GetDBM2(obj, dbmp, dbm);
00841     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00842          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00843 
00844         rb_yield(rb_gdbm_fetch2(dbm, keystr));
00845         GetDBM2(obj, dbmp, dbm);
00846     }
00847     return obj;
00848 }
00849 
00850 /*
00851  * call-seq:
00852  *      gdbm.each_key { |key| block } -> gdbm
00853  *
00854  * Executes _block_ for each key in the database, passing the
00855  * _key_ as a parameter.
00856  */
00857 static VALUE
00858 fgdbm_each_key(VALUE obj)
00859 {
00860     struct dbmdata *dbmp;
00861     GDBM_FILE dbm;
00862     VALUE keystr;
00863 
00864     RETURN_ENUMERATOR(obj, 0, 0);
00865 
00866     GetDBM2(obj, dbmp, dbm);
00867     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00868          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00869 
00870         rb_yield(keystr);
00871         GetDBM2(obj, dbmp, dbm);
00872     }
00873     return obj;
00874 }
00875 
00876 /*
00877  * call-seq:
00878  *      gdbm.each_pair { |key, value| block } -> gdbm
00879  *
00880  * Executes _block_ for each key in the database, passing the _key_ and the
00881  * correspoding _value_ as a parameter.
00882  */
00883 static VALUE
00884 fgdbm_each_pair(VALUE obj)
00885 {
00886     GDBM_FILE dbm;
00887     struct dbmdata *dbmp;
00888     VALUE keystr;
00889 
00890     RETURN_ENUMERATOR(obj, 0, 0);
00891 
00892     GetDBM2(obj, dbmp, dbm);
00893     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00894          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00895 
00896         rb_yield(rb_assoc_new(keystr, rb_gdbm_fetch2(dbm, keystr)));
00897         GetDBM2(obj, dbmp, dbm);
00898     }
00899 
00900     return obj;
00901 }
00902 
00903 /*
00904  * call-seq:
00905  *      gdbm.keys -> array
00906  *
00907  * Returns an array of all keys of this database.
00908  */
00909 static VALUE
00910 fgdbm_keys(VALUE obj)
00911 {
00912     struct dbmdata *dbmp;
00913     GDBM_FILE dbm;
00914     VALUE keystr, ary;
00915 
00916     GetDBM2(obj, dbmp, dbm);
00917     ary = rb_ary_new();
00918     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00919          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00920 
00921         rb_ary_push(ary, keystr);
00922     }
00923 
00924     return ary;
00925 }
00926 
00927 /*
00928  * call-seq:
00929  *      gdbm.values -> array
00930  *
00931  * Returns an array of all values of this database.
00932  */
00933 static VALUE
00934 fgdbm_values(VALUE obj)
00935 {
00936     datum key, nextkey;
00937     struct dbmdata *dbmp;
00938     GDBM_FILE dbm;
00939     VALUE valstr, ary;
00940 
00941     GetDBM2(obj, dbmp, dbm);
00942     ary = rb_ary_new();
00943     for (key = gdbm_firstkey(dbm); key.dptr; key = nextkey) {
00944         nextkey = gdbm_nextkey(dbm, key);
00945         valstr = rb_gdbm_fetch(dbm, key);
00946         free(key.dptr);
00947         rb_ary_push(ary, valstr);
00948     }
00949 
00950     return ary;
00951 }
00952 
00953 /*
00954  * call-seq:
00955  *      gdbm.include?(k) -> true or false
00956  *      gdbm.has_key?(k) -> true or false
00957  *      gdbm.member?(k) -> true or false
00958  *      gdbm.key?(k) -> true or false
00959  *
00960  * Returns true if the given key _k_ exists within the database.
00961  * Returns false otherwise.
00962  */
00963 static VALUE
00964 fgdbm_has_key(VALUE obj, VALUE keystr)
00965 {
00966     datum key;
00967     struct dbmdata *dbmp;
00968     GDBM_FILE dbm;
00969     long len;
00970 
00971     StringValue(keystr);
00972     len = RSTRING_LENINT(keystr);
00973     if (TOO_LONG(len)) return Qfalse;
00974     key.dptr = RSTRING_PTR(keystr);
00975     key.dsize = (int)len;
00976 
00977     GetDBM2(obj, dbmp, dbm);
00978     if (gdbm_exists(dbm, key))
00979         return Qtrue;
00980     return Qfalse;
00981 }
00982 
00983 /*
00984  * call-seq:
00985  *      gdbm.has_value?(v) -> true or false
00986  *      gdbm.value?(v) -> true or false
00987  *
00988  * Returns true if the given value _v_ exists within the database.
00989  * Returns false otherwise.
00990  */
00991 static VALUE
00992 fgdbm_has_value(VALUE obj, VALUE valstr)
00993 {
00994     struct dbmdata *dbmp;
00995     GDBM_FILE dbm;
00996     VALUE keystr, valstr2;
00997 
00998     StringValue(valstr);
00999     GetDBM2(obj, dbmp, dbm);
01000     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
01001          keystr = rb_gdbm_nextkey(dbm, keystr)) {
01002 
01003         valstr2 = rb_gdbm_fetch2(dbm, keystr);
01004 
01005         if (!NIL_P(valstr2) &&
01006             (int)RSTRING_LEN(valstr) == (int)RSTRING_LEN(valstr2) &&
01007             memcmp(RSTRING_PTR(valstr), RSTRING_PTR(valstr2),
01008                    (int)RSTRING_LEN(valstr)) == 0) {
01009             return Qtrue;
01010         }
01011     }
01012     return Qfalse;
01013 }
01014 
01015 /*
01016  * call-seq:
01017  *     gdbm.to_a -> array
01018  *
01019  * Returns an array of all key-value pairs contained in the database.
01020  */
01021 static VALUE
01022 fgdbm_to_a(VALUE obj)
01023 {
01024     struct dbmdata *dbmp;
01025     GDBM_FILE dbm;
01026     VALUE keystr, ary;
01027 
01028     GetDBM2(obj, dbmp, dbm);
01029     ary = rb_ary_new();
01030     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
01031          keystr = rb_gdbm_nextkey(dbm, keystr)) {
01032 
01033         rb_ary_push(ary, rb_assoc_new(keystr, rb_gdbm_fetch2(dbm, keystr)));
01034     }
01035 
01036     return ary;
01037 }
01038 
01039 /*
01040  * call-seq:
01041  *     gdbm.reorganize -> gdbm
01042  *
01043  * Reorganizes the database file. This operation removes reserved space of
01044  * elements that have already been deleted. It is only useful after a lot of
01045  * deletions in the database.
01046  */
01047 static VALUE
01048 fgdbm_reorganize(VALUE obj)
01049 {
01050     struct dbmdata *dbmp;
01051     GDBM_FILE dbm;
01052 
01053     rb_gdbm_modify(obj);
01054     GetDBM2(obj, dbmp, dbm);
01055     gdbm_reorganize(dbm);
01056     rb_fd_fix_cloexec(gdbm_fdesc(dbm));
01057     return obj;
01058 }
01059 
01060 /*
01061  * call-seq:
01062  *     gdbm.sync -> gdbm
01063  *
01064  * Unless the _gdbm_ object has been opened with the *SYNC* flag, it is not
01065  * guarenteed that database modification operations are immediately applied to
01066  * the database file. This method ensures that all recent modifications
01067  * to the database are written to the file. Blocks until all writing operations
01068  * to the disk have been finished.
01069  */
01070 static VALUE
01071 fgdbm_sync(VALUE obj)
01072 {
01073     struct dbmdata *dbmp;
01074     GDBM_FILE dbm;
01075 
01076     rb_gdbm_modify(obj);
01077     GetDBM2(obj, dbmp, dbm);
01078     gdbm_sync(dbm);
01079     return obj;
01080 }
01081 
01082 /*
01083  * call-seq:
01084  *     gdbm.cachesize = size -> size
01085  *
01086  * Sets the size of the internal bucket cache to _size_.
01087  */
01088 static VALUE
01089 fgdbm_set_cachesize(VALUE obj, VALUE val)
01090 {
01091     struct dbmdata *dbmp;
01092     GDBM_FILE dbm;
01093     int optval;
01094 
01095     GetDBM2(obj, dbmp, dbm);
01096     optval = FIX2INT(val);
01097     if (gdbm_setopt(dbm, GDBM_CACHESIZE, &optval, sizeof(optval)) == -1) {
01098         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01099     }
01100     return val;
01101 }
01102 
01103 /*
01104  * call-seq:
01105  *     gdbm.fastmode = boolean -> boolean
01106  *
01107  * Turns the database's fast mode on or off. If fast mode is turned on, gdbm
01108  * does not wait for writes to be flushed to the disk before continuing.
01109  *
01110  * This option is obsolete for gdbm >= 1.8 since fast mode is turned on by
01111  * default. See also: #syncmode=
01112  */
01113 static VALUE
01114 fgdbm_set_fastmode(VALUE obj, VALUE val)
01115 {
01116     struct dbmdata *dbmp;
01117     GDBM_FILE dbm;
01118     int optval;
01119 
01120     GetDBM2(obj, dbmp, dbm);
01121     optval = 0;
01122     if (RTEST(val))
01123         optval = 1;
01124 
01125     if (gdbm_setopt(dbm, GDBM_FASTMODE, &optval, sizeof(optval)) == -1) {
01126         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01127     }
01128     return val;
01129 }
01130 
01131 /*
01132  * call-seq:
01133  *     gdbm.syncmode = boolean -> boolean
01134  *
01135  * Turns the database's synchronization mode on or off. If the synchronization
01136  * mode is turned on, the database's in-memory state will be synchronized to
01137  * disk after every database modification operation. If the synchronization
01138  * mode is turned off, GDBM does not wait for writes to be flushed to the disk
01139  * before continuing.
01140  *
01141  * This option is only available for gdbm >= 1.8 where syncmode is turned off
01142  * by default. See also: #fastmode=
01143  */
01144 static VALUE
01145 fgdbm_set_syncmode(VALUE obj, VALUE val)
01146 {
01147 #if !defined(GDBM_SYNCMODE)
01148     fgdbm_set_fastmode(obj, RTEST(val) ? Qfalse : Qtrue);
01149     return val;
01150 #else
01151     struct dbmdata *dbmp;
01152     GDBM_FILE dbm;
01153     int optval;
01154 
01155     GetDBM2(obj, dbmp, dbm);
01156     optval = 0;
01157     if (RTEST(val))
01158         optval = 1;
01159 
01160     if (gdbm_setopt(dbm, GDBM_FASTMODE, &optval, sizeof(optval)) == -1) {
01161         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01162     }
01163     return val;
01164 #endif
01165 }
01166 
01167 /*
01168  * call-seq:
01169  *     gdbm.to_hash -> hash
01170  *
01171  * Returns a hash of all key-value pairs contained in the database.
01172  */
01173 static VALUE
01174 fgdbm_to_hash(VALUE obj)
01175 {
01176     struct dbmdata *dbmp;
01177     GDBM_FILE dbm;
01178     VALUE keystr, hash;
01179 
01180     GetDBM2(obj, dbmp, dbm);
01181     hash = rb_hash_new();
01182     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
01183          keystr = rb_gdbm_nextkey(dbm, keystr)) {
01184 
01185         rb_hash_aset(hash, keystr, rb_gdbm_fetch2(dbm, keystr));
01186     }
01187 
01188     return hash;
01189 }
01190 
01191 /*
01192  * call-seq:
01193  *      gdbm.reject { |key, value| block } -> hash
01194  *
01195  * Returns a hash copy of _gdbm_ where all key-value pairs from _gdbm_ for
01196  * which _block_ evaluates to true are removed. See also: #delete_if
01197  */
01198 static VALUE
01199 fgdbm_reject(VALUE obj)
01200 {
01201     return rb_hash_delete_if(fgdbm_to_hash(obj));
01202 }
01203 
01204 void
01205 Init_gdbm(void)
01206 {
01207     rb_cGDBM = rb_define_class("GDBM", rb_cObject);
01208     rb_eGDBMError = rb_define_class("GDBMError", rb_eStandardError);
01209     rb_eGDBMFatalError = rb_define_class("GDBMFatalError", rb_eException);
01210     rb_include_module(rb_cGDBM, rb_mEnumerable);
01211 
01212     rb_define_alloc_func(rb_cGDBM, fgdbm_s_alloc);
01213     rb_define_singleton_method(rb_cGDBM, "open", fgdbm_s_open, -1);
01214 
01215     rb_define_method(rb_cGDBM, "initialize", fgdbm_initialize, -1);
01216     rb_define_method(rb_cGDBM, "close", fgdbm_close, 0);
01217     rb_define_method(rb_cGDBM, "closed?", fgdbm_closed, 0);
01218     rb_define_method(rb_cGDBM, "[]", fgdbm_aref, 1);
01219     rb_define_method(rb_cGDBM, "fetch", fgdbm_fetch_m, -1);
01220     rb_define_method(rb_cGDBM, "[]=", fgdbm_store, 2);
01221     rb_define_method(rb_cGDBM, "store", fgdbm_store, 2);
01222     rb_define_method(rb_cGDBM, "index",  fgdbm_index, 1);
01223     rb_define_method(rb_cGDBM, "key",  fgdbm_key, 1);
01224     rb_define_method(rb_cGDBM, "select",  fgdbm_select, 0);
01225     rb_define_method(rb_cGDBM, "values_at",  fgdbm_values_at, -1);
01226     rb_define_method(rb_cGDBM, "length", fgdbm_length, 0);
01227     rb_define_method(rb_cGDBM, "size", fgdbm_length, 0);
01228     rb_define_method(rb_cGDBM, "empty?", fgdbm_empty_p, 0);
01229     rb_define_method(rb_cGDBM, "each", fgdbm_each_pair, 0);
01230     rb_define_method(rb_cGDBM, "each_value", fgdbm_each_value, 0);
01231     rb_define_method(rb_cGDBM, "each_key", fgdbm_each_key, 0);
01232     rb_define_method(rb_cGDBM, "each_pair", fgdbm_each_pair, 0);
01233     rb_define_method(rb_cGDBM, "keys", fgdbm_keys, 0);
01234     rb_define_method(rb_cGDBM, "values", fgdbm_values, 0);
01235     rb_define_method(rb_cGDBM, "shift", fgdbm_shift, 0);
01236     rb_define_method(rb_cGDBM, "delete", fgdbm_delete, 1);
01237     rb_define_method(rb_cGDBM, "delete_if", fgdbm_delete_if, 0);
01238     rb_define_method(rb_cGDBM, "reject!", fgdbm_delete_if, 0);
01239     rb_define_method(rb_cGDBM, "reject", fgdbm_reject, 0);
01240     rb_define_method(rb_cGDBM, "clear", fgdbm_clear, 0);
01241     rb_define_method(rb_cGDBM, "invert", fgdbm_invert, 0);
01242     rb_define_method(rb_cGDBM, "update", fgdbm_update, 1);
01243     rb_define_method(rb_cGDBM, "replace", fgdbm_replace, 1);
01244     rb_define_method(rb_cGDBM, "reorganize", fgdbm_reorganize, 0);
01245     rb_define_method(rb_cGDBM, "sync", fgdbm_sync, 0);
01246     /* rb_define_method(rb_cGDBM, "setopt", fgdbm_setopt, 2); */
01247     rb_define_method(rb_cGDBM, "cachesize=", fgdbm_set_cachesize, 1);
01248     rb_define_method(rb_cGDBM, "fastmode=", fgdbm_set_fastmode, 1);
01249     rb_define_method(rb_cGDBM, "syncmode=", fgdbm_set_syncmode, 1);
01250 
01251     rb_define_method(rb_cGDBM, "include?", fgdbm_has_key, 1);
01252     rb_define_method(rb_cGDBM, "has_key?", fgdbm_has_key, 1);
01253     rb_define_method(rb_cGDBM, "member?", fgdbm_has_key, 1);
01254     rb_define_method(rb_cGDBM, "has_value?", fgdbm_has_value, 1);
01255     rb_define_method(rb_cGDBM, "key?", fgdbm_has_key, 1);
01256     rb_define_method(rb_cGDBM, "value?", fgdbm_has_value, 1);
01257 
01258     rb_define_method(rb_cGDBM, "to_a", fgdbm_to_a, 0);
01259     rb_define_method(rb_cGDBM, "to_hash", fgdbm_to_hash, 0);
01260 
01261     /* flag for #new and #open: open database as a reader */
01262     rb_define_const(rb_cGDBM, "READER",  INT2FIX(GDBM_READER|RUBY_GDBM_RW_BIT));
01263     /* flag for #new and #open: open database as a writer */
01264     rb_define_const(rb_cGDBM, "WRITER",  INT2FIX(GDBM_WRITER|RUBY_GDBM_RW_BIT));
01265     /* flag for #new and #open: open database as a writer; if the database does not exist, create a new one */
01266     rb_define_const(rb_cGDBM, "WRCREAT", INT2FIX(GDBM_WRCREAT|RUBY_GDBM_RW_BIT));
01267     /* flag for #new and #open: open database as a writer; overwrite any existing databases  */
01268     rb_define_const(rb_cGDBM, "NEWDB",   INT2FIX(GDBM_NEWDB|RUBY_GDBM_RW_BIT));
01269 
01270     /* flag for #new and #open. this flag is obsolete for gdbm >= 1.8 */
01271     rb_define_const(rb_cGDBM, "FAST", INT2FIX(GDBM_FAST));
01272     /* this flag is obsolete in gdbm 1.8.
01273        On gdbm 1.8, fast mode is default behavior. */
01274 
01275     /* gdbm version 1.8 specific */
01276 #if defined(GDBM_SYNC)
01277     /* flag for #new and #open. only for gdbm >= 1.8 */
01278     rb_define_const(rb_cGDBM, "SYNC",    INT2FIX(GDBM_SYNC));
01279 #endif
01280 #if defined(GDBM_NOLOCK)
01281     /* flag for #new and #open */
01282     rb_define_const(rb_cGDBM, "NOLOCK",  INT2FIX(GDBM_NOLOCK));
01283 #endif
01284     /* version of the gdbm library*/
01285     rb_define_const(rb_cGDBM, "VERSION",  rb_str_new2(gdbm_version));
01286 }
01287 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7