Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
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
67static VALUE rb_cDBM, rb_eDBMError;
68
69struct dbmdata {
70 int di_size;
71 DBM *di_dbm;
72};
73
74NORETURN(static void closed_sdbm(void));
75
76static void
77closed_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
92static void
93free_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
101static size_t
102memsize_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
111static 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 */
126static VALUE
127fsdbm_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 */
144static VALUE
145fsdbm_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
156static VALUE
157fsdbm_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 */
178static VALUE
179fsdbm_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 */
237static VALUE
238fsdbm_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
253static VALUE
254fsdbm_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 */
282static VALUE
283fsdbm_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 */
302static VALUE
303fsdbm_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 */
323static VALUE
324fsdbm_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 */
347static VALUE
348fsdbm_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 */
373static VALUE
374fsdbm_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 */
402static VALUE
403fsdbm_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
415static void
416fdbm_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 */
433static VALUE
434fsdbm_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 */
475static VALUE
476fsdbm_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 */
506static VALUE
507fsdbm_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 */
552static VALUE
553fsdbm_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 */
588static VALUE
589fsdbm_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 */
619static VALUE
620fsdbm_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
654static VALUE
655update_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 */
676static VALUE
677fsdbm_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 */
692static VALUE
693fsdbm_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 */
707static VALUE
708fsdbm_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 */
732static VALUE
733fsdbm_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 */
763static VALUE
764fsdbm_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 */
790static VALUE
791fsdbm_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 */
818static VALUE
819fsdbm_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 */
846static VALUE
847fsdbm_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 */
869static VALUE
870fsdbm_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);
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 */
896static VALUE
897fsdbm_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 */
920static VALUE
921fsdbm_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 */
957static VALUE
958fsdbm_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 */
982static VALUE
983fsdbm_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 */
1009static VALUE
1010fsdbm_reject(VALUE obj)
1011{
1012 return rb_hash_delete_if(fsdbm_to_hash(obj));
1013}
1014
1015void
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}
int sdbm_store(register DBM *db, datum key, datum val, int flags)
Definition: _sdbm.c:312
int sdbm_delete(register DBM *db, datum key)
Definition: _sdbm.c:288
void sdbm_close(register DBM *db)
Definition: _sdbm.c:264
datum sdbm_firstkey(register DBM *db)
Definition: _sdbm.c:467
DBM * sdbm_open(register char *file, register int flags, register int mode)
Definition: _sdbm.c:147
int errno
datum sdbm_nextkey(register DBM *db)
Definition: _sdbm.c:486
datum sdbm_fetch(register DBM *db, datum key)
Definition: _sdbm.c:276
struct RIMemo * ptr
Definition: debug.c:65
void rb_include_module(VALUE, VALUE)
Definition: class.c:882
VALUE rb_define_class(const char *, VALUE)
Defines a top-level class.
Definition: class.c:662
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:898
VALUE rb_cObject
Object class.
Definition: ruby.h:2012
VALUE rb_mEnumerable
Definition: enum.c:20
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
VALUE rb_eStandardError
Definition: error.c:921
void rb_error_frozen(const char *what)
Definition: error.c:2976
void rb_sys_fail_str(VALUE mesg)
Definition: error.c:2801
VALUE rb_protect(VALUE(*)(VALUE), VALUE, int *)
Protects a function call from potential global escapes from the function.
Definition: eval.c:1072
void rb_warn(const char *fmt,...)
Definition: error.c:315
VALUE rb_eArgError
Definition: error.c:925
VALUE rb_eIndexError
Definition: error.c:926
VALUE rb_ensure(VALUE(*)(VALUE), VALUE, VALUE(*)(VALUE), VALUE)
An equivalent to ensure clause.
Definition: eval.c:1115
void rb_jump_tag(int tag)
Continues the exception caught by rb_protect() and rb_eval_string_protect().
Definition: eval.c:884
void rb_sys_fail(const char *mesg)
Definition: error.c:2795
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
#define RARRAY_LEN(a)
int memcmp(const void *, const void *, size_t)
Definition: memcmp.c:7
#define RSTRING_LEN(str)
#define RTEST(v)
VALUE rb_assoc_new(VALUE, VALUE)
Definition: array.c:896
#define RARRAY_LENINT(ary)
const VALUE VALUE obj
VALUE rb_external_str_new(const char *, long)
Definition: string.c:1087
VALUE rb_hash_delete_if(VALUE)
Definition: hash.c:2493
#define RSTRING_PTR(str)
#define NIL_P(v)
#define RSTRING_LENINT(str)
const char size_t n
VALUE rb_ary_push(VALUE, VALUE)
Definition: array.c:1195
#define FilePathValue(v)
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
uint32_t i
#define OBJ_FROZEN(x)
#define ExportStringValue(v)
#define NUM2INT(x)
void rb_define_singleton_method(VALUE, const char *, VALUE(*)(), int)
#define RUBY_TYPED_FREE_IMMEDIATELY
#define TypedData_Get_Struct(obj, type, data_type, sval)
int VALUE v
VALUE rb_ary_new(void)
Definition: array.c:723
#define rb_scan_args(argc, argvp, fmt,...)
#define rb_intern(str)
unsigned int size
#define Qtrue
#define Qnil
#define Qfalse
#define T_ARRAY
#define INT2FIX(i)
#define TypedData_Make_Struct(klass, type, data_type, sval)
const VALUE * argv
void void ruby_xfree(void *)
Definition: gc.c:10183
#define RETURN_ENUMERATOR(obj, argc, argv)
#define Check_Type(v, t)
VALUE rb_hash_aset(VALUE, VALUE, VALUE)
Definition: hash.c:2852
VALUE rb_block_call(VALUE, ID, int, const VALUE *, rb_block_call_func_t, VALUE)
Definition: vm_eval.c:1470
#define EPERM
VALUE rb_str_dup(VALUE)
Definition: string.c:1516
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1237
void rb_define_method(VALUE, const char *, VALUE(*)(), int)
#define rb_ary_new2
#define RARRAY_AREF(a, i)
VALUE rb_hash_new(void)
Definition: hash.c:1523
#define RARRAY_CONST_PTR(a)
unsigned long VALUE
Definition: ruby.h:102
#define GetDBM2(obj, dbmp, dbm)
Definition: init.c:87
void Init_sdbm(void)
Definition: init.c:1016
#define GetDBM(obj, dbmp)
Definition: init.c:82
NORETURN(static void closed_sdbm(void))
#define sdbm_clearerr(db)
Definition: sdbm.h:45
#define DBM_REPLACE
Definition: sdbm.h:67
Definition: sdbm.h:20
Definition: sdbm.h:50
char * dptr
Definition: sdbm.h:51
int dsize
Definition: sdbm.h:52
Definition: dbm.c:37
long di_size
Definition: dbm.c:38
DBM * di_dbm
Definition: dbm.c:39