PageRenderTime 69ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/sdbm/init.c

https://github.com/vuxuandung/ruby
C | 1046 lines | 588 code | 113 blank | 345 comment | 82 complexity | 413c71920e09e9b534b1f66807b0c3d4 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0, 0BSD
  1. /************************************************
  2. sdbminit.c -
  3. $Author$
  4. created at: Fri May 7 08:34:24 JST 1999
  5. Copyright (C) 1995-2001 Yukihiro Matsumoto
  6. ************************************************/
  7. #include "ruby.h"
  8. #include "sdbm.h"
  9. #include <fcntl.h>
  10. #include <errno.h>
  11. /*
  12. * Document-class: SDBM
  13. *
  14. * SDBM provides a simple file-based key-value store, which can only store
  15. * String keys and values.
  16. *
  17. * Note that Ruby comes with the source code for SDBM, while the DBM and GDBM
  18. * standard libraries rely on external libraries and headers.
  19. *
  20. * === Examples
  21. *
  22. * Insert values:
  23. *
  24. * require 'sdbm'
  25. *
  26. * SDBM.open 'my_database' do |db|
  27. * db['apple'] = 'fruit'
  28. * db['pear'] = 'fruit'
  29. * db['carrot'] = 'vegetable'
  30. * db['tomato'] = 'vegetable'
  31. * end
  32. *
  33. * Bulk update:
  34. *
  35. * require 'sdbm'
  36. *
  37. * SDBM.open 'my_database' do |db|
  38. * db.update('peach' => 'fruit', 'tomato' => 'fruit')
  39. * end
  40. *
  41. * Retrieve values:
  42. *
  43. * require 'sdbm'
  44. *
  45. * SDBM.open 'my_database' do |db|
  46. * db.each do |key, value|
  47. * puts "Key: #{key}, Value: #{value}"
  48. * end
  49. * end
  50. *
  51. * Outputs:
  52. *
  53. * Key: apple, Value: fruit
  54. * Key: pear, Value: fruit
  55. * Key: carrot, Value: vegetable
  56. * Key: peach, Value: fruit
  57. * Key: tomato, Value: fruit
  58. */
  59. static VALUE rb_cDBM, rb_eDBMError;
  60. struct dbmdata {
  61. int di_size;
  62. DBM *di_dbm;
  63. };
  64. static void
  65. closed_sdbm()
  66. {
  67. rb_raise(rb_eDBMError, "closed SDBM file");
  68. }
  69. #define GetDBM(obj, dbmp) {\
  70. Data_Get_Struct((obj), struct dbmdata, (dbmp));\
  71. if ((dbmp) == 0) closed_sdbm();\
  72. if ((dbmp)->di_dbm == 0) closed_sdbm();\
  73. }
  74. #define GetDBM2(obj, data, dbm) {\
  75. GetDBM((obj), (data));\
  76. (dbm) = dbmp->di_dbm;\
  77. }
  78. static void
  79. free_sdbm(struct dbmdata *dbmp)
  80. {
  81. if (dbmp->di_dbm) sdbm_close(dbmp->di_dbm);
  82. ruby_xfree(dbmp);
  83. }
  84. /*
  85. * call-seq:
  86. * sdbm.close -> nil
  87. *
  88. * Closes the database file.
  89. *
  90. * Raises SDBMError if the database is already closed.
  91. */
  92. static VALUE
  93. fsdbm_close(VALUE obj)
  94. {
  95. struct dbmdata *dbmp;
  96. GetDBM(obj, dbmp);
  97. sdbm_close(dbmp->di_dbm);
  98. dbmp->di_dbm = 0;
  99. return Qnil;
  100. }
  101. /*
  102. * call-seq:
  103. * sdbm.closed? -> true or false
  104. *
  105. * Returns +true+ if the database is closed.
  106. */
  107. static VALUE
  108. fsdbm_closed(VALUE obj)
  109. {
  110. struct dbmdata *dbmp;
  111. Data_Get_Struct(obj, struct dbmdata, dbmp);
  112. if (dbmp == 0)
  113. return Qtrue;
  114. if (dbmp->di_dbm == 0)
  115. return Qtrue;
  116. return Qfalse;
  117. }
  118. static VALUE
  119. fsdbm_alloc(VALUE klass)
  120. {
  121. return Data_Wrap_Struct(klass, 0, free_sdbm, 0);
  122. }
  123. /*
  124. * call-seq:
  125. * SDBM.new(filename, mode = 0666)
  126. *
  127. * Creates a new database handle by opening the given +filename+. SDBM actually
  128. * uses two physical files, with extensions '.dir' and '.pag'. These extensions
  129. * will automatically be appended to the +filename+.
  130. *
  131. * If the file does not exist, a new file will be created using the given
  132. * +mode+, unless +mode+ is explicitly set to nil. In the latter case, no
  133. * database will be created.
  134. *
  135. * If the file exists, it will be opened in read/write mode. If this fails, it
  136. * will be opened in read-only mode.
  137. */
  138. static VALUE
  139. fsdbm_initialize(int argc, VALUE *argv, VALUE obj)
  140. {
  141. volatile VALUE file;
  142. VALUE vmode;
  143. DBM *dbm;
  144. struct dbmdata *dbmp;
  145. int mode;
  146. if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) {
  147. mode = 0666; /* default value */
  148. }
  149. else if (NIL_P(vmode)) {
  150. mode = -1; /* return nil if DB not exist */
  151. }
  152. else {
  153. mode = NUM2INT(vmode);
  154. }
  155. FilePathValue(file);
  156. dbm = 0;
  157. if (mode >= 0)
  158. dbm = sdbm_open(RSTRING_PTR(file), O_RDWR|O_CREAT, mode);
  159. if (!dbm)
  160. dbm = sdbm_open(RSTRING_PTR(file), O_RDWR, 0);
  161. if (!dbm)
  162. dbm = sdbm_open(RSTRING_PTR(file), O_RDONLY, 0);
  163. if (!dbm) {
  164. if (mode == -1) return Qnil;
  165. rb_sys_fail_str(file);
  166. }
  167. dbmp = ALLOC(struct dbmdata);
  168. DATA_PTR(obj) = dbmp;
  169. dbmp->di_dbm = dbm;
  170. dbmp->di_size = -1;
  171. return obj;
  172. }
  173. /*
  174. * call-seq:
  175. * SDBM.open(filename, mode = 0666)
  176. * SDBM.open(filename, mode = 0666) { |sdbm| ... }
  177. *
  178. * If called without a block, this is the same as SDBM.new.
  179. *
  180. * If a block is given, the new database will be passed to the block and
  181. * will be safely closed after the block has executed.
  182. *
  183. * Example:
  184. *
  185. * require 'sdbm'
  186. *
  187. * SDBM.open('my_database') do |db|
  188. * db['hello'] = 'world'
  189. * end
  190. */
  191. static VALUE
  192. fsdbm_s_open(int argc, VALUE *argv, VALUE klass)
  193. {
  194. VALUE obj = Data_Wrap_Struct(klass, 0, free_sdbm, 0);
  195. if (NIL_P(fsdbm_initialize(argc, argv, obj))) {
  196. return Qnil;
  197. }
  198. if (rb_block_given_p()) {
  199. return rb_ensure(rb_yield, obj, fsdbm_close, obj);
  200. }
  201. return obj;
  202. }
  203. static VALUE
  204. fsdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone)
  205. {
  206. datum key, value;
  207. struct dbmdata *dbmp;
  208. DBM *dbm;
  209. ExportStringValue(keystr);
  210. key.dptr = RSTRING_PTR(keystr);
  211. key.dsize = RSTRING_LENINT(keystr);
  212. GetDBM2(obj, dbmp, dbm);
  213. value = sdbm_fetch(dbm, key);
  214. if (value.dptr == 0) {
  215. if (ifnone == Qnil && rb_block_given_p())
  216. return rb_yield(rb_external_str_new(key.dptr, key.dsize));
  217. return ifnone;
  218. }
  219. return rb_external_str_new(value.dptr, value.dsize);
  220. }
  221. /*
  222. * call-seq:
  223. * sdbm[key] -> value or nil
  224. *
  225. * Returns the +value+ in the database associated with the given +key+ string.
  226. *
  227. * If no value is found, returns +nil+.
  228. */
  229. static VALUE
  230. fsdbm_aref(VALUE obj, VALUE keystr)
  231. {
  232. return fsdbm_fetch(obj, keystr, Qnil);
  233. }
  234. /*
  235. * call-seq:
  236. * sdbm.fetch(key) -> value or nil
  237. * sdbm.fetch(key) { |key| ... }
  238. *
  239. * Returns the +value+ in the database associated with the given +key+ string.
  240. *
  241. * If a block is provided, the block will be called when there is no
  242. * +value+ associated with the given +key+. The +key+ will be passed in as an
  243. * argument to the block.
  244. *
  245. * If no block is provided and no value is associated with the given +key+,
  246. * then an IndexError will be raised.
  247. */
  248. static VALUE
  249. fsdbm_fetch_m(int argc, VALUE *argv, VALUE obj)
  250. {
  251. VALUE keystr, valstr, ifnone;
  252. rb_scan_args(argc, argv, "11", &keystr, &ifnone);
  253. valstr = fsdbm_fetch(obj, keystr, ifnone);
  254. if (argc == 1 && !rb_block_given_p() && NIL_P(valstr))
  255. rb_raise(rb_eIndexError, "key not found");
  256. return valstr;
  257. }
  258. /*
  259. * call-seq:
  260. * sdbm.key(value) -> key
  261. *
  262. * Returns the +key+ associated with the given +value+. If more than one
  263. * +key+ corresponds to the given +value+, then the first key to be found
  264. * will be returned. If no keys are found, +nil+ will be returned.
  265. */
  266. static VALUE
  267. fsdbm_key(VALUE obj, VALUE valstr)
  268. {
  269. datum key, val;
  270. struct dbmdata *dbmp;
  271. DBM *dbm;
  272. ExportStringValue(valstr);
  273. val.dptr = RSTRING_PTR(valstr);
  274. val.dsize = RSTRING_LENINT(valstr);
  275. GetDBM2(obj, dbmp, dbm);
  276. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  277. val = sdbm_fetch(dbm, key);
  278. if (val.dsize == RSTRING_LEN(valstr) &&
  279. memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0)
  280. return rb_external_str_new(key.dptr, key.dsize);
  281. }
  282. return Qnil;
  283. }
  284. /*
  285. * :nodoc:
  286. */
  287. static VALUE
  288. fsdbm_index(VALUE hash, VALUE value)
  289. {
  290. rb_warn("SDBM#index is deprecated; use SDBM#key");
  291. return fsdbm_key(hash, value);
  292. }
  293. /* call-seq:
  294. * sdbm.select { |key, value| ... } -> Array
  295. *
  296. * Returns a new Array of key-value pairs for which the block returns +true+.
  297. *
  298. * Example:
  299. *
  300. * require 'sdbm'
  301. *
  302. * SDBM.open 'my_database' do |db|
  303. * db['apple'] = 'fruit'
  304. * db['pear'] = 'fruit'
  305. * db['spinach'] = 'vegetable'
  306. *
  307. * veggies = db.select do |key, value|
  308. * value == 'vegetable'
  309. * end #=> [["apple", "fruit"], ["pear", "fruit"]]
  310. * end
  311. */
  312. static VALUE
  313. fsdbm_select(VALUE obj)
  314. {
  315. VALUE new = rb_ary_new();
  316. datum key, val;
  317. DBM *dbm;
  318. struct dbmdata *dbmp;
  319. GetDBM2(obj, dbmp, dbm);
  320. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  321. VALUE assoc, v;
  322. val = sdbm_fetch(dbm, key);
  323. assoc = rb_assoc_new(rb_external_str_new(key.dptr, key.dsize),
  324. rb_external_str_new(val.dptr, val.dsize));
  325. v = rb_yield(assoc);
  326. if (RTEST(v)) {
  327. rb_ary_push(new, assoc);
  328. }
  329. GetDBM2(obj, dbmp, dbm);
  330. }
  331. return new;
  332. }
  333. /* call-seq:
  334. * sdbm.values_at(key, ...) -> Array
  335. *
  336. * Returns an Array of values corresponding to the given keys.
  337. */
  338. static VALUE
  339. fsdbm_values_at(int argc, VALUE *argv, VALUE obj)
  340. {
  341. VALUE new = rb_ary_new2(argc);
  342. int i;
  343. for (i=0; i<argc; i++) {
  344. rb_ary_push(new, fsdbm_fetch(obj, argv[i], Qnil));
  345. }
  346. return new;
  347. }
  348. static void
  349. fdbm_modify(VALUE obj)
  350. {
  351. rb_secure(4);
  352. if (OBJ_FROZEN(obj)) rb_error_frozen("SDBM");
  353. }
  354. /*
  355. * call-seq:
  356. * sdbm.delete(key) -> value or nil
  357. * sdbm.delete(key) { |key, value| ... }
  358. *
  359. * Deletes the key-value pair corresponding to the given +key+. If the
  360. * +key+ exists, the deleted value will be returned, otherwise +nil+.
  361. *
  362. * If a block is provided, the deleted +key+ and +value+ will be passed to
  363. * the block as arguments. If the +key+ does not exist in the database, the
  364. * value will be +nil+.
  365. */
  366. static VALUE
  367. fsdbm_delete(VALUE obj, VALUE keystr)
  368. {
  369. datum key, value;
  370. struct dbmdata *dbmp;
  371. DBM *dbm;
  372. VALUE valstr;
  373. fdbm_modify(obj);
  374. ExportStringValue(keystr);
  375. key.dptr = RSTRING_PTR(keystr);
  376. key.dsize = RSTRING_LENINT(keystr);
  377. GetDBM2(obj, dbmp, dbm);
  378. dbmp->di_size = -1;
  379. value = sdbm_fetch(dbm, key);
  380. if (value.dptr == 0) {
  381. if (rb_block_given_p()) return rb_yield(keystr);
  382. return Qnil;
  383. }
  384. /* need to save value before sdbm_delete() */
  385. valstr = rb_external_str_new(value.dptr, value.dsize);
  386. if (sdbm_delete(dbm, key)) {
  387. dbmp->di_size = -1;
  388. rb_raise(rb_eDBMError, "dbm_delete failed");
  389. }
  390. else if (dbmp->di_size >= 0) {
  391. dbmp->di_size--;
  392. }
  393. return valstr;
  394. }
  395. /*
  396. * call-seq:
  397. * sdbm.shift -> Array or nil
  398. *
  399. * Removes a key-value pair from the database and returns them as an
  400. * Array. If the database is empty, returns +nil+.
  401. */
  402. static VALUE
  403. fsdbm_shift(VALUE obj)
  404. {
  405. datum key, val;
  406. struct dbmdata *dbmp;
  407. DBM *dbm;
  408. VALUE keystr, valstr;
  409. fdbm_modify(obj);
  410. GetDBM2(obj, dbmp, dbm);
  411. key = sdbm_firstkey(dbm);
  412. if (!key.dptr) return Qnil;
  413. val = sdbm_fetch(dbm, key);
  414. keystr = rb_external_str_new(key.dptr, key.dsize);
  415. valstr = rb_external_str_new(val.dptr, val.dsize);
  416. sdbm_delete(dbm, key);
  417. if (dbmp->di_size >= 0) {
  418. dbmp->di_size--;
  419. }
  420. return rb_assoc_new(keystr, valstr);
  421. }
  422. /*
  423. * call-seq:
  424. * sdbm.delete_if { |key, value| ... } -> self
  425. * sdbm.reject! { |key, value| ... } -> self
  426. *
  427. * Iterates over the key-value pairs in the database, deleting those for
  428. * which the block returns +true+.
  429. */
  430. static VALUE
  431. fsdbm_delete_if(VALUE obj)
  432. {
  433. datum key, val;
  434. struct dbmdata *dbmp;
  435. DBM *dbm;
  436. VALUE keystr, valstr;
  437. VALUE ret, ary = rb_ary_new();
  438. int i, status = 0, n;
  439. fdbm_modify(obj);
  440. GetDBM2(obj, dbmp, dbm);
  441. n = dbmp->di_size;
  442. dbmp->di_size = -1;
  443. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  444. val = sdbm_fetch(dbm, key);
  445. keystr = rb_external_str_new(key.dptr, key.dsize);
  446. valstr = rb_external_str_new(val.dptr, val.dsize);
  447. ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status);
  448. if (status != 0) break;
  449. if (RTEST(ret)) rb_ary_push(ary, keystr);
  450. GetDBM2(obj, dbmp, dbm);
  451. }
  452. for (i = 0; i < RARRAY_LEN(ary); i++) {
  453. keystr = RARRAY_PTR(ary)[i];
  454. ExportStringValue(keystr);
  455. key.dptr = RSTRING_PTR(keystr);
  456. key.dsize = RSTRING_LENINT(keystr);
  457. if (sdbm_delete(dbm, key)) {
  458. rb_raise(rb_eDBMError, "sdbm_delete failed");
  459. }
  460. }
  461. if (status) rb_jump_tag(status);
  462. if (n > 0) dbmp->di_size = n - RARRAY_LENINT(ary);
  463. return obj;
  464. }
  465. /*
  466. * call-seq:
  467. * sdbm.clear -> self
  468. *
  469. * Deletes all data from the database.
  470. */
  471. static VALUE
  472. fsdbm_clear(VALUE obj)
  473. {
  474. datum key;
  475. struct dbmdata *dbmp;
  476. DBM *dbm;
  477. fdbm_modify(obj);
  478. GetDBM2(obj, dbmp, dbm);
  479. dbmp->di_size = -1;
  480. while (key = sdbm_firstkey(dbm), key.dptr) {
  481. if (sdbm_delete(dbm, key)) {
  482. rb_raise(rb_eDBMError, "sdbm_delete failed");
  483. }
  484. }
  485. dbmp->di_size = 0;
  486. return obj;
  487. }
  488. /*
  489. * call-seq:
  490. * sdbm.invert -> Hash
  491. *
  492. * Returns a Hash in which the key-value pairs have been inverted.
  493. *
  494. * Example:
  495. *
  496. * require 'sdbm'
  497. *
  498. * SDBM.open 'my_database' do |db|
  499. * db.update('apple' => 'fruit', 'spinach' => 'vegetable')
  500. *
  501. * db.invert #=> {"fruit" => "apple", "vegetable" => "spinach"}
  502. * end
  503. */
  504. static VALUE
  505. fsdbm_invert(VALUE obj)
  506. {
  507. datum key, val;
  508. struct dbmdata *dbmp;
  509. DBM *dbm;
  510. VALUE keystr, valstr;
  511. VALUE hash = rb_hash_new();
  512. GetDBM2(obj, dbmp, dbm);
  513. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  514. val = sdbm_fetch(dbm, key);
  515. keystr = rb_external_str_new(key.dptr, key.dsize);
  516. valstr = rb_external_str_new(val.dptr, val.dsize);
  517. rb_hash_aset(hash, valstr, keystr);
  518. }
  519. return hash;
  520. }
  521. /*
  522. * call-seq:
  523. * sdbm[key] = value -> value
  524. * sdbm.store(key, value) -> value
  525. *
  526. * Stores a new +value+ in the database with the given +key+ as an index.
  527. *
  528. * If the +key+ already exists, this will update the +value+ associated with
  529. * the +key+.
  530. *
  531. * Returns the given +value+.
  532. */
  533. static VALUE
  534. fsdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
  535. {
  536. datum key, val;
  537. struct dbmdata *dbmp;
  538. DBM *dbm;
  539. if (valstr == Qnil) {
  540. fsdbm_delete(obj, keystr);
  541. return Qnil;
  542. }
  543. fdbm_modify(obj);
  544. ExportStringValue(keystr);
  545. ExportStringValue(valstr);
  546. key.dptr = RSTRING_PTR(keystr);
  547. key.dsize = RSTRING_LENINT(keystr);
  548. val.dptr = RSTRING_PTR(valstr);
  549. val.dsize = RSTRING_LENINT(valstr);
  550. GetDBM2(obj, dbmp, dbm);
  551. dbmp->di_size = -1;
  552. if (sdbm_store(dbm, key, val, DBM_REPLACE)) {
  553. #ifdef HAVE_DBM_CLAERERR
  554. sdbm_clearerr(dbm);
  555. #endif
  556. if (errno == EPERM) rb_sys_fail(0);
  557. rb_raise(rb_eDBMError, "sdbm_store failed");
  558. }
  559. return valstr;
  560. }
  561. static VALUE
  562. update_i(VALUE pair, VALUE dbm)
  563. {
  564. Check_Type(pair, T_ARRAY);
  565. if (RARRAY_LEN(pair) < 2) {
  566. rb_raise(rb_eArgError, "pair must be [key, value]");
  567. }
  568. fsdbm_store(dbm, RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1]);
  569. return Qnil;
  570. }
  571. /*
  572. * call-seq:
  573. * sdbm.update(pairs) -> self
  574. *
  575. * Insert or update key-value pairs.
  576. *
  577. * This method will work with any object which implements an each_pair
  578. * method, such as a Hash.
  579. */
  580. static VALUE
  581. fsdbm_update(VALUE obj, VALUE other)
  582. {
  583. rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
  584. return obj;
  585. }
  586. /*
  587. * call-seq:
  588. * sdbm.replace(pairs) -> self
  589. *
  590. * Empties the database, then inserts the given key-value pairs.
  591. *
  592. * This method will work with any object which implements an each_pair
  593. * method, such as a Hash.
  594. */
  595. static VALUE
  596. fsdbm_replace(VALUE obj, VALUE other)
  597. {
  598. fsdbm_clear(obj);
  599. rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
  600. return obj;
  601. }
  602. /*
  603. * call-seq:
  604. * sdbm.length -> integer
  605. * sdbm.size -> integer
  606. *
  607. * Returns the number of keys in the database.
  608. */
  609. static VALUE
  610. fsdbm_length(VALUE obj)
  611. {
  612. datum key;
  613. struct dbmdata *dbmp;
  614. DBM *dbm;
  615. int i = 0;
  616. GetDBM2(obj, dbmp, dbm);
  617. if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
  618. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  619. i++;
  620. }
  621. dbmp->di_size = i;
  622. return INT2FIX(i);
  623. }
  624. /*
  625. * call-seq:
  626. * sdbm.empty? -> true or false
  627. *
  628. * Returns +true+ if the database is empty.
  629. */
  630. static VALUE
  631. fsdbm_empty_p(VALUE obj)
  632. {
  633. datum key;
  634. struct dbmdata *dbmp;
  635. DBM *dbm;
  636. GetDBM(obj, dbmp);
  637. if (dbmp->di_size < 0) {
  638. dbm = dbmp->di_dbm;
  639. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  640. return Qfalse;
  641. }
  642. }
  643. else {
  644. if (dbmp->di_size)
  645. return Qfalse;
  646. }
  647. return Qtrue;
  648. }
  649. /*
  650. * call-seq:
  651. * sdbm.each_value
  652. * sdbm.each_value { |value| ... }
  653. *
  654. * Iterates over each +value+ in the database.
  655. *
  656. * If no block is given, returns an Enumerator.
  657. */
  658. static VALUE
  659. fsdbm_each_value(VALUE obj)
  660. {
  661. datum key, val;
  662. struct dbmdata *dbmp;
  663. DBM *dbm;
  664. RETURN_ENUMERATOR(obj, 0, 0);
  665. GetDBM2(obj, dbmp, dbm);
  666. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  667. val = sdbm_fetch(dbm, key);
  668. rb_yield(rb_external_str_new(val.dptr, val.dsize));
  669. GetDBM2(obj, dbmp, dbm);
  670. }
  671. return obj;
  672. }
  673. /*
  674. * call-seq:
  675. * sdbm.each_key
  676. * sdbm.each_key { |key| ... }
  677. *
  678. * Iterates over each +key+ in the database.
  679. *
  680. * If no block is given, returns an Enumerator.
  681. */
  682. static VALUE
  683. fsdbm_each_key(VALUE obj)
  684. {
  685. datum key;
  686. struct dbmdata *dbmp;
  687. DBM *dbm;
  688. RETURN_ENUMERATOR(obj, 0, 0);
  689. GetDBM2(obj, dbmp, dbm);
  690. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  691. rb_yield(rb_external_str_new(key.dptr, key.dsize));
  692. GetDBM2(obj, dbmp, dbm);
  693. }
  694. return obj;
  695. }
  696. /*
  697. * call-seq:
  698. * sdbm.each
  699. * sdbm.each { |key, value| ... }
  700. * sdbm.each_pair
  701. * sdbm.each_pair { |key, value| ... }
  702. *
  703. * Iterates over each key-value pair in the database.
  704. *
  705. * If no block is given, returns an Enumerator.
  706. */
  707. static VALUE
  708. fsdbm_each_pair(VALUE obj)
  709. {
  710. datum key, val;
  711. DBM *dbm;
  712. struct dbmdata *dbmp;
  713. VALUE keystr, valstr;
  714. RETURN_ENUMERATOR(obj, 0, 0);
  715. GetDBM2(obj, dbmp, dbm);
  716. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  717. val = sdbm_fetch(dbm, key);
  718. keystr = rb_external_str_new(key.dptr, key.dsize);
  719. valstr = rb_external_str_new(val.dptr, val.dsize);
  720. rb_yield(rb_assoc_new(keystr, valstr));
  721. GetDBM2(obj, dbmp, dbm);
  722. }
  723. return obj;
  724. }
  725. /*
  726. * call-seq:
  727. * sdbm.keys -> Array
  728. *
  729. * Returns a new Array containing the keys in the database.
  730. */
  731. static VALUE
  732. fsdbm_keys(VALUE obj)
  733. {
  734. datum key;
  735. struct dbmdata *dbmp;
  736. DBM *dbm;
  737. VALUE ary;
  738. GetDBM2(obj, dbmp, dbm);
  739. ary = rb_ary_new();
  740. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  741. rb_ary_push(ary, rb_external_str_new(key.dptr, key.dsize));
  742. }
  743. return ary;
  744. }
  745. /*
  746. * call-seq:
  747. * sdbm.values -> Array
  748. *
  749. * Returns a new Array containing the values in the database.
  750. */
  751. static VALUE
  752. fsdbm_values(VALUE obj)
  753. {
  754. datum key, val;
  755. struct dbmdata *dbmp;
  756. DBM *dbm;
  757. VALUE ary;
  758. GetDBM2(obj, dbmp, dbm);
  759. ary = rb_ary_new();
  760. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  761. val = sdbm_fetch(dbm, key);
  762. rb_ary_push(ary, rb_external_str_new(val.dptr, val.dsize));
  763. }
  764. return ary;
  765. }
  766. /*
  767. * call-seq:
  768. * sdbm.include?(key) -> true or false
  769. * sdbm.key?(key) -> true or false
  770. * sdbm.member?(key) -> true or false
  771. * sdbm.has_key?(key) -> true or false
  772. *
  773. * Returns +true+ if the database contains the given +key+.
  774. */
  775. static VALUE
  776. fsdbm_has_key(VALUE obj, VALUE keystr)
  777. {
  778. datum key, val;
  779. struct dbmdata *dbmp;
  780. DBM *dbm;
  781. ExportStringValue(keystr);
  782. key.dptr = RSTRING_PTR(keystr);
  783. key.dsize = RSTRING_LENINT(keystr);
  784. GetDBM2(obj, dbmp, dbm);
  785. val = sdbm_fetch(dbm, key);
  786. if (val.dptr) return Qtrue;
  787. return Qfalse;
  788. }
  789. /*
  790. * call-seq:
  791. * sdbm.value?(key) -> true or false
  792. * sdbm.has_value?(key) -> true or false
  793. *
  794. * Returns +true+ if the database contains the given +value+.
  795. */
  796. static VALUE
  797. fsdbm_has_value(VALUE obj, VALUE valstr)
  798. {
  799. datum key, val;
  800. struct dbmdata *dbmp;
  801. DBM *dbm;
  802. ExportStringValue(valstr);
  803. val.dptr = RSTRING_PTR(valstr);
  804. val.dsize = RSTRING_LENINT(valstr);
  805. GetDBM2(obj, dbmp, dbm);
  806. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  807. val = sdbm_fetch(dbm, key);
  808. if (val.dsize == RSTRING_LENINT(valstr) &&
  809. memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0)
  810. return Qtrue;
  811. }
  812. return Qfalse;
  813. }
  814. /*
  815. * call-seq:
  816. * sdbm.to_a -> Array
  817. *
  818. * Returns a new Array containing each key-value pair in the database.
  819. *
  820. * Example:
  821. *
  822. * require 'sdbm'
  823. *
  824. * SDBM.open 'my_database' do |db|
  825. * db.update('apple' => 'fruit', 'spinach' => 'vegetable')
  826. *
  827. * db.to_a #=> [["apple", "fruit"], ["spinach", "vegetable"]]
  828. * end
  829. */
  830. static VALUE
  831. fsdbm_to_a(VALUE obj)
  832. {
  833. datum key, val;
  834. struct dbmdata *dbmp;
  835. DBM *dbm;
  836. VALUE ary;
  837. GetDBM2(obj, dbmp, dbm);
  838. ary = rb_ary_new();
  839. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  840. val = sdbm_fetch(dbm, key);
  841. rb_ary_push(ary, rb_assoc_new(rb_external_str_new(key.dptr, key.dsize),
  842. rb_external_str_new(val.dptr, val.dsize)));
  843. }
  844. return ary;
  845. }
  846. /*
  847. * call-seq:
  848. * sdbm.to_hash -> Hash
  849. *
  850. * Returns a new Hash containing each key-value pair in the database.
  851. */
  852. static VALUE
  853. fsdbm_to_hash(VALUE obj)
  854. {
  855. datum key, val;
  856. struct dbmdata *dbmp;
  857. DBM *dbm;
  858. VALUE hash;
  859. GetDBM2(obj, dbmp, dbm);
  860. hash = rb_hash_new();
  861. for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
  862. val = sdbm_fetch(dbm, key);
  863. rb_hash_aset(hash, rb_external_str_new(key.dptr, key.dsize),
  864. rb_external_str_new(val.dptr, val.dsize));
  865. }
  866. return hash;
  867. }
  868. /*
  869. * call-seq:
  870. * sdbm.reject { |key, value| ... } -> Hash
  871. *
  872. * Creates a new Hash using the key-value pairs from the database, then
  873. * calls Hash#reject with the given block, which returns a Hash with
  874. * only the key-value pairs for which the block returns +false+.
  875. */
  876. static VALUE
  877. fsdbm_reject(VALUE obj)
  878. {
  879. return rb_hash_delete_if(fsdbm_to_hash(obj));
  880. }
  881. void
  882. Init_sdbm()
  883. {
  884. rb_cDBM = rb_define_class("SDBM", rb_cObject);
  885. rb_eDBMError = rb_define_class("SDBMError", rb_eStandardError);
  886. /* Document-class: SDBMError
  887. * Exception class used to return errors from the sdbm library.
  888. */
  889. rb_include_module(rb_cDBM, rb_mEnumerable);
  890. rb_define_alloc_func(rb_cDBM, fsdbm_alloc);
  891. rb_define_singleton_method(rb_cDBM, "open", fsdbm_s_open, -1);
  892. rb_define_method(rb_cDBM, "initialize", fsdbm_initialize, -1);
  893. rb_define_method(rb_cDBM, "close", fsdbm_close, 0);
  894. rb_define_method(rb_cDBM, "closed?", fsdbm_closed, 0);
  895. rb_define_method(rb_cDBM, "[]", fsdbm_aref, 1);
  896. rb_define_method(rb_cDBM, "fetch", fsdbm_fetch_m, -1);
  897. rb_define_method(rb_cDBM, "[]=", fsdbm_store, 2);
  898. rb_define_method(rb_cDBM, "store", fsdbm_store, 2);
  899. rb_define_method(rb_cDBM, "index", fsdbm_index, 1);
  900. rb_define_method(rb_cDBM, "key", fsdbm_key, 1);
  901. rb_define_method(rb_cDBM, "select", fsdbm_select, 0);
  902. rb_define_method(rb_cDBM, "values_at", fsdbm_values_at, -1);
  903. rb_define_method(rb_cDBM, "length", fsdbm_length, 0);
  904. rb_define_method(rb_cDBM, "size", fsdbm_length, 0);
  905. rb_define_method(rb_cDBM, "empty?", fsdbm_empty_p, 0);
  906. rb_define_method(rb_cDBM, "each", fsdbm_each_pair, 0);
  907. rb_define_method(rb_cDBM, "each_value", fsdbm_each_value, 0);
  908. rb_define_method(rb_cDBM, "each_key", fsdbm_each_key, 0);
  909. rb_define_method(rb_cDBM, "each_pair", fsdbm_each_pair, 0);
  910. rb_define_method(rb_cDBM, "keys", fsdbm_keys, 0);
  911. rb_define_method(rb_cDBM, "values", fsdbm_values, 0);
  912. rb_define_method(rb_cDBM, "shift", fsdbm_shift, 0);
  913. rb_define_method(rb_cDBM, "delete", fsdbm_delete, 1);
  914. rb_define_method(rb_cDBM, "delete_if", fsdbm_delete_if, 0);
  915. rb_define_method(rb_cDBM, "reject!", fsdbm_delete_if, 0);
  916. rb_define_method(rb_cDBM, "reject", fsdbm_reject, 0);
  917. rb_define_method(rb_cDBM, "clear", fsdbm_clear, 0);
  918. rb_define_method(rb_cDBM,"invert", fsdbm_invert, 0);
  919. rb_define_method(rb_cDBM,"update", fsdbm_update, 1);
  920. rb_define_method(rb_cDBM,"replace", fsdbm_replace, 1);
  921. rb_define_method(rb_cDBM, "has_key?", fsdbm_has_key, 1);
  922. rb_define_method(rb_cDBM, "include?", fsdbm_has_key, 1);
  923. rb_define_method(rb_cDBM, "key?", fsdbm_has_key, 1);
  924. rb_define_method(rb_cDBM, "member?", fsdbm_has_key, 1);
  925. rb_define_method(rb_cDBM, "has_value?", fsdbm_has_value, 1);
  926. rb_define_method(rb_cDBM, "value?", fsdbm_has_value, 1);
  927. rb_define_method(rb_cDBM, "to_a", fsdbm_to_a, 0);
  928. rb_define_method(rb_cDBM, "to_hash", fsdbm_to_hash, 0);
  929. }