Ruby
2.0.0p247(2013-06-27revision41674)
|
00001 /************************************************ 00002 00003 sdbminit.c - 00004 00005 $Author: naruse $ 00006 created at: Fri May 7 08:34:24 JST 1999 00007 00008 Copyright (C) 1995-2001 Yukihiro Matsumoto 00009 00010 ************************************************/ 00011 00012 #include "ruby.h" 00013 00014 #include "sdbm.h" 00015 #include <fcntl.h> 00016 #include <errno.h> 00017 00018 /* 00019 * Document-class: SDBM 00020 * 00021 * SDBM provides a simple file-based key-value store, which can only store 00022 * String keys and values. 00023 * 00024 * Note that Ruby comes with the source code for SDBM, while the DBM and GDBM 00025 * standard libraries rely on external libraries and headers. 00026 * 00027 * === Examples 00028 * 00029 * Insert values: 00030 * 00031 * require 'sdbm' 00032 * 00033 * SDBM.open 'my_database' do |db| 00034 * db['apple'] = 'fruit' 00035 * db['pear'] = 'fruit' 00036 * db['carrot'] = 'vegetable' 00037 * db['tomato'] = 'vegetable' 00038 * end 00039 * 00040 * Bulk update: 00041 * 00042 * require 'sdbm' 00043 * 00044 * SDBM.open 'my_database' do |db| 00045 * db.update('peach' => 'fruit', 'tomato' => 'fruit') 00046 * end 00047 * 00048 * Retrieve values: 00049 * 00050 * require 'sdbm' 00051 * 00052 * SDBM.open 'my_database' do |db| 00053 * db.each do |key, value| 00054 * puts "Key: #{key}, Value: #{value}" 00055 * end 00056 * end 00057 * 00058 * Outputs: 00059 * 00060 * Key: apple, Value: fruit 00061 * Key: pear, Value: fruit 00062 * Key: carrot, Value: vegetable 00063 * Key: peach, Value: fruit 00064 * Key: tomato, Value: fruit 00065 */ 00066 00067 static VALUE rb_cDBM, rb_eDBMError; 00068 00069 struct dbmdata { 00070 int di_size; 00071 DBM *di_dbm; 00072 }; 00073 00074 static void 00075 closed_sdbm() 00076 { 00077 rb_raise(rb_eDBMError, "closed SDBM file"); 00078 } 00079 00080 #define GetDBM(obj, dbmp) {\ 00081 Data_Get_Struct((obj), struct dbmdata, (dbmp));\ 00082 if ((dbmp) == 0) closed_sdbm();\ 00083 if ((dbmp)->di_dbm == 0) closed_sdbm();\ 00084 } 00085 00086 #define GetDBM2(obj, data, dbm) {\ 00087 GetDBM((obj), (data));\ 00088 (dbm) = dbmp->di_dbm;\ 00089 } 00090 00091 static void 00092 free_sdbm(struct dbmdata *dbmp) 00093 { 00094 00095 if (dbmp->di_dbm) sdbm_close(dbmp->di_dbm); 00096 ruby_xfree(dbmp); 00097 } 00098 00099 /* 00100 * call-seq: 00101 * sdbm.close -> nil 00102 * 00103 * Closes the database file. 00104 * 00105 * Raises SDBMError if the database is already closed. 00106 */ 00107 static VALUE 00108 fsdbm_close(VALUE obj) 00109 { 00110 struct dbmdata *dbmp; 00111 00112 GetDBM(obj, dbmp); 00113 sdbm_close(dbmp->di_dbm); 00114 dbmp->di_dbm = 0; 00115 00116 return Qnil; 00117 } 00118 00119 /* 00120 * call-seq: 00121 * sdbm.closed? -> true or false 00122 * 00123 * Returns +true+ if the database is closed. 00124 */ 00125 static VALUE 00126 fsdbm_closed(VALUE obj) 00127 { 00128 struct dbmdata *dbmp; 00129 00130 Data_Get_Struct(obj, struct dbmdata, dbmp); 00131 if (dbmp == 0) 00132 return Qtrue; 00133 if (dbmp->di_dbm == 0) 00134 return Qtrue; 00135 00136 return Qfalse; 00137 } 00138 00139 static VALUE 00140 fsdbm_alloc(VALUE klass) 00141 { 00142 return Data_Wrap_Struct(klass, 0, free_sdbm, 0); 00143 } 00144 /* 00145 * call-seq: 00146 * SDBM.new(filename, mode = 0666) 00147 * 00148 * Creates a new database handle by opening the given +filename+. SDBM actually 00149 * uses two physical files, with extensions '.dir' and '.pag'. These extensions 00150 * will automatically be appended to the +filename+. 00151 * 00152 * If the file does not exist, a new file will be created using the given 00153 * +mode+, unless +mode+ is explicitly set to nil. In the latter case, no 00154 * database will be created. 00155 * 00156 * If the file exists, it will be opened in read/write mode. If this fails, it 00157 * will be opened in read-only mode. 00158 */ 00159 static VALUE 00160 fsdbm_initialize(int argc, VALUE *argv, VALUE obj) 00161 { 00162 volatile VALUE file; 00163 VALUE vmode; 00164 DBM *dbm; 00165 struct dbmdata *dbmp; 00166 int mode; 00167 00168 if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) { 00169 mode = 0666; /* default value */ 00170 } 00171 else if (NIL_P(vmode)) { 00172 mode = -1; /* return nil if DB not exist */ 00173 } 00174 else { 00175 mode = NUM2INT(vmode); 00176 } 00177 FilePathValue(file); 00178 00179 dbm = 0; 00180 if (mode >= 0) 00181 dbm = sdbm_open(RSTRING_PTR(file), O_RDWR|O_CREAT, mode); 00182 if (!dbm) 00183 dbm = sdbm_open(RSTRING_PTR(file), O_RDWR, 0); 00184 if (!dbm) 00185 dbm = sdbm_open(RSTRING_PTR(file), O_RDONLY, 0); 00186 00187 if (!dbm) { 00188 if (mode == -1) return Qnil; 00189 rb_sys_fail_str(file); 00190 } 00191 00192 dbmp = ALLOC(struct dbmdata); 00193 DATA_PTR(obj) = dbmp; 00194 dbmp->di_dbm = dbm; 00195 dbmp->di_size = -1; 00196 00197 return obj; 00198 } 00199 00200 /* 00201 * call-seq: 00202 * SDBM.open(filename, mode = 0666) 00203 * SDBM.open(filename, mode = 0666) { |sdbm| ... } 00204 * 00205 * If called without a block, this is the same as SDBM.new. 00206 * 00207 * If a block is given, the new database will be passed to the block and 00208 * will be safely closed after the block has executed. 00209 * 00210 * Example: 00211 * 00212 * require 'sdbm' 00213 * 00214 * SDBM.open('my_database') do |db| 00215 * db['hello'] = 'world' 00216 * end 00217 */ 00218 static VALUE 00219 fsdbm_s_open(int argc, VALUE *argv, VALUE klass) 00220 { 00221 VALUE obj = Data_Wrap_Struct(klass, 0, free_sdbm, 0); 00222 00223 if (NIL_P(fsdbm_initialize(argc, argv, obj))) { 00224 return Qnil; 00225 } 00226 00227 if (rb_block_given_p()) { 00228 return rb_ensure(rb_yield, obj, fsdbm_close, obj); 00229 } 00230 00231 return obj; 00232 } 00233 00234 static VALUE 00235 fsdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone) 00236 { 00237 datum key, value; 00238 struct dbmdata *dbmp; 00239 DBM *dbm; 00240 00241 ExportStringValue(keystr); 00242 key.dptr = RSTRING_PTR(keystr); 00243 key.dsize = RSTRING_LENINT(keystr); 00244 00245 GetDBM2(obj, dbmp, dbm); 00246 value = sdbm_fetch(dbm, key); 00247 if (value.dptr == 0) { 00248 if (ifnone == Qnil && rb_block_given_p()) 00249 return rb_yield(rb_external_str_new(key.dptr, key.dsize)); 00250 return ifnone; 00251 } 00252 return rb_external_str_new(value.dptr, value.dsize); 00253 } 00254 00255 /* 00256 * call-seq: 00257 * sdbm[key] -> value or nil 00258 * 00259 * Returns the +value+ in the database associated with the given +key+ string. 00260 * 00261 * If no value is found, returns +nil+. 00262 */ 00263 static VALUE 00264 fsdbm_aref(VALUE obj, VALUE keystr) 00265 { 00266 return fsdbm_fetch(obj, keystr, Qnil); 00267 } 00268 00269 /* 00270 * call-seq: 00271 * sdbm.fetch(key) -> value or nil 00272 * sdbm.fetch(key) { |key| ... } 00273 * 00274 * Returns the +value+ in the database associated with the given +key+ string. 00275 * 00276 * If a block is provided, the block will be called when there is no 00277 * +value+ associated with the given +key+. The +key+ will be passed in as an 00278 * argument to the block. 00279 * 00280 * If no block is provided and no value is associated with the given +key+, 00281 * then an IndexError will be raised. 00282 */ 00283 static VALUE 00284 fsdbm_fetch_m(int argc, VALUE *argv, VALUE obj) 00285 { 00286 VALUE keystr, valstr, ifnone; 00287 00288 rb_scan_args(argc, argv, "11", &keystr, &ifnone); 00289 valstr = fsdbm_fetch(obj, keystr, ifnone); 00290 if (argc == 1 && !rb_block_given_p() && NIL_P(valstr)) 00291 rb_raise(rb_eIndexError, "key not found"); 00292 00293 return valstr; 00294 } 00295 00296 /* 00297 * call-seq: 00298 * sdbm.key(value) -> key 00299 * 00300 * Returns the +key+ associated with the given +value+. If more than one 00301 * +key+ corresponds to the given +value+, then the first key to be found 00302 * will be returned. If no keys are found, +nil+ will be returned. 00303 */ 00304 static VALUE 00305 fsdbm_key(VALUE obj, VALUE valstr) 00306 { 00307 datum key, val; 00308 struct dbmdata *dbmp; 00309 DBM *dbm; 00310 00311 ExportStringValue(valstr); 00312 val.dptr = RSTRING_PTR(valstr); 00313 val.dsize = RSTRING_LENINT(valstr); 00314 00315 GetDBM2(obj, dbmp, dbm); 00316 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00317 val = sdbm_fetch(dbm, key); 00318 if (val.dsize == RSTRING_LEN(valstr) && 00319 memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0) 00320 return rb_external_str_new(key.dptr, key.dsize); 00321 } 00322 return Qnil; 00323 } 00324 00325 /* 00326 * :nodoc: 00327 */ 00328 static VALUE 00329 fsdbm_index(VALUE hash, VALUE value) 00330 { 00331 rb_warn("SDBM#index is deprecated; use SDBM#key"); 00332 return fsdbm_key(hash, value); 00333 } 00334 00335 /* call-seq: 00336 * sdbm.select { |key, value| ... } -> Array 00337 * 00338 * Returns a new Array of key-value pairs for which the block returns +true+. 00339 * 00340 * Example: 00341 * 00342 * require 'sdbm' 00343 * 00344 * SDBM.open 'my_database' do |db| 00345 * db['apple'] = 'fruit' 00346 * db['pear'] = 'fruit' 00347 * db['spinach'] = 'vegetable' 00348 * 00349 * veggies = db.select do |key, value| 00350 * value == 'vegetable' 00351 * end #=> [["apple", "fruit"], ["pear", "fruit"]] 00352 * end 00353 */ 00354 static VALUE 00355 fsdbm_select(VALUE obj) 00356 { 00357 VALUE new = rb_ary_new(); 00358 datum key, val; 00359 DBM *dbm; 00360 struct dbmdata *dbmp; 00361 00362 GetDBM2(obj, dbmp, dbm); 00363 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00364 VALUE assoc, v; 00365 val = sdbm_fetch(dbm, key); 00366 assoc = rb_assoc_new(rb_external_str_new(key.dptr, key.dsize), 00367 rb_external_str_new(val.dptr, val.dsize)); 00368 v = rb_yield(assoc); 00369 if (RTEST(v)) { 00370 rb_ary_push(new, assoc); 00371 } 00372 GetDBM2(obj, dbmp, dbm); 00373 } 00374 00375 return new; 00376 } 00377 00378 /* call-seq: 00379 * sdbm.values_at(key, ...) -> Array 00380 * 00381 * Returns an Array of values corresponding to the given keys. 00382 */ 00383 static VALUE 00384 fsdbm_values_at(int argc, VALUE *argv, VALUE obj) 00385 { 00386 VALUE new = rb_ary_new2(argc); 00387 int i; 00388 00389 for (i=0; i<argc; i++) { 00390 rb_ary_push(new, fsdbm_fetch(obj, argv[i], Qnil)); 00391 } 00392 00393 return new; 00394 } 00395 00396 static void 00397 fdbm_modify(VALUE obj) 00398 { 00399 rb_secure(4); 00400 if (OBJ_FROZEN(obj)) rb_error_frozen("SDBM"); 00401 } 00402 00403 /* 00404 * call-seq: 00405 * sdbm.delete(key) -> value or nil 00406 * sdbm.delete(key) { |key, value| ... } 00407 * 00408 * Deletes the key-value pair corresponding to the given +key+. If the 00409 * +key+ exists, the deleted value will be returned, otherwise +nil+. 00410 * 00411 * If a block is provided, the deleted +key+ and +value+ will be passed to 00412 * the block as arguments. If the +key+ does not exist in the database, the 00413 * value will be +nil+. 00414 */ 00415 static VALUE 00416 fsdbm_delete(VALUE obj, VALUE keystr) 00417 { 00418 datum key, value; 00419 struct dbmdata *dbmp; 00420 DBM *dbm; 00421 VALUE valstr; 00422 00423 fdbm_modify(obj); 00424 ExportStringValue(keystr); 00425 key.dptr = RSTRING_PTR(keystr); 00426 key.dsize = RSTRING_LENINT(keystr); 00427 00428 GetDBM2(obj, dbmp, dbm); 00429 dbmp->di_size = -1; 00430 00431 value = sdbm_fetch(dbm, key); 00432 if (value.dptr == 0) { 00433 if (rb_block_given_p()) return rb_yield(keystr); 00434 return Qnil; 00435 } 00436 00437 /* need to save value before sdbm_delete() */ 00438 valstr = rb_external_str_new(value.dptr, value.dsize); 00439 00440 if (sdbm_delete(dbm, key)) { 00441 dbmp->di_size = -1; 00442 rb_raise(rb_eDBMError, "dbm_delete failed"); 00443 } 00444 else if (dbmp->di_size >= 0) { 00445 dbmp->di_size--; 00446 } 00447 return valstr; 00448 } 00449 00450 /* 00451 * call-seq: 00452 * sdbm.shift -> Array or nil 00453 * 00454 * Removes a key-value pair from the database and returns them as an 00455 * Array. If the database is empty, returns +nil+. 00456 */ 00457 static VALUE 00458 fsdbm_shift(VALUE obj) 00459 { 00460 datum key, val; 00461 struct dbmdata *dbmp; 00462 DBM *dbm; 00463 VALUE keystr, valstr; 00464 00465 fdbm_modify(obj); 00466 GetDBM2(obj, dbmp, dbm); 00467 key = sdbm_firstkey(dbm); 00468 if (!key.dptr) return Qnil; 00469 val = sdbm_fetch(dbm, key); 00470 keystr = rb_external_str_new(key.dptr, key.dsize); 00471 valstr = rb_external_str_new(val.dptr, val.dsize); 00472 sdbm_delete(dbm, key); 00473 if (dbmp->di_size >= 0) { 00474 dbmp->di_size--; 00475 } 00476 00477 return rb_assoc_new(keystr, valstr); 00478 } 00479 00480 /* 00481 * call-seq: 00482 * sdbm.delete_if { |key, value| ... } -> self 00483 * sdbm.reject! { |key, value| ... } -> self 00484 * 00485 * Iterates over the key-value pairs in the database, deleting those for 00486 * which the block returns +true+. 00487 */ 00488 static VALUE 00489 fsdbm_delete_if(VALUE obj) 00490 { 00491 datum key, val; 00492 struct dbmdata *dbmp; 00493 DBM *dbm; 00494 VALUE keystr, valstr; 00495 VALUE ret, ary = rb_ary_new(); 00496 int i, status = 0, n; 00497 00498 fdbm_modify(obj); 00499 GetDBM2(obj, dbmp, dbm); 00500 n = dbmp->di_size; 00501 dbmp->di_size = -1; 00502 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00503 val = sdbm_fetch(dbm, key); 00504 keystr = rb_external_str_new(key.dptr, key.dsize); 00505 valstr = rb_external_str_new(val.dptr, val.dsize); 00506 ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status); 00507 if (status != 0) break; 00508 if (RTEST(ret)) rb_ary_push(ary, keystr); 00509 GetDBM2(obj, dbmp, dbm); 00510 } 00511 00512 for (i = 0; i < RARRAY_LEN(ary); i++) { 00513 keystr = RARRAY_PTR(ary)[i]; 00514 ExportStringValue(keystr); 00515 key.dptr = RSTRING_PTR(keystr); 00516 key.dsize = RSTRING_LENINT(keystr); 00517 if (sdbm_delete(dbm, key)) { 00518 rb_raise(rb_eDBMError, "sdbm_delete failed"); 00519 } 00520 } 00521 if (status) rb_jump_tag(status); 00522 if (n > 0) dbmp->di_size = n - RARRAY_LENINT(ary); 00523 00524 return obj; 00525 } 00526 00527 /* 00528 * call-seq: 00529 * sdbm.clear -> self 00530 * 00531 * Deletes all data from the database. 00532 */ 00533 static VALUE 00534 fsdbm_clear(VALUE obj) 00535 { 00536 datum key; 00537 struct dbmdata *dbmp; 00538 DBM *dbm; 00539 00540 fdbm_modify(obj); 00541 GetDBM2(obj, dbmp, dbm); 00542 dbmp->di_size = -1; 00543 while (key = sdbm_firstkey(dbm), key.dptr) { 00544 if (sdbm_delete(dbm, key)) { 00545 rb_raise(rb_eDBMError, "sdbm_delete failed"); 00546 } 00547 } 00548 dbmp->di_size = 0; 00549 00550 return obj; 00551 } 00552 00553 /* 00554 * call-seq: 00555 * sdbm.invert -> Hash 00556 * 00557 * Returns a Hash in which the key-value pairs have been inverted. 00558 * 00559 * Example: 00560 * 00561 * require 'sdbm' 00562 * 00563 * SDBM.open 'my_database' do |db| 00564 * db.update('apple' => 'fruit', 'spinach' => 'vegetable') 00565 * 00566 * db.invert #=> {"fruit" => "apple", "vegetable" => "spinach"} 00567 * end 00568 */ 00569 static VALUE 00570 fsdbm_invert(VALUE obj) 00571 { 00572 datum key, val; 00573 struct dbmdata *dbmp; 00574 DBM *dbm; 00575 VALUE keystr, valstr; 00576 VALUE hash = rb_hash_new(); 00577 00578 GetDBM2(obj, dbmp, dbm); 00579 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00580 val = sdbm_fetch(dbm, key); 00581 keystr = rb_external_str_new(key.dptr, key.dsize); 00582 valstr = rb_external_str_new(val.dptr, val.dsize); 00583 rb_hash_aset(hash, valstr, keystr); 00584 } 00585 return hash; 00586 } 00587 00588 /* 00589 * call-seq: 00590 * sdbm[key] = value -> value 00591 * sdbm.store(key, value) -> value 00592 * 00593 * Stores a new +value+ in the database with the given +key+ as an index. 00594 * 00595 * If the +key+ already exists, this will update the +value+ associated with 00596 * the +key+. 00597 * 00598 * Returns the given +value+. 00599 */ 00600 static VALUE 00601 fsdbm_store(VALUE obj, VALUE keystr, VALUE valstr) 00602 { 00603 datum key, val; 00604 struct dbmdata *dbmp; 00605 DBM *dbm; 00606 00607 if (valstr == Qnil) { 00608 fsdbm_delete(obj, keystr); 00609 return Qnil; 00610 } 00611 00612 fdbm_modify(obj); 00613 ExportStringValue(keystr); 00614 ExportStringValue(valstr); 00615 00616 key.dptr = RSTRING_PTR(keystr); 00617 key.dsize = RSTRING_LENINT(keystr); 00618 00619 val.dptr = RSTRING_PTR(valstr); 00620 val.dsize = RSTRING_LENINT(valstr); 00621 00622 GetDBM2(obj, dbmp, dbm); 00623 dbmp->di_size = -1; 00624 if (sdbm_store(dbm, key, val, DBM_REPLACE)) { 00625 #ifdef HAVE_DBM_CLAERERR 00626 sdbm_clearerr(dbm); 00627 #endif 00628 if (errno == EPERM) rb_sys_fail(0); 00629 rb_raise(rb_eDBMError, "sdbm_store failed"); 00630 } 00631 00632 return valstr; 00633 } 00634 00635 static VALUE 00636 update_i(VALUE pair, VALUE dbm) 00637 { 00638 Check_Type(pair, T_ARRAY); 00639 if (RARRAY_LEN(pair) < 2) { 00640 rb_raise(rb_eArgError, "pair must be [key, value]"); 00641 } 00642 fsdbm_store(dbm, RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1]); 00643 return Qnil; 00644 } 00645 00646 /* 00647 * call-seq: 00648 * sdbm.update(pairs) -> self 00649 * 00650 * Insert or update key-value pairs. 00651 * 00652 * This method will work with any object which implements an each_pair 00653 * method, such as a Hash. 00654 */ 00655 static VALUE 00656 fsdbm_update(VALUE obj, VALUE other) 00657 { 00658 rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj); 00659 return obj; 00660 } 00661 00662 /* 00663 * call-seq: 00664 * sdbm.replace(pairs) -> self 00665 * 00666 * Empties the database, then inserts the given key-value pairs. 00667 * 00668 * This method will work with any object which implements an each_pair 00669 * method, such as a Hash. 00670 */ 00671 static VALUE 00672 fsdbm_replace(VALUE obj, VALUE other) 00673 { 00674 fsdbm_clear(obj); 00675 rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj); 00676 return obj; 00677 } 00678 00679 /* 00680 * call-seq: 00681 * sdbm.length -> integer 00682 * sdbm.size -> integer 00683 * 00684 * Returns the number of keys in the database. 00685 */ 00686 static VALUE 00687 fsdbm_length(VALUE obj) 00688 { 00689 datum key; 00690 struct dbmdata *dbmp; 00691 DBM *dbm; 00692 int i = 0; 00693 00694 GetDBM2(obj, dbmp, dbm); 00695 if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size); 00696 00697 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00698 i++; 00699 } 00700 dbmp->di_size = i; 00701 00702 return INT2FIX(i); 00703 } 00704 00705 /* 00706 * call-seq: 00707 * sdbm.empty? -> true or false 00708 * 00709 * Returns +true+ if the database is empty. 00710 */ 00711 static VALUE 00712 fsdbm_empty_p(VALUE obj) 00713 { 00714 datum key; 00715 struct dbmdata *dbmp; 00716 DBM *dbm; 00717 00718 GetDBM(obj, dbmp); 00719 if (dbmp->di_size < 0) { 00720 dbm = dbmp->di_dbm; 00721 00722 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00723 return Qfalse; 00724 } 00725 } 00726 else { 00727 if (dbmp->di_size) 00728 return Qfalse; 00729 } 00730 return Qtrue; 00731 } 00732 00733 /* 00734 * call-seq: 00735 * sdbm.each_value 00736 * sdbm.each_value { |value| ... } 00737 * 00738 * Iterates over each +value+ in the database. 00739 * 00740 * If no block is given, returns an Enumerator. 00741 */ 00742 static VALUE 00743 fsdbm_each_value(VALUE obj) 00744 { 00745 datum key, val; 00746 struct dbmdata *dbmp; 00747 DBM *dbm; 00748 00749 RETURN_ENUMERATOR(obj, 0, 0); 00750 00751 GetDBM2(obj, dbmp, dbm); 00752 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00753 val = sdbm_fetch(dbm, key); 00754 rb_yield(rb_external_str_new(val.dptr, val.dsize)); 00755 GetDBM2(obj, dbmp, dbm); 00756 } 00757 return obj; 00758 } 00759 00760 /* 00761 * call-seq: 00762 * sdbm.each_key 00763 * sdbm.each_key { |key| ... } 00764 * 00765 * Iterates over each +key+ in the database. 00766 * 00767 * If no block is given, returns an Enumerator. 00768 */ 00769 static VALUE 00770 fsdbm_each_key(VALUE obj) 00771 { 00772 datum key; 00773 struct dbmdata *dbmp; 00774 DBM *dbm; 00775 00776 RETURN_ENUMERATOR(obj, 0, 0); 00777 00778 GetDBM2(obj, dbmp, dbm); 00779 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00780 rb_yield(rb_external_str_new(key.dptr, key.dsize)); 00781 GetDBM2(obj, dbmp, dbm); 00782 } 00783 return obj; 00784 } 00785 00786 /* 00787 * call-seq: 00788 * sdbm.each 00789 * sdbm.each { |key, value| ... } 00790 * sdbm.each_pair 00791 * sdbm.each_pair { |key, value| ... } 00792 * 00793 * Iterates over each key-value pair in the database. 00794 * 00795 * If no block is given, returns an Enumerator. 00796 */ 00797 static VALUE 00798 fsdbm_each_pair(VALUE obj) 00799 { 00800 datum key, val; 00801 DBM *dbm; 00802 struct dbmdata *dbmp; 00803 VALUE keystr, valstr; 00804 00805 RETURN_ENUMERATOR(obj, 0, 0); 00806 00807 GetDBM2(obj, dbmp, dbm); 00808 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00809 val = sdbm_fetch(dbm, key); 00810 keystr = rb_external_str_new(key.dptr, key.dsize); 00811 valstr = rb_external_str_new(val.dptr, val.dsize); 00812 rb_yield(rb_assoc_new(keystr, valstr)); 00813 GetDBM2(obj, dbmp, dbm); 00814 } 00815 00816 return obj; 00817 } 00818 00819 /* 00820 * call-seq: 00821 * sdbm.keys -> Array 00822 * 00823 * Returns a new Array containing the keys in the database. 00824 */ 00825 static VALUE 00826 fsdbm_keys(VALUE obj) 00827 { 00828 datum key; 00829 struct dbmdata *dbmp; 00830 DBM *dbm; 00831 VALUE ary; 00832 00833 GetDBM2(obj, dbmp, dbm); 00834 ary = rb_ary_new(); 00835 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00836 rb_ary_push(ary, rb_external_str_new(key.dptr, key.dsize)); 00837 } 00838 00839 return ary; 00840 } 00841 00842 /* 00843 * call-seq: 00844 * sdbm.values -> Array 00845 * 00846 * Returns a new Array containing the values in the database. 00847 */ 00848 static VALUE 00849 fsdbm_values(VALUE obj) 00850 { 00851 datum key, val; 00852 struct dbmdata *dbmp; 00853 DBM *dbm; 00854 VALUE ary; 00855 00856 GetDBM2(obj, dbmp, dbm); 00857 ary = rb_ary_new(); 00858 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00859 val = sdbm_fetch(dbm, key); 00860 rb_ary_push(ary, rb_external_str_new(val.dptr, val.dsize)); 00861 } 00862 00863 return ary; 00864 } 00865 00866 /* 00867 * call-seq: 00868 * sdbm.include?(key) -> true or false 00869 * sdbm.key?(key) -> true or false 00870 * sdbm.member?(key) -> true or false 00871 * sdbm.has_key?(key) -> true or false 00872 * 00873 * Returns +true+ if the database contains the given +key+. 00874 */ 00875 static VALUE 00876 fsdbm_has_key(VALUE obj, VALUE keystr) 00877 { 00878 datum key, val; 00879 struct dbmdata *dbmp; 00880 DBM *dbm; 00881 00882 ExportStringValue(keystr); 00883 key.dptr = RSTRING_PTR(keystr); 00884 key.dsize = RSTRING_LENINT(keystr); 00885 00886 GetDBM2(obj, dbmp, dbm); 00887 val = sdbm_fetch(dbm, key); 00888 if (val.dptr) return Qtrue; 00889 return Qfalse; 00890 } 00891 00892 /* 00893 * call-seq: 00894 * sdbm.value?(key) -> true or false 00895 * sdbm.has_value?(key) -> true or false 00896 * 00897 * Returns +true+ if the database contains the given +value+. 00898 */ 00899 static VALUE 00900 fsdbm_has_value(VALUE obj, VALUE valstr) 00901 { 00902 datum key, val; 00903 struct dbmdata *dbmp; 00904 DBM *dbm; 00905 00906 ExportStringValue(valstr); 00907 val.dptr = RSTRING_PTR(valstr); 00908 val.dsize = RSTRING_LENINT(valstr); 00909 00910 GetDBM2(obj, dbmp, dbm); 00911 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00912 val = sdbm_fetch(dbm, key); 00913 if (val.dsize == RSTRING_LENINT(valstr) && 00914 memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0) 00915 return Qtrue; 00916 } 00917 return Qfalse; 00918 } 00919 00920 /* 00921 * call-seq: 00922 * sdbm.to_a -> Array 00923 * 00924 * Returns a new Array containing each key-value pair in the database. 00925 * 00926 * Example: 00927 * 00928 * require 'sdbm' 00929 * 00930 * SDBM.open 'my_database' do |db| 00931 * db.update('apple' => 'fruit', 'spinach' => 'vegetable') 00932 * 00933 * db.to_a #=> [["apple", "fruit"], ["spinach", "vegetable"]] 00934 * end 00935 */ 00936 static VALUE 00937 fsdbm_to_a(VALUE obj) 00938 { 00939 datum key, val; 00940 struct dbmdata *dbmp; 00941 DBM *dbm; 00942 VALUE ary; 00943 00944 GetDBM2(obj, dbmp, dbm); 00945 ary = rb_ary_new(); 00946 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00947 val = sdbm_fetch(dbm, key); 00948 rb_ary_push(ary, rb_assoc_new(rb_external_str_new(key.dptr, key.dsize), 00949 rb_external_str_new(val.dptr, val.dsize))); 00950 } 00951 00952 return ary; 00953 } 00954 00955 /* 00956 * call-seq: 00957 * sdbm.to_hash -> Hash 00958 * 00959 * Returns a new Hash containing each key-value pair in the database. 00960 */ 00961 static VALUE 00962 fsdbm_to_hash(VALUE obj) 00963 { 00964 datum key, val; 00965 struct dbmdata *dbmp; 00966 DBM *dbm; 00967 VALUE hash; 00968 00969 GetDBM2(obj, dbmp, dbm); 00970 hash = rb_hash_new(); 00971 for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) { 00972 val = sdbm_fetch(dbm, key); 00973 rb_hash_aset(hash, rb_external_str_new(key.dptr, key.dsize), 00974 rb_external_str_new(val.dptr, val.dsize)); 00975 } 00976 00977 return hash; 00978 } 00979 00980 /* 00981 * call-seq: 00982 * sdbm.reject { |key, value| ... } -> Hash 00983 * 00984 * Creates a new Hash using the key-value pairs from the database, then 00985 * calls Hash#reject with the given block, which returns a Hash with 00986 * only the key-value pairs for which the block returns +false+. 00987 */ 00988 static VALUE 00989 fsdbm_reject(VALUE obj) 00990 { 00991 return rb_hash_delete_if(fsdbm_to_hash(obj)); 00992 } 00993 00994 void 00995 Init_sdbm() 00996 { 00997 rb_cDBM = rb_define_class("SDBM", rb_cObject); 00998 rb_eDBMError = rb_define_class("SDBMError", rb_eStandardError); 00999 /* Document-class: SDBMError 01000 * Exception class used to return errors from the sdbm library. 01001 */ 01002 rb_include_module(rb_cDBM, rb_mEnumerable); 01003 01004 rb_define_alloc_func(rb_cDBM, fsdbm_alloc); 01005 rb_define_singleton_method(rb_cDBM, "open", fsdbm_s_open, -1); 01006 01007 rb_define_method(rb_cDBM, "initialize", fsdbm_initialize, -1); 01008 rb_define_method(rb_cDBM, "close", fsdbm_close, 0); 01009 rb_define_method(rb_cDBM, "closed?", fsdbm_closed, 0); 01010 rb_define_method(rb_cDBM, "[]", fsdbm_aref, 1); 01011 rb_define_method(rb_cDBM, "fetch", fsdbm_fetch_m, -1); 01012 rb_define_method(rb_cDBM, "[]=", fsdbm_store, 2); 01013 rb_define_method(rb_cDBM, "store", fsdbm_store, 2); 01014 rb_define_method(rb_cDBM, "index", fsdbm_index, 1); 01015 rb_define_method(rb_cDBM, "key", fsdbm_key, 1); 01016 rb_define_method(rb_cDBM, "select", fsdbm_select, 0); 01017 rb_define_method(rb_cDBM, "values_at", fsdbm_values_at, -1); 01018 rb_define_method(rb_cDBM, "length", fsdbm_length, 0); 01019 rb_define_method(rb_cDBM, "size", fsdbm_length, 0); 01020 rb_define_method(rb_cDBM, "empty?", fsdbm_empty_p, 0); 01021 rb_define_method(rb_cDBM, "each", fsdbm_each_pair, 0); 01022 rb_define_method(rb_cDBM, "each_value", fsdbm_each_value, 0); 01023 rb_define_method(rb_cDBM, "each_key", fsdbm_each_key, 0); 01024 rb_define_method(rb_cDBM, "each_pair", fsdbm_each_pair, 0); 01025 rb_define_method(rb_cDBM, "keys", fsdbm_keys, 0); 01026 rb_define_method(rb_cDBM, "values", fsdbm_values, 0); 01027 rb_define_method(rb_cDBM, "shift", fsdbm_shift, 0); 01028 rb_define_method(rb_cDBM, "delete", fsdbm_delete, 1); 01029 rb_define_method(rb_cDBM, "delete_if", fsdbm_delete_if, 0); 01030 rb_define_method(rb_cDBM, "reject!", fsdbm_delete_if, 0); 01031 rb_define_method(rb_cDBM, "reject", fsdbm_reject, 0); 01032 rb_define_method(rb_cDBM, "clear", fsdbm_clear, 0); 01033 rb_define_method(rb_cDBM,"invert", fsdbm_invert, 0); 01034 rb_define_method(rb_cDBM,"update", fsdbm_update, 1); 01035 rb_define_method(rb_cDBM,"replace", fsdbm_replace, 1); 01036 01037 rb_define_method(rb_cDBM, "has_key?", fsdbm_has_key, 1); 01038 rb_define_method(rb_cDBM, "include?", fsdbm_has_key, 1); 01039 rb_define_method(rb_cDBM, "key?", fsdbm_has_key, 1); 01040 rb_define_method(rb_cDBM, "member?", fsdbm_has_key, 1); 01041 rb_define_method(rb_cDBM, "has_value?", fsdbm_has_value, 1); 01042 rb_define_method(rb_cDBM, "value?", fsdbm_has_value, 1); 01043 01044 rb_define_method(rb_cDBM, "to_a", fsdbm_to_a, 0); 01045 rb_define_method(rb_cDBM, "to_hash", fsdbm_to_hash, 0); 01046 } 01047