dbm 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/ext/dbm/dbm.c +1160 -0
  3. data/ext/dbm/extconf.rb +290 -0
  4. metadata +75 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 31cec15138a9142216ad46055bc42306214cdd19
4
+ data.tar.gz: ec6c97382c6c3cce47c2613dafb48ea70ff54263
5
+ SHA512:
6
+ metadata.gz: 6ba19f6220f2af16c7f5390ac6c25d183db149925029a5585e96939a70bf921fbf33e259ed9f760036bff830468739893c288db1567e23530b61ee190a4792db
7
+ data.tar.gz: 3256035cb393c4c8f986576a50923889f3cb60c53e37b0bd1ad3fe4f911243470268ea28c415f89220a89c378afba720efd88ee09749db7a74be99a6dd5adef9
@@ -0,0 +1,1160 @@
1
+ /************************************************
2
+
3
+ dbm.c -
4
+
5
+ $Author$
6
+ created at: Mon Jan 24 15:59:52 JST 1994
7
+
8
+ Copyright (C) 1995-2001 Yukihiro Matsumoto
9
+
10
+ ************************************************/
11
+
12
+ #include "ruby.h"
13
+
14
+ #ifdef HAVE_CDEFS_H
15
+ # include <cdefs.h>
16
+ #endif
17
+ #ifdef HAVE_SYS_CDEFS_H
18
+ # include <sys/cdefs.h>
19
+ #endif
20
+ #include DBM_HDR
21
+ #include <fcntl.h>
22
+ #include <errno.h>
23
+
24
+ #define DSIZE_TYPE TYPEOF_DATUM_DSIZE
25
+ #if SIZEOF_DATUM_DSIZE > SIZEOF_INT
26
+ # define RSTRING_DSIZE(s) RSTRING_LEN(s)
27
+ # define TOO_LONG(n) 0
28
+ #else
29
+ # define RSTRING_DSIZE(s) RSTRING_LENINT(s)
30
+ # define TOO_LONG(n) ((long)(+(DSIZE_TYPE)(n)) != (n))
31
+ #endif
32
+
33
+ static VALUE rb_cDBM, rb_eDBMError;
34
+
35
+ #define RUBY_DBM_RW_BIT 0x20000000
36
+
37
+ struct dbmdata {
38
+ long di_size;
39
+ DBM *di_dbm;
40
+ };
41
+
42
+ static void
43
+ closed_dbm(void)
44
+ {
45
+ rb_raise(rb_eDBMError, "closed DBM file");
46
+ }
47
+
48
+ #define GetDBM(obj, dbmp) do {\
49
+ TypedData_Get_Struct((obj), struct dbmdata, &dbm_type, (dbmp));\
50
+ if ((dbmp) == 0) closed_dbm();\
51
+ if ((dbmp)->di_dbm == 0) closed_dbm();\
52
+ } while (0)
53
+
54
+ #define GetDBM2(obj, dbmp, dbm) do {\
55
+ GetDBM((obj), (dbmp));\
56
+ (dbm) = (dbmp)->di_dbm;\
57
+ } while (0)
58
+
59
+ static void
60
+ free_dbm(void *ptr)
61
+ {
62
+ struct dbmdata *dbmp = ptr;
63
+ if (dbmp) {
64
+ if (dbmp->di_dbm) dbm_close(dbmp->di_dbm);
65
+ xfree(dbmp);
66
+ }
67
+ }
68
+
69
+ static size_t
70
+ memsize_dbm(const void *ptr)
71
+ {
72
+ size_t size = 0;
73
+ const struct dbmdata *dbmp = ptr;
74
+ if (dbmp) {
75
+ size += sizeof(*dbmp);
76
+ if (dbmp->di_dbm) size += DBM_SIZEOF_DBM;
77
+ }
78
+ return size;
79
+ }
80
+
81
+ static const rb_data_type_t dbm_type = {
82
+ "dbm",
83
+ {0, free_dbm, memsize_dbm,},
84
+ 0, 0,
85
+ RUBY_TYPED_FREE_IMMEDIATELY,
86
+ };
87
+
88
+ /*
89
+ * call-seq:
90
+ * dbm.close
91
+ *
92
+ * Closes the database.
93
+ */
94
+ static VALUE
95
+ fdbm_close(VALUE obj)
96
+ {
97
+ struct dbmdata *dbmp;
98
+
99
+ GetDBM(obj, dbmp);
100
+ dbm_close(dbmp->di_dbm);
101
+ dbmp->di_dbm = 0;
102
+
103
+ return Qnil;
104
+ }
105
+
106
+ /*
107
+ * call-seq:
108
+ * dbm.closed? -> true or false
109
+ *
110
+ * Returns true if the database is closed, false otherwise.
111
+ */
112
+ static VALUE
113
+ fdbm_closed(VALUE obj)
114
+ {
115
+ struct dbmdata *dbmp;
116
+
117
+ TypedData_Get_Struct(obj, struct dbmdata, &dbm_type, dbmp);
118
+ if (dbmp == 0)
119
+ return Qtrue;
120
+ if (dbmp->di_dbm == 0)
121
+ return Qtrue;
122
+
123
+ return Qfalse;
124
+ }
125
+
126
+ static VALUE
127
+ fdbm_alloc(VALUE klass)
128
+ {
129
+ return TypedData_Wrap_Struct(klass, &dbm_type, 0);
130
+ }
131
+
132
+ /*
133
+ * call-seq:
134
+ * DBM.new(filename[, mode[, flags]]) -> dbm
135
+ *
136
+ * Open a dbm database with the specified name, which can include a directory
137
+ * path. Any file extensions needed will be supplied automatically by the dbm
138
+ * library. For example, Berkeley DB appends '.db', and GNU gdbm uses two
139
+ * physical files with extensions '.dir' and '.pag'.
140
+ *
141
+ * The mode should be an integer, as for Unix chmod.
142
+ *
143
+ * Flags should be one of READER, WRITER, WRCREAT or NEWDB.
144
+ */
145
+ static VALUE
146
+ fdbm_initialize(int argc, VALUE *argv, VALUE obj)
147
+ {
148
+ VALUE file, vmode, vflags;
149
+ DBM *dbm;
150
+ struct dbmdata *dbmp;
151
+ int mode, flags = 0;
152
+
153
+ if (rb_scan_args(argc, argv, "12", &file, &vmode, &vflags) == 1) {
154
+ mode = 0666; /* default value */
155
+ }
156
+ else if (NIL_P(vmode)) {
157
+ mode = -1; /* return nil if DB not exist */
158
+ }
159
+ else {
160
+ mode = NUM2INT(vmode);
161
+ }
162
+
163
+ if (!NIL_P(vflags))
164
+ flags = NUM2INT(vflags);
165
+
166
+ FilePathValue(file);
167
+
168
+ /*
169
+ * Note:
170
+ * gdbm 1.10 works with O_CLOEXEC. gdbm 1.9.1 silently ignore it.
171
+ */
172
+ #ifndef O_CLOEXEC
173
+ # define O_CLOEXEC 0
174
+ #endif
175
+
176
+ if (flags & RUBY_DBM_RW_BIT) {
177
+ flags &= ~RUBY_DBM_RW_BIT;
178
+ dbm = dbm_open(RSTRING_PTR(file), flags|O_CLOEXEC, mode);
179
+ }
180
+ else {
181
+ dbm = 0;
182
+ if (mode >= 0) {
183
+ dbm = dbm_open(RSTRING_PTR(file), O_RDWR|O_CREAT|O_CLOEXEC, mode);
184
+ }
185
+ if (!dbm) {
186
+ dbm = dbm_open(RSTRING_PTR(file), O_RDWR|O_CLOEXEC, 0);
187
+ }
188
+ if (!dbm) {
189
+ dbm = dbm_open(RSTRING_PTR(file), O_RDONLY|O_CLOEXEC, 0);
190
+ }
191
+ }
192
+
193
+ if (dbm) {
194
+ /*
195
+ * History of dbm_pagfno() and dbm_dirfno() in ndbm and its compatibles.
196
+ * (dbm_pagfno() and dbm_dirfno() is not standardized.)
197
+ *
198
+ * 1986: 4.3BSD provides ndbm.
199
+ * It provides dbm_pagfno() and dbm_dirfno() as macros.
200
+ * 1991: gdbm-1.5 provides them as functions.
201
+ * They returns a same descriptor.
202
+ * (Earlier releases may have the functions too.)
203
+ * 1991: Net/2 provides Berkeley DB.
204
+ * It doesn't provide dbm_pagfno() and dbm_dirfno().
205
+ * 1992: 4.4BSD Alpha provides Berkeley DB with dbm_dirfno() as a function.
206
+ * dbm_pagfno() is a macro as DBM_PAGFNO_NOT_AVAILABLE.
207
+ * 1997: Berkeley DB 2.0 is released by Sleepycat Software, Inc.
208
+ * It defines dbm_pagfno() and dbm_dirfno() as macros.
209
+ * 2011: gdbm-1.9 creates a separate dir file.
210
+ * dbm_pagfno() and dbm_dirfno() returns different descriptors.
211
+ */
212
+ #if defined(HAVE_DBM_PAGFNO)
213
+ rb_fd_fix_cloexec(dbm_pagfno(dbm));
214
+ #endif
215
+ #if defined(HAVE_DBM_DIRFNO)
216
+ rb_fd_fix_cloexec(dbm_dirfno(dbm));
217
+ #endif
218
+
219
+ #if defined(RUBYDBM_DB_HEADER) && defined(HAVE_TYPE_DBC)
220
+ /* Disable Berkeley DB error messages such as:
221
+ * DB->put: attempt to modify a read-only database */
222
+ ((DBC*)dbm)->dbp->set_errfile(((DBC*)dbm)->dbp, NULL);
223
+ #endif
224
+ }
225
+
226
+ if (!dbm) {
227
+ if (mode == -1) return Qnil;
228
+ rb_sys_fail_str(file);
229
+ }
230
+
231
+ dbmp = ALLOC(struct dbmdata);
232
+ DATA_PTR(obj) = dbmp;
233
+ dbmp->di_dbm = dbm;
234
+ dbmp->di_size = -1;
235
+
236
+ return obj;
237
+ }
238
+
239
+ /*
240
+ * call-seq:
241
+ * DBM.open(filename[, mode[, flags]]) -> dbm
242
+ * DBM.open(filename[, mode[, flags]]) {|dbm| block}
243
+ *
244
+ * Open a dbm database and yields it if a block is given. See also
245
+ * <code>DBM.new</code>.
246
+ */
247
+ static VALUE
248
+ fdbm_s_open(int argc, VALUE *argv, VALUE klass)
249
+ {
250
+ VALUE obj = fdbm_alloc(klass);
251
+
252
+ if (NIL_P(fdbm_initialize(argc, argv, obj))) {
253
+ return Qnil;
254
+ }
255
+
256
+ if (rb_block_given_p()) {
257
+ return rb_ensure(rb_yield, obj, fdbm_close, obj);
258
+ }
259
+
260
+ return obj;
261
+ }
262
+
263
+ static VALUE
264
+ fdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone)
265
+ {
266
+ datum key, value;
267
+ struct dbmdata *dbmp;
268
+ DBM *dbm;
269
+ long len;
270
+
271
+ ExportStringValue(keystr);
272
+ len = RSTRING_LEN(keystr);
273
+ if (TOO_LONG(len)) goto not_found;
274
+ key.dptr = RSTRING_PTR(keystr);
275
+ key.dsize = (DSIZE_TYPE)len;
276
+
277
+ GetDBM2(obj, dbmp, dbm);
278
+ value = dbm_fetch(dbm, key);
279
+ if (value.dptr == 0) {
280
+ not_found:
281
+ if (NIL_P(ifnone) && rb_block_given_p()) {
282
+ keystr = rb_str_dup(keystr);
283
+ OBJ_TAINT(keystr);
284
+ return rb_yield(keystr);
285
+ }
286
+ return ifnone;
287
+ }
288
+ return rb_tainted_str_new(value.dptr, value.dsize);
289
+ }
290
+
291
+ /*
292
+ * call-seq:
293
+ * dbm[key] -> string value or nil
294
+ *
295
+ * Return a value from the database by locating the key string
296
+ * provided. If the key is not found, returns nil.
297
+ */
298
+ static VALUE
299
+ fdbm_aref(VALUE obj, VALUE keystr)
300
+ {
301
+ return fdbm_fetch(obj, keystr, Qnil);
302
+ }
303
+
304
+ /*
305
+ * call-seq:
306
+ * dbm.fetch(key[, ifnone]) -> value
307
+ *
308
+ * Return a value from the database by locating the key string
309
+ * provided. If the key is not found, returns +ifnone+. If +ifnone+
310
+ * is not given, raises IndexError.
311
+ */
312
+ static VALUE
313
+ fdbm_fetch_m(int argc, VALUE *argv, VALUE obj)
314
+ {
315
+ VALUE keystr, valstr, ifnone;
316
+
317
+ rb_scan_args(argc, argv, "11", &keystr, &ifnone);
318
+ valstr = fdbm_fetch(obj, keystr, ifnone);
319
+ if (argc == 1 && !rb_block_given_p() && NIL_P(valstr))
320
+ rb_raise(rb_eIndexError, "key not found");
321
+
322
+ return valstr;
323
+ }
324
+
325
+ /*
326
+ * call-seq:
327
+ * dbm.key(value) -> string
328
+ *
329
+ * Returns the key for the specified value.
330
+ */
331
+ static VALUE
332
+ fdbm_key(VALUE obj, VALUE valstr)
333
+ {
334
+ datum key, val;
335
+ struct dbmdata *dbmp;
336
+ DBM *dbm;
337
+ long len;
338
+
339
+ ExportStringValue(valstr);
340
+ len = RSTRING_LEN(valstr);
341
+ if (TOO_LONG(len)) return Qnil;
342
+ val.dptr = RSTRING_PTR(valstr);
343
+ val.dsize = (DSIZE_TYPE)len;
344
+
345
+ GetDBM2(obj, dbmp, dbm);
346
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
347
+ val = dbm_fetch(dbm, key);
348
+ if ((long)val.dsize == RSTRING_LEN(valstr) &&
349
+ memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0) {
350
+ return rb_tainted_str_new(key.dptr, key.dsize);
351
+ }
352
+ }
353
+ return Qnil;
354
+ }
355
+
356
+ /* :nodoc: */
357
+ static VALUE
358
+ fdbm_index(VALUE hash, VALUE value)
359
+ {
360
+ rb_warn("DBM#index is deprecated; use DBM#key");
361
+ return fdbm_key(hash, value);
362
+ }
363
+
364
+ /*
365
+ * call-seq:
366
+ * dbm.select {|key, value| block} -> array
367
+ *
368
+ * Returns a new array consisting of the [key, value] pairs for which the code
369
+ * block returns true.
370
+ */
371
+ static VALUE
372
+ fdbm_select(VALUE obj)
373
+ {
374
+ VALUE new = rb_ary_new();
375
+ datum key, val;
376
+ DBM *dbm;
377
+ struct dbmdata *dbmp;
378
+
379
+ GetDBM2(obj, dbmp, dbm);
380
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
381
+ VALUE assoc, v;
382
+ val = dbm_fetch(dbm, key);
383
+ assoc = rb_assoc_new(rb_tainted_str_new(key.dptr, key.dsize),
384
+ rb_tainted_str_new(val.dptr, val.dsize));
385
+ v = rb_yield(assoc);
386
+ if (RTEST(v)) {
387
+ rb_ary_push(new, assoc);
388
+ }
389
+ GetDBM2(obj, dbmp, dbm);
390
+ }
391
+
392
+ return new;
393
+ }
394
+
395
+ /*
396
+ * call-seq:
397
+ * dbm.values_at(key, ...) -> Array
398
+ *
399
+ * Returns an array containing the values associated with the given keys.
400
+ */
401
+ static VALUE
402
+ fdbm_values_at(int argc, VALUE *argv, VALUE obj)
403
+ {
404
+ VALUE new = rb_ary_new2(argc);
405
+ int i;
406
+
407
+ for (i=0; i<argc; i++) {
408
+ rb_ary_push(new, fdbm_fetch(obj, argv[i], Qnil));
409
+ }
410
+
411
+ return new;
412
+ }
413
+
414
+ static void
415
+ fdbm_modify(VALUE obj)
416
+ {
417
+ if (OBJ_FROZEN(obj)) rb_error_frozen("DBM");
418
+ }
419
+
420
+ /*
421
+ * call-seq:
422
+ * dbm.delete(key)
423
+ *
424
+ * Deletes an entry from the database.
425
+ */
426
+ static VALUE
427
+ fdbm_delete(VALUE obj, VALUE keystr)
428
+ {
429
+ datum key, value;
430
+ struct dbmdata *dbmp;
431
+ DBM *dbm;
432
+ VALUE valstr;
433
+ long len;
434
+
435
+ fdbm_modify(obj);
436
+ ExportStringValue(keystr);
437
+ len = RSTRING_LEN(keystr);
438
+ if (TOO_LONG(len)) goto not_found;
439
+ key.dptr = RSTRING_PTR(keystr);
440
+ key.dsize = (DSIZE_TYPE)len;
441
+
442
+ GetDBM2(obj, dbmp, dbm);
443
+
444
+ value = dbm_fetch(dbm, key);
445
+ if (value.dptr == 0) {
446
+ not_found:
447
+ if (rb_block_given_p()) return rb_yield(keystr);
448
+ return Qnil;
449
+ }
450
+
451
+ /* need to save value before dbm_delete() */
452
+ valstr = rb_tainted_str_new(value.dptr, value.dsize);
453
+
454
+ if (dbm_delete(dbm, key)) {
455
+ dbmp->di_size = -1;
456
+ rb_raise(rb_eDBMError, "dbm_delete failed");
457
+ }
458
+ else if (dbmp->di_size >= 0) {
459
+ dbmp->di_size--;
460
+ }
461
+ return valstr;
462
+ }
463
+
464
+ /*
465
+ * call-seq:
466
+ * dbm.shift() -> [key, value]
467
+ *
468
+ * Removes a [key, value] pair from the database, and returns it.
469
+ * If the database is empty, returns nil.
470
+ * The order in which values are removed/returned is not guaranteed.
471
+ */
472
+ static VALUE
473
+ fdbm_shift(VALUE obj)
474
+ {
475
+ datum key, val;
476
+ struct dbmdata *dbmp;
477
+ DBM *dbm;
478
+ VALUE keystr, valstr;
479
+
480
+ fdbm_modify(obj);
481
+ GetDBM2(obj, dbmp, dbm);
482
+ dbmp->di_size = -1;
483
+
484
+ key = dbm_firstkey(dbm);
485
+ if (!key.dptr) return Qnil;
486
+ val = dbm_fetch(dbm, key);
487
+ keystr = rb_tainted_str_new(key.dptr, key.dsize);
488
+ valstr = rb_tainted_str_new(val.dptr, val.dsize);
489
+ dbm_delete(dbm, key);
490
+
491
+ return rb_assoc_new(keystr, valstr);
492
+ }
493
+
494
+ /*
495
+ * call-seq:
496
+ * dbm.reject! {|key, value| block} -> self
497
+ * dbm.delete_if {|key, value| block} -> self
498
+ *
499
+ * Deletes all entries for which the code block returns true.
500
+ * Returns self.
501
+ */
502
+ static VALUE
503
+ fdbm_delete_if(VALUE obj)
504
+ {
505
+ datum key, val;
506
+ struct dbmdata *dbmp;
507
+ DBM *dbm;
508
+ VALUE keystr, valstr;
509
+ VALUE ret, ary = rb_ary_tmp_new(0);
510
+ int status = 0;
511
+ long i, n;
512
+
513
+ fdbm_modify(obj);
514
+ GetDBM2(obj, dbmp, dbm);
515
+ n = dbmp->di_size;
516
+ dbmp->di_size = -1;
517
+
518
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
519
+ val = dbm_fetch(dbm, key);
520
+ keystr = rb_tainted_str_new(key.dptr, key.dsize);
521
+ OBJ_FREEZE(keystr);
522
+ valstr = rb_tainted_str_new(val.dptr, val.dsize);
523
+ ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status);
524
+ if (status != 0) break;
525
+ if (RTEST(ret)) rb_ary_push(ary, keystr);
526
+ GetDBM2(obj, dbmp, dbm);
527
+ }
528
+
529
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
530
+ keystr = RARRAY_AREF(ary, i);
531
+ key.dptr = RSTRING_PTR(keystr);
532
+ key.dsize = (DSIZE_TYPE)RSTRING_LEN(keystr);
533
+ if (dbm_delete(dbm, key)) {
534
+ rb_raise(rb_eDBMError, "dbm_delete failed");
535
+ }
536
+ }
537
+ if (status) rb_jump_tag(status);
538
+ if (n > 0) dbmp->di_size = n - RARRAY_LEN(ary);
539
+ rb_ary_clear(ary);
540
+
541
+ return obj;
542
+ }
543
+
544
+ /*
545
+ * call-seq:
546
+ * dbm.clear
547
+ *
548
+ * Deletes all data from the database.
549
+ */
550
+ static VALUE
551
+ fdbm_clear(VALUE obj)
552
+ {
553
+ datum key;
554
+ struct dbmdata *dbmp;
555
+ DBM *dbm;
556
+
557
+ fdbm_modify(obj);
558
+ GetDBM2(obj, dbmp, dbm);
559
+ dbmp->di_size = -1;
560
+ while (key = dbm_firstkey(dbm), key.dptr) {
561
+ if (dbm_delete(dbm, key)) {
562
+ rb_raise(rb_eDBMError, "dbm_delete failed");
563
+ }
564
+ }
565
+ dbmp->di_size = 0;
566
+
567
+ return obj;
568
+ }
569
+
570
+ /*
571
+ * call-seq:
572
+ * dbm.invert -> hash
573
+ *
574
+ * Returns a Hash (not a DBM database) created by using each value in the
575
+ * database as a key, with the corresponding key as its value.
576
+ */
577
+ static VALUE
578
+ fdbm_invert(VALUE obj)
579
+ {
580
+ datum key, val;
581
+ struct dbmdata *dbmp;
582
+ DBM *dbm;
583
+ VALUE keystr, valstr;
584
+ VALUE hash = rb_hash_new();
585
+
586
+ GetDBM2(obj, dbmp, dbm);
587
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
588
+ val = dbm_fetch(dbm, key);
589
+ keystr = rb_tainted_str_new(key.dptr, key.dsize);
590
+ valstr = rb_tainted_str_new(val.dptr, val.dsize);
591
+ rb_hash_aset(hash, valstr, keystr);
592
+ }
593
+ return hash;
594
+ }
595
+
596
+ static VALUE fdbm_store(VALUE,VALUE,VALUE);
597
+
598
+ static VALUE
599
+ update_i(RB_BLOCK_CALL_FUNC_ARGLIST(pair, dbm))
600
+ {
601
+ const VALUE *ptr;
602
+ Check_Type(pair, T_ARRAY);
603
+ if (RARRAY_LEN(pair) < 2) {
604
+ rb_raise(rb_eArgError, "pair must be [key, value]");
605
+ }
606
+ ptr = RARRAY_CONST_PTR(pair);
607
+ fdbm_store(dbm, ptr[0], ptr[1]);
608
+ return Qnil;
609
+ }
610
+
611
+ /*
612
+ * call-seq:
613
+ * dbm.update(obj)
614
+ *
615
+ * Updates the database with multiple values from the specified object.
616
+ * Takes any object which implements the each_pair method, including
617
+ * Hash and DBM objects.
618
+ */
619
+ static VALUE
620
+ fdbm_update(VALUE obj, VALUE other)
621
+ {
622
+ rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
623
+ return obj;
624
+ }
625
+
626
+ /*
627
+ * call-seq:
628
+ * dbm.replace(obj)
629
+ *
630
+ * Replaces the contents of the database with the contents of the specified
631
+ * object. Takes any object which implements the each_pair method, including
632
+ * Hash and DBM objects.
633
+ */
634
+ static VALUE
635
+ fdbm_replace(VALUE obj, VALUE other)
636
+ {
637
+ fdbm_clear(obj);
638
+ rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
639
+ return obj;
640
+ }
641
+
642
+ /*
643
+ * call-seq:
644
+ * dbm.store(key, value) -> value
645
+ * dbm[key] = value
646
+ *
647
+ * Stores the specified string value in the database, indexed via the
648
+ * string key provided.
649
+ */
650
+ static VALUE
651
+ fdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
652
+ {
653
+ datum key, val;
654
+ struct dbmdata *dbmp;
655
+ DBM *dbm;
656
+
657
+ fdbm_modify(obj);
658
+ keystr = rb_obj_as_string(keystr);
659
+ valstr = rb_obj_as_string(valstr);
660
+
661
+ key.dptr = RSTRING_PTR(keystr);
662
+ key.dsize = RSTRING_DSIZE(keystr);
663
+
664
+ val.dptr = RSTRING_PTR(valstr);
665
+ val.dsize = RSTRING_DSIZE(valstr);
666
+
667
+ GetDBM2(obj, dbmp, dbm);
668
+ dbmp->di_size = -1;
669
+ if (dbm_store(dbm, key, val, DBM_REPLACE)) {
670
+ dbm_clearerr(dbm);
671
+ if (errno == EPERM) rb_sys_fail(0);
672
+ rb_raise(rb_eDBMError, "dbm_store failed");
673
+ }
674
+
675
+ return valstr;
676
+ }
677
+
678
+ /*
679
+ * call-seq:
680
+ * dbm.length -> integer
681
+ * dbm.size -> integer
682
+ *
683
+ * Returns the number of entries in the database.
684
+ */
685
+ static VALUE
686
+ fdbm_length(VALUE obj)
687
+ {
688
+ datum key;
689
+ struct dbmdata *dbmp;
690
+ DBM *dbm;
691
+ int i = 0;
692
+
693
+ GetDBM2(obj, dbmp, dbm);
694
+ if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
695
+
696
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
697
+ i++;
698
+ }
699
+ dbmp->di_size = i;
700
+
701
+ return INT2FIX(i);
702
+ }
703
+
704
+ /*
705
+ * call-seq:
706
+ * dbm.empty?
707
+ *
708
+ * Returns true if the database is empty, false otherwise.
709
+ */
710
+ static VALUE
711
+ fdbm_empty_p(VALUE obj)
712
+ {
713
+ datum key;
714
+ struct dbmdata *dbmp;
715
+ DBM *dbm;
716
+
717
+ GetDBM2(obj, dbmp, dbm);
718
+ if (dbmp->di_size < 0) {
719
+ dbm = dbmp->di_dbm;
720
+
721
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
722
+ return Qfalse;
723
+ }
724
+ }
725
+ else {
726
+ if (dbmp->di_size)
727
+ return Qfalse;
728
+ }
729
+ return Qtrue;
730
+ }
731
+
732
+ /*
733
+ * call-seq:
734
+ * dbm.each_value {|value| block} -> self
735
+ *
736
+ * Calls the block once for each value string in the database. Returns self.
737
+ */
738
+ static VALUE
739
+ fdbm_each_value(VALUE obj)
740
+ {
741
+ datum key, val;
742
+ struct dbmdata *dbmp;
743
+ DBM *dbm;
744
+
745
+ RETURN_ENUMERATOR(obj, 0, 0);
746
+
747
+ GetDBM2(obj, dbmp, dbm);
748
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
749
+ val = dbm_fetch(dbm, key);
750
+ rb_yield(rb_tainted_str_new(val.dptr, val.dsize));
751
+ GetDBM2(obj, dbmp, dbm);
752
+ }
753
+ return obj;
754
+ }
755
+
756
+ /*
757
+ * call-seq:
758
+ * dbm.each_key {|key| block} -> self
759
+ *
760
+ * Calls the block once for each key string in the database. Returns self.
761
+ */
762
+ static VALUE
763
+ fdbm_each_key(VALUE obj)
764
+ {
765
+ datum key;
766
+ struct dbmdata *dbmp;
767
+ DBM *dbm;
768
+
769
+ RETURN_ENUMERATOR(obj, 0, 0);
770
+
771
+ GetDBM2(obj, dbmp, dbm);
772
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
773
+ rb_yield(rb_tainted_str_new(key.dptr, key.dsize));
774
+ GetDBM2(obj, dbmp, dbm);
775
+ }
776
+ return obj;
777
+ }
778
+
779
+ /*
780
+ * call-seq:
781
+ * dbm.each_pair {|key,value| block} -> self
782
+ *
783
+ * Calls the block once for each [key, value] pair in the database.
784
+ * Returns self.
785
+ */
786
+ static VALUE
787
+ fdbm_each_pair(VALUE obj)
788
+ {
789
+ datum key, val;
790
+ DBM *dbm;
791
+ struct dbmdata *dbmp;
792
+ VALUE keystr, valstr;
793
+
794
+ RETURN_ENUMERATOR(obj, 0, 0);
795
+
796
+ GetDBM2(obj, dbmp, dbm);
797
+
798
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
799
+ val = dbm_fetch(dbm, key);
800
+ keystr = rb_tainted_str_new(key.dptr, key.dsize);
801
+ valstr = rb_tainted_str_new(val.dptr, val.dsize);
802
+ rb_yield(rb_assoc_new(keystr, valstr));
803
+ GetDBM2(obj, dbmp, dbm);
804
+ }
805
+
806
+ return obj;
807
+ }
808
+
809
+ /*
810
+ * call-seq:
811
+ * dbm.keys -> array
812
+ *
813
+ * Returns an array of all the string keys in the database.
814
+ */
815
+ static VALUE
816
+ fdbm_keys(VALUE obj)
817
+ {
818
+ datum key;
819
+ struct dbmdata *dbmp;
820
+ DBM *dbm;
821
+ VALUE ary;
822
+
823
+ GetDBM2(obj, dbmp, dbm);
824
+
825
+ ary = rb_ary_new();
826
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
827
+ rb_ary_push(ary, rb_tainted_str_new(key.dptr, key.dsize));
828
+ }
829
+
830
+ return ary;
831
+ }
832
+
833
+ /*
834
+ * call-seq:
835
+ * dbm.values -> array
836
+ *
837
+ * Returns an array of all the string values in the database.
838
+ */
839
+ static VALUE
840
+ fdbm_values(VALUE obj)
841
+ {
842
+ datum key, val;
843
+ struct dbmdata *dbmp;
844
+ DBM *dbm;
845
+ VALUE ary;
846
+
847
+ GetDBM2(obj, dbmp, dbm);
848
+ ary = rb_ary_new();
849
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
850
+ val = dbm_fetch(dbm, key);
851
+ rb_ary_push(ary, rb_tainted_str_new(val.dptr, val.dsize));
852
+ }
853
+
854
+ return ary;
855
+ }
856
+
857
+ /*
858
+ * call-seq:
859
+ * dbm.include?(key) -> boolean
860
+ * dbm.has_key?(key) -> boolean
861
+ * dbm.member?(key) -> boolean
862
+ * dbm.key?(key) -> boolean
863
+ *
864
+ * Returns true if the database contains the specified key, false otherwise.
865
+ */
866
+ static VALUE
867
+ fdbm_has_key(VALUE obj, VALUE keystr)
868
+ {
869
+ datum key, val;
870
+ struct dbmdata *dbmp;
871
+ DBM *dbm;
872
+ long len;
873
+
874
+ ExportStringValue(keystr);
875
+ len = RSTRING_LEN(keystr);
876
+ if (TOO_LONG(len)) return Qfalse;
877
+ key.dptr = RSTRING_PTR(keystr);
878
+ key.dsize = (DSIZE_TYPE)len;
879
+
880
+ GetDBM2(obj, dbmp, dbm);
881
+ val = dbm_fetch(dbm, key);
882
+ if (val.dptr) return Qtrue;
883
+ return Qfalse;
884
+ }
885
+
886
+ /*
887
+ * call-seq:
888
+ * dbm.has_value?(value) -> boolean
889
+ * dbm.value?(value) -> boolean
890
+ *
891
+ * Returns true if the database contains the specified string value, false
892
+ * otherwise.
893
+ */
894
+ static VALUE
895
+ fdbm_has_value(VALUE obj, VALUE valstr)
896
+ {
897
+ datum key, val;
898
+ struct dbmdata *dbmp;
899
+ DBM *dbm;
900
+ long len;
901
+
902
+ ExportStringValue(valstr);
903
+ len = RSTRING_LEN(valstr);
904
+ if (TOO_LONG(len)) return Qfalse;
905
+ val.dptr = RSTRING_PTR(valstr);
906
+ val.dsize = (DSIZE_TYPE)len;
907
+
908
+ GetDBM2(obj, dbmp, dbm);
909
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
910
+ val = dbm_fetch(dbm, key);
911
+ if ((DSIZE_TYPE)val.dsize == (DSIZE_TYPE)RSTRING_LEN(valstr) &&
912
+ memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0)
913
+ return Qtrue;
914
+ }
915
+ return Qfalse;
916
+ }
917
+
918
+ /*
919
+ * call-seq:
920
+ * dbm.to_a -> array
921
+ *
922
+ * Converts the contents of the database to an array of [key, value] arrays,
923
+ * and returns it.
924
+ */
925
+ static VALUE
926
+ fdbm_to_a(VALUE obj)
927
+ {
928
+ datum key, val;
929
+ struct dbmdata *dbmp;
930
+ DBM *dbm;
931
+ VALUE ary;
932
+
933
+ GetDBM2(obj, dbmp, dbm);
934
+ ary = rb_ary_new();
935
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
936
+ val = dbm_fetch(dbm, key);
937
+ rb_ary_push(ary, rb_assoc_new(rb_tainted_str_new(key.dptr, key.dsize),
938
+ rb_tainted_str_new(val.dptr, val.dsize)));
939
+ }
940
+
941
+ return ary;
942
+ }
943
+
944
+ /*
945
+ * call-seq:
946
+ * dbm.to_hash -> hash
947
+ *
948
+ * Converts the contents of the database to an in-memory Hash object, and
949
+ * returns it.
950
+ */
951
+ static VALUE
952
+ fdbm_to_hash(VALUE obj)
953
+ {
954
+ datum key, val;
955
+ struct dbmdata *dbmp;
956
+ DBM *dbm;
957
+ VALUE hash;
958
+
959
+ GetDBM2(obj, dbmp, dbm);
960
+ hash = rb_hash_new();
961
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
962
+ val = dbm_fetch(dbm, key);
963
+ rb_hash_aset(hash, rb_tainted_str_new(key.dptr, key.dsize),
964
+ rb_tainted_str_new(val.dptr, val.dsize));
965
+ }
966
+
967
+ return hash;
968
+ }
969
+
970
+ /*
971
+ * call-seq:
972
+ * dbm.reject {|key,value| block} -> Hash
973
+ *
974
+ * Converts the contents of the database to an in-memory Hash, then calls
975
+ * Hash#reject with the specified code block, returning a new Hash.
976
+ */
977
+ static VALUE
978
+ fdbm_reject(VALUE obj)
979
+ {
980
+ return rb_hash_delete_if(fdbm_to_hash(obj));
981
+ }
982
+
983
+ /*
984
+ * == Introduction
985
+ *
986
+ * The DBM class provides a wrapper to a Unix-style
987
+ * {dbm}[http://en.wikipedia.org/wiki/Dbm] or Database Manager library.
988
+ *
989
+ * Dbm databases do not have tables or columns; they are simple key-value
990
+ * data stores, like a Ruby Hash except not resident in RAM. Keys and values
991
+ * must be strings.
992
+ *
993
+ * The exact library used depends on how Ruby was compiled. It could be any
994
+ * of the following:
995
+ *
996
+ * - The original ndbm library is released in 4.3BSD.
997
+ * It is based on dbm library in Unix Version 7 but has different API to
998
+ * support multiple databases in a process.
999
+ * - {Berkeley DB}[http://en.wikipedia.org/wiki/Berkeley_DB] versions
1000
+ * 1 thru 5, also known as BDB and Sleepycat DB, now owned by Oracle
1001
+ * Corporation.
1002
+ * - Berkeley DB 1.x, still found in 4.4BSD derivatives (FreeBSD, OpenBSD, etc).
1003
+ * - {gdbm}[http://www.gnu.org/software/gdbm/], the GNU implementation of dbm.
1004
+ * - {qdbm}[http://fallabs.com/qdbm/index.html], another open source
1005
+ * reimplementation of dbm.
1006
+ *
1007
+ * All of these dbm implementations have their own Ruby interfaces
1008
+ * available, which provide richer (but varying) APIs.
1009
+ *
1010
+ * == Cautions
1011
+ *
1012
+ * Before you decide to use DBM, there are some issues you should consider:
1013
+ *
1014
+ * - Each implementation of dbm has its own file format. Generally, dbm
1015
+ * libraries will not read each other's files. This makes dbm files
1016
+ * a bad choice for data exchange.
1017
+ *
1018
+ * - Even running the same OS and the same dbm implementation, the database
1019
+ * file format may depend on the CPU architecture. For example, files may
1020
+ * not be portable between PowerPC and 386, or between 32 and 64 bit Linux.
1021
+ *
1022
+ * - Different versions of Berkeley DB use different file formats. A change to
1023
+ * the OS may therefore break DBM access to existing files.
1024
+ *
1025
+ * - Data size limits vary between implementations. Original Berkeley DB was
1026
+ * limited to 2GB of data. Dbm libraries also sometimes limit the total
1027
+ * size of a key/value pair, and the total size of all the keys that hash
1028
+ * to the same value. These limits can be as little as 512 bytes. That said,
1029
+ * gdbm and recent versions of Berkeley DB do away with these limits.
1030
+ *
1031
+ * Given the above cautions, DBM is not a good choice for long term storage of
1032
+ * important data. It is probably best used as a fast and easy alternative
1033
+ * to a Hash for processing large amounts of data.
1034
+ *
1035
+ * == Example
1036
+ *
1037
+ * require 'dbm'
1038
+ * db = DBM.open('rfcs', 0666, DBM::WRCREAT)
1039
+ * db['822'] = 'Standard for the Format of ARPA Internet Text Messages'
1040
+ * db['1123'] = 'Requirements for Internet Hosts - Application and Support'
1041
+ * db['3068'] = 'An Anycast Prefix for 6to4 Relay Routers'
1042
+ * puts db['822']
1043
+ */
1044
+ void
1045
+ Init_dbm(void)
1046
+ {
1047
+ rb_cDBM = rb_define_class("DBM", rb_cObject);
1048
+ /* Document-class: DBMError
1049
+ * Exception class used to return errors from the dbm library.
1050
+ */
1051
+ rb_eDBMError = rb_define_class("DBMError", rb_eStandardError);
1052
+ rb_include_module(rb_cDBM, rb_mEnumerable);
1053
+
1054
+ rb_define_alloc_func(rb_cDBM, fdbm_alloc);
1055
+ rb_define_singleton_method(rb_cDBM, "open", fdbm_s_open, -1);
1056
+
1057
+ rb_define_method(rb_cDBM, "initialize", fdbm_initialize, -1);
1058
+ rb_define_method(rb_cDBM, "close", fdbm_close, 0);
1059
+ rb_define_method(rb_cDBM, "closed?", fdbm_closed, 0);
1060
+ rb_define_method(rb_cDBM, "[]", fdbm_aref, 1);
1061
+ rb_define_method(rb_cDBM, "fetch", fdbm_fetch_m, -1);
1062
+ rb_define_method(rb_cDBM, "[]=", fdbm_store, 2);
1063
+ rb_define_method(rb_cDBM, "store", fdbm_store, 2);
1064
+ rb_define_method(rb_cDBM, "index", fdbm_index, 1);
1065
+ rb_define_method(rb_cDBM, "key", fdbm_key, 1);
1066
+ rb_define_method(rb_cDBM, "select", fdbm_select, 0);
1067
+ rb_define_method(rb_cDBM, "values_at", fdbm_values_at, -1);
1068
+ rb_define_method(rb_cDBM, "length", fdbm_length, 0);
1069
+ rb_define_method(rb_cDBM, "size", fdbm_length, 0);
1070
+ rb_define_method(rb_cDBM, "empty?", fdbm_empty_p, 0);
1071
+ rb_define_method(rb_cDBM, "each", fdbm_each_pair, 0);
1072
+ rb_define_method(rb_cDBM, "each_value", fdbm_each_value, 0);
1073
+ rb_define_method(rb_cDBM, "each_key", fdbm_each_key, 0);
1074
+ rb_define_method(rb_cDBM, "each_pair", fdbm_each_pair, 0);
1075
+ rb_define_method(rb_cDBM, "keys", fdbm_keys, 0);
1076
+ rb_define_method(rb_cDBM, "values", fdbm_values, 0);
1077
+ rb_define_method(rb_cDBM, "shift", fdbm_shift, 0);
1078
+ rb_define_method(rb_cDBM, "delete", fdbm_delete, 1);
1079
+ rb_define_method(rb_cDBM, "delete_if", fdbm_delete_if, 0);
1080
+ rb_define_method(rb_cDBM, "reject!", fdbm_delete_if, 0);
1081
+ rb_define_method(rb_cDBM, "reject", fdbm_reject, 0);
1082
+ rb_define_method(rb_cDBM, "clear", fdbm_clear, 0);
1083
+ rb_define_method(rb_cDBM, "invert", fdbm_invert, 0);
1084
+ rb_define_method(rb_cDBM, "update", fdbm_update, 1);
1085
+ rb_define_method(rb_cDBM, "replace", fdbm_replace, 1);
1086
+
1087
+ rb_define_method(rb_cDBM, "include?", fdbm_has_key, 1);
1088
+ rb_define_method(rb_cDBM, "has_key?", fdbm_has_key, 1);
1089
+ rb_define_method(rb_cDBM, "member?", fdbm_has_key, 1);
1090
+ rb_define_method(rb_cDBM, "has_value?", fdbm_has_value, 1);
1091
+ rb_define_method(rb_cDBM, "key?", fdbm_has_key, 1);
1092
+ rb_define_method(rb_cDBM, "value?", fdbm_has_value, 1);
1093
+
1094
+ rb_define_method(rb_cDBM, "to_a", fdbm_to_a, 0);
1095
+ rb_define_method(rb_cDBM, "to_hash", fdbm_to_hash, 0);
1096
+
1097
+ /* Indicates that dbm_open() should open the database in read-only mode */
1098
+ rb_define_const(rb_cDBM, "READER", INT2FIX(O_RDONLY|RUBY_DBM_RW_BIT));
1099
+
1100
+ /* Indicates that dbm_open() should open the database in read/write mode */
1101
+ rb_define_const(rb_cDBM, "WRITER", INT2FIX(O_RDWR|RUBY_DBM_RW_BIT));
1102
+
1103
+ /* Indicates that dbm_open() should open the database in read/write mode,
1104
+ * and create it if it does not already exist
1105
+ */
1106
+ rb_define_const(rb_cDBM, "WRCREAT", INT2FIX(O_RDWR|O_CREAT|RUBY_DBM_RW_BIT));
1107
+
1108
+ /* Indicates that dbm_open() should open the database in read/write mode,
1109
+ * create it if it does not already exist, and delete all contents if it
1110
+ * does already exist.
1111
+ */
1112
+ rb_define_const(rb_cDBM, "NEWDB", INT2FIX(O_RDWR|O_CREAT|O_TRUNC|RUBY_DBM_RW_BIT));
1113
+
1114
+ {
1115
+ VALUE version;
1116
+ #if defined(_DBM_IOERR)
1117
+ version = rb_str_new2("ndbm (4.3BSD)");
1118
+ #elif defined(RUBYDBM_GDBM_HEADER)
1119
+ # if defined(HAVE_DECLARED_LIBVAR_GDBM_VERSION)
1120
+ /* since gdbm 1.9 */
1121
+ version = rb_str_new2(gdbm_version);
1122
+ # elif defined(HAVE_UNDECLARED_LIBVAR_GDBM_VERSION)
1123
+ /* ndbm.h doesn't declare gdbm_version until gdbm 1.8.3.
1124
+ * See extconf.rb for more information. */
1125
+ RUBY_EXTERN char *gdbm_version;
1126
+ version = rb_str_new2(gdbm_version);
1127
+ # else
1128
+ version = rb_str_new2("GDBM (unknown)");
1129
+ # endif
1130
+ #elif defined(RUBYDBM_DB_HEADER)
1131
+ # if defined(HAVE_DB_VERSION)
1132
+ /* The version of the dbm library, if using Berkeley DB */
1133
+ version = rb_str_new2(db_version(NULL, NULL, NULL));
1134
+ # else
1135
+ version = rb_str_new2("Berkeley DB (unknown)");
1136
+ # endif
1137
+ #elif defined(_RELIC_H)
1138
+ # if defined(HAVE_DPVERSION)
1139
+ version = rb_sprintf("QDBM %s", dpversion);
1140
+ # else
1141
+ version = rb_str_new2("QDBM (unknown)");
1142
+ # endif
1143
+ #else
1144
+ version = rb_str_new2("ndbm (unknown)");
1145
+ #endif
1146
+ /*
1147
+ * Identifies ndbm library version.
1148
+ *
1149
+ * Examples:
1150
+ *
1151
+ * - "ndbm (4.3BSD)"
1152
+ * - "Berkeley DB 4.8.30: (April 9, 2010)"
1153
+ * - "Berkeley DB (unknown)" (4.4BSD, maybe)
1154
+ * - "GDBM version 1.8.3. 10/15/2002 (built Jul 1 2011 12:32:45)"
1155
+ * - "QDBM 1.8.78"
1156
+ *
1157
+ */
1158
+ rb_define_const(rb_cDBM, "VERSION", version);
1159
+ }
1160
+ }