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