sdbm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ # AUTOGENERATED DEPENDENCIES START
2
+ _sdbm.o: $(RUBY_EXTCONF_H)
3
+ _sdbm.o: $(arch_hdrdir)/ruby/config.h
4
+ _sdbm.o: $(hdrdir)/ruby/backward.h
5
+ _sdbm.o: $(hdrdir)/ruby/defines.h
6
+ _sdbm.o: $(hdrdir)/ruby/intern.h
7
+ _sdbm.o: $(hdrdir)/ruby/missing.h
8
+ _sdbm.o: $(hdrdir)/ruby/ruby.h
9
+ _sdbm.o: $(hdrdir)/ruby/st.h
10
+ _sdbm.o: $(hdrdir)/ruby/subst.h
11
+ _sdbm.o: _sdbm.c
12
+ _sdbm.o: sdbm.h
13
+ init.o: $(RUBY_EXTCONF_H)
14
+ init.o: $(arch_hdrdir)/ruby/config.h
15
+ init.o: $(hdrdir)/ruby/backward.h
16
+ init.o: $(hdrdir)/ruby/defines.h
17
+ init.o: $(hdrdir)/ruby/intern.h
18
+ init.o: $(hdrdir)/ruby/missing.h
19
+ init.o: $(hdrdir)/ruby/ruby.h
20
+ init.o: $(hdrdir)/ruby/st.h
21
+ init.o: $(hdrdir)/ruby/subst.h
22
+ # init.o: $(top_srcdir)/include/ruby.h
23
+ init.o: init.c
24
+ init.o: sdbm.h
25
+ # AUTOGENERATED DEPENDENCIES END
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: false
2
+ require 'mkmf'
3
+
4
+ $defs << "-D""BADMESS=0"
5
+ create_makefile("sdbm")
@@ -0,0 +1,1067 @@
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,
116
+ RUBY_TYPED_FREE_IMMEDIATELY,
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);
775
+ rb_yield(rb_external_str_new(val.dptr, val.dsize));
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);
969
+ rb_ary_push(ary, rb_assoc_new(rb_external_str_new(key.dptr, key.dsize),
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
1016
+ Init_sdbm(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
+ */
1023
+ rb_include_module(rb_cDBM, rb_mEnumerable);
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
+ }