dbm 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }