Ruby  2.5.0dev(2017-10-22revision60238)
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 static void
75 closed_sdbm(void)
76 {
77  rb_raise(rb_eDBMError, "closed SDBM file");
78 }
79 
80 #define GetDBM(obj, dbmp) do {\
81  TypedData_Get_Struct((obj), struct dbmdata, &sdbm_type, (dbmp));\
82  if ((dbmp) == 0) closed_sdbm();\
83  if ((dbmp)->di_dbm == 0) closed_sdbm();\
84 } while (0)
85 
86 #define GetDBM2(obj, dbmp, dbm) do {\
87  GetDBM((obj), (dbmp));\
88  (dbm) = (dbmp)->di_dbm;\
89 } while (0)
90 
91 static void
92 free_sdbm(void *ptr)
93 {
94  struct dbmdata *dbmp = ptr;
95 
96  if (dbmp->di_dbm) sdbm_close(dbmp->di_dbm);
97  ruby_xfree(dbmp);
98 }
99 
100 static size_t
101 memsize_dbm(const void *ptr)
102 {
103  size_t size = 0;
104  const struct dbmdata *dbmp = ptr;
105  if (dbmp) {
106  size += sizeof(*dbmp);
107  if (dbmp->di_dbm) size += sizeof(DBM);
108  }
109  return size;
110 }
111 
112 static const rb_data_type_t sdbm_type = {
113  "sdbm",
114  {0, free_sdbm, memsize_dbm,},
115  0, 0,
117 };
118 
119 /*
120  * call-seq:
121  * sdbm.close -> nil
122  *
123  * Closes the database file.
124  *
125  * Raises SDBMError if the database is already closed.
126  */
127 static VALUE
128 fsdbm_close(VALUE obj)
129 {
130  struct dbmdata *dbmp;
131 
132  GetDBM(obj, dbmp);
133  sdbm_close(dbmp->di_dbm);
134  dbmp->di_dbm = 0;
135 
136  return Qnil;
137 }
138 
139 /*
140  * call-seq:
141  * sdbm.closed? -> true or false
142  *
143  * Returns +true+ if the database is closed.
144  */
145 static VALUE
146 fsdbm_closed(VALUE obj)
147 {
148  struct dbmdata *dbmp;
149 
150  TypedData_Get_Struct(obj, struct dbmdata, &sdbm_type, dbmp);
151  if (dbmp == 0)
152  return Qtrue;
153  if (dbmp->di_dbm == 0)
154  return Qtrue;
155 
156  return Qfalse;
157 }
158 
159 static VALUE
160 fsdbm_alloc(VALUE klass)
161 {
162  return TypedData_Wrap_Struct(klass, &sdbm_type, 0);
163 }
164 /*
165  * call-seq:
166  * SDBM.new(filename, mode = 0666)
167  *
168  * Creates a new database handle by opening the given +filename+. SDBM actually
169  * uses two physical files, with extensions '.dir' and '.pag'. These extensions
170  * will automatically be appended to the +filename+.
171  *
172  * If the file does not exist, a new file will be created using the given
173  * +mode+, unless +mode+ is explicitly set to nil. In the latter case, no
174  * database will be created.
175  *
176  * If the file exists, it will be opened in read/write mode. If this fails, it
177  * will be opened in read-only mode.
178  */
179 static VALUE
180 fsdbm_initialize(int argc, VALUE *argv, VALUE obj)
181 {
182  VALUE file, vmode;
183  DBM *dbm;
184  struct dbmdata *dbmp;
185  int mode;
186 
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  dbmp = ALLOC(struct dbmdata);
212  DATA_PTR(obj) = dbmp;
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)) {
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 }
DBM * di_dbm
Definition: dbm.c:39
VALUE rb_protect(VALUE(*proc)(VALUE), VALUE data, int *pstate)
Protects a function call from potential global escapes from the function.
Definition: eval.c:992
void rb_warn(const char *fmt,...)
Definition: error.c:246
#define RARRAY_LEN(a)
Definition: ruby.h:1019
#define RUBY_TYPED_FREE_IMMEDIATELY
Definition: ruby.h:1138
#define NUM2INT(x)
Definition: ruby.h:684
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1716
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:835
#define FilePathValue(v)
Definition: ruby.h:594
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2284
void rb_jump_tag(int tag)
Continues the exception caught by rb_protect() and rb_eval_string_protect().
Definition: eval.c:821
#define Qtrue
Definition: ruby.h:437
#define TypedData_Wrap_Struct(klass, data_type, sval)
Definition: ruby.h:1162
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: ruby.h:1183
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:924
long di_size
Definition: dbm.c:38
#define Check_Type(v, t)
Definition: ruby.h:562
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
Definition: dbm.c:37
#define DATA_PTR(dta)
Definition: ruby.h:1106
void rb_include_module(VALUE klass, VALUE module)
Definition: class.c:864
char * dptr
Definition: sdbm.h:51
VALUE rb_block_call(VALUE, ID, int, const VALUE *, rb_block_call_func_t, VALUE)
#define T_ARRAY
Definition: ruby.h:498
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1035
#define rb_ary_new2
Definition: intern.h:90
VALUE rb_eArgError
Definition: error.c:802
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Definition: ruby.h:1851
void sdbm_close(register DBM *db)
Definition: _sdbm.c:265
DBM * sdbm_open(register char *file, register int flags, register int mode)
Definition: _sdbm.c:148
Definition: sdbm.h:50
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Definition: hash.c:1616
#define DBM_REPLACE
Definition: sdbm.h:67
#define val
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:1893
VALUE rb_ary_new(void)
Definition: array.c:499
VALUE rb_eIndexError
Definition: error.c:803
#define NIL_P(v)
Definition: ruby.h:451
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:646
datum sdbm_firstkey(register DBM *db)
Definition: _sdbm.c:468
int argc
Definition: ruby.c:187
#define Qfalse
Definition: ruby.h:436
VALUE rb_external_str_new(const char *, long)
Definition: string.c:1055
void rb_error_frozen(const char *what)
Definition: error.c:2584
#define ALLOC(type)
Definition: ruby.h:1588
void rb_sys_fail(const char *mesg)
Definition: error.c:2403
#define RSTRING_LEN(str)
Definition: ruby.h:971
VALUE rb_yield(VALUE)
Definition: vm_eval.c:973
#define RARRAY_CONST_PTR(a)
Definition: ruby.h:1021
int errno
VALUE rb_mEnumerable
Definition: enum.c:19
Definition: sdbm.h:20
VALUE rb_hash_new(void)
Definition: hash.c:424
void ruby_xfree(void *x)
Definition: gc.c:8085
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1908
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Definition: array.c:639
#define Qnil
Definition: ruby.h:438
VALUE rb_eStandardError
Definition: error.c:799
int dsize
Definition: sdbm.h:52
unsigned long VALUE
Definition: ruby.h:85
#define EPERM
Definition: _sdbm.c:93
int memcmp(const void *s1, const void *s2, size_t len)
Definition: memcmp.c:7
#define sdbm_clearerr(db)
Definition: sdbm.h:45
#define RARRAY_LENINT(ary)
Definition: ruby.h:1020
VALUE rb_str_dup(VALUE)
Definition: string.c:1488
#define RSTRING_PTR(str)
Definition: ruby.h:975
int size
Definition: encoding.c:57
int sdbm_store(register DBM *db, datum key, datum val, int flags)
Definition: _sdbm.c:313
#define INT2FIX(i)
Definition: ruby.h:232
#define ExportStringValue(v)
Definition: ruby.h:587
#define RARRAY_AREF(a, i)
Definition: ruby.h:1033
#define GetDBM(obj, dbmp)
Definition: init.c:80
#define RTEST(v)
Definition: ruby.h:450
#define GetDBM2(obj, dbmp, dbm)
Definition: init.c:86
#define OBJ_FROZEN(x)
Definition: ruby.h:1304
#define RETURN_ENUMERATOR(obj, argc, argv)
Definition: intern.h:238
datum sdbm_nextkey(register DBM *db)
Definition: _sdbm.c:487
#define RSTRING_LENINT(str)
Definition: ruby.h:983
VALUE rb_hash_delete_if(VALUE hash)
Definition: hash.c:1253
int sdbm_delete(register DBM *db, datum key)
Definition: _sdbm.c:289
void Init_sdbm(void)
Definition: init.c:1016
datum sdbm_fetch(register DBM *db, datum key)
Definition: _sdbm.c:277
#define rb_intern(str)
void rb_sys_fail_str(VALUE mesg)
Definition: error.c:2409
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
char ** argv
Definition: ruby.c:188