rubysl-dbm 2.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9b01efe0b11f70480e16b908a646565b0107bbe2
4
+ data.tar.gz: 63fea60edc8b938ce05a2da4935ec16718cedd4d
5
+ SHA512:
6
+ metadata.gz: 553d6ee38e78d4d1848f0a5b10e271b72c9f73e28cefc767c9dfa07000bc63f9a9b2335a39e494d15935885b7abc96b69d9062d141601caaca0b9c0768d2bf98
7
+ data.tar.gz: 7249cec13d5f82f0975330afe4a808619ed794bff3e758cb3ded69e5908536a3994e527286d04698ec9bad3a849c7eac8926783d8e076e39c5dba5eb2faba6d2
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec spec
5
+ rvm:
6
+ - rbx-nightly-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-dbm.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,29 @@
1
+ # Rubysl::Dbm
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-dbm'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-dbm
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,1042 @@
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
+ static VALUE rb_cDBM, rb_eDBMError;
25
+
26
+ #define RUBY_DBM_RW_BIT 0x20000000
27
+
28
+ struct dbmdata {
29
+ long di_size;
30
+ DBM *di_dbm;
31
+ };
32
+
33
+ static void
34
+ closed_dbm(void)
35
+ {
36
+ rb_raise(rb_eDBMError, "closed DBM file");
37
+ }
38
+
39
+ #define GetDBM(obj, dbmp) {\
40
+ Data_Get_Struct((obj), struct dbmdata, (dbmp));\
41
+ if ((dbmp) == 0) closed_dbm();\
42
+ if ((dbmp)->di_dbm == 0) closed_dbm();\
43
+ }
44
+
45
+ #define GetDBM2(obj, data, dbm) {\
46
+ GetDBM((obj), (data));\
47
+ (dbm) = dbmp->di_dbm;\
48
+ }
49
+
50
+ static void
51
+ free_dbm(struct dbmdata *dbmp)
52
+ {
53
+ if (dbmp) {
54
+ if (dbmp->di_dbm) dbm_close(dbmp->di_dbm);
55
+ xfree(dbmp);
56
+ }
57
+ }
58
+
59
+ /*
60
+ * call-seq:
61
+ * dbm.close
62
+ *
63
+ * Closes the database.
64
+ */
65
+ static VALUE
66
+ fdbm_close(VALUE obj)
67
+ {
68
+ struct dbmdata *dbmp;
69
+
70
+ GetDBM(obj, dbmp);
71
+ dbm_close(dbmp->di_dbm);
72
+ dbmp->di_dbm = 0;
73
+
74
+ return Qnil;
75
+ }
76
+
77
+ /*
78
+ * call-seq:
79
+ * dbm.closed? -> true or false
80
+ *
81
+ * Returns true if the database is closed, false otherwise.
82
+ */
83
+ static VALUE
84
+ fdbm_closed(VALUE obj)
85
+ {
86
+ struct dbmdata *dbmp;
87
+
88
+ Data_Get_Struct(obj, struct dbmdata, dbmp);
89
+ if (dbmp == 0)
90
+ return Qtrue;
91
+ if (dbmp->di_dbm == 0)
92
+ return Qtrue;
93
+
94
+ return Qfalse;
95
+ }
96
+
97
+ static VALUE
98
+ fdbm_alloc(VALUE klass)
99
+ {
100
+ return Data_Wrap_Struct(klass, 0, free_dbm, 0);
101
+ }
102
+
103
+ /*
104
+ * call-seq:
105
+ * DBM.new(filename[, mode[, flags]]) -> dbm
106
+ *
107
+ * Open a dbm database with the specified name, which can include a directory
108
+ * path. Any file extensions needed will be supplied automatically by the dbm
109
+ * library. For example, Berkeley DB appends '.db', and GNU gdbm uses two
110
+ * physical files with extensions '.dir' and '.pag'.
111
+ *
112
+ * The mode should be an integer, as for Unix chmod.
113
+ *
114
+ * Flags should be one of READER, WRITER, WRCREAT or NEWDB.
115
+ */
116
+ static VALUE
117
+ fdbm_initialize(int argc, VALUE *argv, VALUE obj)
118
+ {
119
+ volatile VALUE file;
120
+ VALUE vmode, vflags;
121
+ DBM *dbm;
122
+ struct dbmdata *dbmp;
123
+ int mode, flags = 0;
124
+
125
+ if (rb_scan_args(argc, argv, "12", &file, &vmode, &vflags) == 1) {
126
+ mode = 0666; /* default value */
127
+ }
128
+ else if (NIL_P(vmode)) {
129
+ mode = -1; /* return nil if DB not exist */
130
+ }
131
+ else {
132
+ mode = NUM2INT(vmode);
133
+ }
134
+
135
+ if (!NIL_P(vflags))
136
+ flags = NUM2INT(vflags);
137
+
138
+ FilePathValue(file);
139
+
140
+ if (flags & RUBY_DBM_RW_BIT) {
141
+ flags &= ~RUBY_DBM_RW_BIT;
142
+ dbm = dbm_open(RSTRING_PTR(file), flags, mode);
143
+ }
144
+ else {
145
+ dbm = 0;
146
+ if (mode >= 0) {
147
+ dbm = dbm_open(RSTRING_PTR(file), O_RDWR|O_CREAT, mode);
148
+ }
149
+ if (!dbm) {
150
+ dbm = dbm_open(RSTRING_PTR(file), O_RDWR, 0);
151
+ }
152
+ if (!dbm) {
153
+ dbm = dbm_open(RSTRING_PTR(file), O_RDONLY, 0);
154
+ }
155
+ }
156
+
157
+ if (!dbm) {
158
+ if (mode == -1) return Qnil;
159
+ rb_sys_fail(RSTRING_PTR(file));
160
+ }
161
+
162
+ dbmp = ALLOC(struct dbmdata);
163
+ DATA_PTR(obj) = dbmp;
164
+ dbmp->di_dbm = dbm;
165
+ dbmp->di_size = -1;
166
+
167
+ return obj;
168
+ }
169
+
170
+ /*
171
+ * call-seq:
172
+ * DBM.open(filename[, mode[, flags]]) -> dbm
173
+ * DBM.open(filename[, mode[, flags]]) {|dbm| block}
174
+ *
175
+ * Open a dbm database and yields it if a block is given. See also
176
+ * <code>DBM.new</code>.
177
+ */
178
+ static VALUE
179
+ fdbm_s_open(int argc, VALUE *argv, VALUE klass)
180
+ {
181
+ VALUE obj = Data_Wrap_Struct(klass, 0, free_dbm, 0);
182
+
183
+ if (NIL_P(fdbm_initialize(argc, argv, obj))) {
184
+ return Qnil;
185
+ }
186
+
187
+ if (rb_block_given_p()) {
188
+ return rb_ensure(rb_yield, obj, fdbm_close, obj);
189
+ }
190
+
191
+ return obj;
192
+ }
193
+
194
+ static VALUE
195
+ fdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone)
196
+ {
197
+ datum key, value;
198
+ struct dbmdata *dbmp;
199
+ DBM *dbm;
200
+
201
+ ExportStringValue(keystr);
202
+ key.dptr = RSTRING_PTR(keystr);
203
+ key.dsize = (int)RSTRING_LEN(keystr);
204
+
205
+ GetDBM2(obj, dbmp, dbm);
206
+ value = dbm_fetch(dbm, key);
207
+ if (value.dptr == 0) {
208
+ if (ifnone == Qnil && rb_block_given_p())
209
+ return rb_yield(rb_tainted_str_new(key.dptr, key.dsize));
210
+ return ifnone;
211
+ }
212
+ return rb_tainted_str_new(value.dptr, value.dsize);
213
+ }
214
+
215
+ /*
216
+ * call-seq:
217
+ * dbm[key] -> string value or nil
218
+ *
219
+ * Return a value from the database by locating the key string
220
+ * provided. If the key is not found, returns nil.
221
+ */
222
+ static VALUE
223
+ fdbm_aref(VALUE obj, VALUE keystr)
224
+ {
225
+ return fdbm_fetch(obj, keystr, Qnil);
226
+ }
227
+
228
+ /*
229
+ * call-seq:
230
+ * dbm.fetch(key[, ifnone]) -> value
231
+ *
232
+ * Return a value from the database by locating the key string
233
+ * provided. If the key is not found, returns +ifnone+. If +ifnone+
234
+ * is not given, raises IndexError.
235
+ */
236
+ static VALUE
237
+ fdbm_fetch_m(int argc, VALUE *argv, VALUE obj)
238
+ {
239
+ VALUE keystr, valstr, ifnone;
240
+
241
+ rb_scan_args(argc, argv, "11", &keystr, &ifnone);
242
+ valstr = fdbm_fetch(obj, keystr, ifnone);
243
+ if (argc == 1 && !rb_block_given_p() && NIL_P(valstr))
244
+ rb_raise(rb_eIndexError, "key not found");
245
+
246
+ return valstr;
247
+ }
248
+
249
+ /*
250
+ * call-seq:
251
+ * dbm.key(value) -> string
252
+ *
253
+ * Returns the key for the specified value.
254
+ */
255
+ static VALUE
256
+ fdbm_key(VALUE obj, VALUE valstr)
257
+ {
258
+ datum key, val;
259
+ struct dbmdata *dbmp;
260
+ DBM *dbm;
261
+
262
+ ExportStringValue(valstr);
263
+ val.dptr = RSTRING_PTR(valstr);
264
+ val.dsize = (int)RSTRING_LEN(valstr);
265
+
266
+ GetDBM2(obj, dbmp, dbm);
267
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
268
+ val = dbm_fetch(dbm, key);
269
+ if ((long)val.dsize == (int)RSTRING_LEN(valstr) &&
270
+ memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0) {
271
+ return rb_tainted_str_new(key.dptr, key.dsize);
272
+ }
273
+ }
274
+ return Qnil;
275
+ }
276
+
277
+ /* :nodoc: */
278
+ static VALUE
279
+ fdbm_index(VALUE hash, VALUE value)
280
+ {
281
+ rb_warn("DBM#index is deprecated; use DBM#key");
282
+ return fdbm_key(hash, value);
283
+ }
284
+
285
+ /*
286
+ * call-seq:
287
+ * dbm.select {|key, value| block} -> array
288
+ *
289
+ * Returns a new array consisting of the [key, value] pairs for which the code
290
+ * block returns true.
291
+ */
292
+ static VALUE
293
+ fdbm_select(VALUE obj)
294
+ {
295
+ VALUE new = rb_ary_new();
296
+ datum key, val;
297
+ DBM *dbm;
298
+ struct dbmdata *dbmp;
299
+
300
+ GetDBM2(obj, dbmp, dbm);
301
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
302
+ VALUE assoc, v;
303
+ val = dbm_fetch(dbm, key);
304
+ assoc = rb_assoc_new(rb_tainted_str_new(key.dptr, key.dsize),
305
+ rb_tainted_str_new(val.dptr, val.dsize));
306
+ v = rb_yield(assoc);
307
+ if (RTEST(v)) {
308
+ rb_ary_push(new, assoc);
309
+ }
310
+ GetDBM2(obj, dbmp, dbm);
311
+ }
312
+
313
+ return new;
314
+ }
315
+
316
+ /*
317
+ * call-seq:
318
+ * dbm.values_at(key, ...) -> Array
319
+ *
320
+ * Returns an array containing the values associated with the given keys.
321
+ */
322
+ static VALUE
323
+ fdbm_values_at(int argc, VALUE *argv, VALUE obj)
324
+ {
325
+ VALUE new = rb_ary_new2(argc);
326
+ int i;
327
+
328
+ for (i=0; i<argc; i++) {
329
+ rb_ary_push(new, fdbm_fetch(obj, argv[i], Qnil));
330
+ }
331
+
332
+ return new;
333
+ }
334
+
335
+ static void
336
+ fdbm_modify(VALUE obj)
337
+ {
338
+ rb_secure(4);
339
+ if (OBJ_FROZEN(obj)) rb_error_frozen("DBM");
340
+ }
341
+
342
+ /*
343
+ * call-seq:
344
+ * dbm.delete(key)
345
+ *
346
+ * Deletes an entry from the database.
347
+ */
348
+ static VALUE
349
+ fdbm_delete(VALUE obj, VALUE keystr)
350
+ {
351
+ datum key, value;
352
+ struct dbmdata *dbmp;
353
+ DBM *dbm;
354
+ VALUE valstr;
355
+
356
+ fdbm_modify(obj);
357
+ ExportStringValue(keystr);
358
+ key.dptr = RSTRING_PTR(keystr);
359
+ key.dsize = (int)RSTRING_LEN(keystr);
360
+
361
+ GetDBM2(obj, dbmp, dbm);
362
+
363
+ value = dbm_fetch(dbm, key);
364
+ if (value.dptr == 0) {
365
+ if (rb_block_given_p()) return rb_yield(keystr);
366
+ return Qnil;
367
+ }
368
+
369
+ /* need to save value before dbm_delete() */
370
+ valstr = rb_tainted_str_new(value.dptr, value.dsize);
371
+
372
+ if (dbm_delete(dbm, key)) {
373
+ dbmp->di_size = -1;
374
+ rb_raise(rb_eDBMError, "dbm_delete failed");
375
+ }
376
+ else if (dbmp->di_size >= 0) {
377
+ dbmp->di_size--;
378
+ }
379
+ return valstr;
380
+ }
381
+
382
+ /*
383
+ * call-seq:
384
+ * dbm.shift() -> [key, value]
385
+ *
386
+ * Removes a [key, value] pair from the database, and returns it.
387
+ * If the database is empty, returns nil.
388
+ * The order in which values are removed/returned is not guaranteed.
389
+ */
390
+ static VALUE
391
+ fdbm_shift(VALUE obj)
392
+ {
393
+ datum key, val;
394
+ struct dbmdata *dbmp;
395
+ DBM *dbm;
396
+ VALUE keystr, valstr;
397
+
398
+ fdbm_modify(obj);
399
+ GetDBM2(obj, dbmp, dbm);
400
+ dbmp->di_size = -1;
401
+
402
+ key = dbm_firstkey(dbm);
403
+ if (!key.dptr) return Qnil;
404
+ val = dbm_fetch(dbm, key);
405
+ keystr = rb_tainted_str_new(key.dptr, key.dsize);
406
+ valstr = rb_tainted_str_new(val.dptr, val.dsize);
407
+ dbm_delete(dbm, key);
408
+
409
+ return rb_assoc_new(keystr, valstr);
410
+ }
411
+
412
+ /*
413
+ * call-seq:
414
+ * dbm.reject! {|key, value| block} -> self
415
+ * dbm.delete_if {|key, value| block} -> self
416
+ *
417
+ * Deletes all entries for which the code block returns true.
418
+ * Returns self.
419
+ */
420
+ static VALUE
421
+ fdbm_delete_if(VALUE obj)
422
+ {
423
+ datum key, val;
424
+ struct dbmdata *dbmp;
425
+ DBM *dbm;
426
+ VALUE keystr, valstr;
427
+ VALUE ret, ary = rb_ary_new();
428
+ int i, status = 0;
429
+ long n;
430
+
431
+ fdbm_modify(obj);
432
+ GetDBM2(obj, dbmp, dbm);
433
+ n = dbmp->di_size;
434
+ dbmp->di_size = -1;
435
+
436
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
437
+ val = dbm_fetch(dbm, key);
438
+ keystr = rb_tainted_str_new(key.dptr, key.dsize);
439
+ valstr = rb_tainted_str_new(val.dptr, val.dsize);
440
+ ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status);
441
+ if (status != 0) break;
442
+ if (RTEST(ret)) rb_ary_push(ary, keystr);
443
+ GetDBM2(obj, dbmp, dbm);
444
+ }
445
+
446
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
447
+ keystr = rb_ary_entry(ary, i);
448
+ ExportStringValue(keystr);
449
+ key.dptr = RSTRING_PTR(keystr);
450
+ key.dsize = (int)RSTRING_LEN(keystr);
451
+ if (dbm_delete(dbm, key)) {
452
+ rb_raise(rb_eDBMError, "dbm_delete failed");
453
+ }
454
+ }
455
+ if (status) rb_jump_tag(status);
456
+ if (n > 0) dbmp->di_size = n - RARRAY_LEN(ary);
457
+
458
+ return obj;
459
+ }
460
+
461
+ /*
462
+ * call-seq:
463
+ * dbm.clear
464
+ *
465
+ * Deletes all data from the database.
466
+ */
467
+ static VALUE
468
+ fdbm_clear(VALUE obj)
469
+ {
470
+ datum key;
471
+ struct dbmdata *dbmp;
472
+ DBM *dbm;
473
+
474
+ fdbm_modify(obj);
475
+ GetDBM2(obj, dbmp, dbm);
476
+ dbmp->di_size = -1;
477
+ while (key = dbm_firstkey(dbm), key.dptr) {
478
+ if (dbm_delete(dbm, key)) {
479
+ rb_raise(rb_eDBMError, "dbm_delete failed");
480
+ }
481
+ }
482
+ dbmp->di_size = 0;
483
+
484
+ return obj;
485
+ }
486
+
487
+ /*
488
+ * call-seq:
489
+ * dbm.invert -> hash
490
+ *
491
+ * Returns a Hash (not a DBM database) created by using each value in the
492
+ * database as a key, with the corresponding key as its value.
493
+ */
494
+ static VALUE
495
+ fdbm_invert(VALUE obj)
496
+ {
497
+ datum key, val;
498
+ struct dbmdata *dbmp;
499
+ DBM *dbm;
500
+ VALUE keystr, valstr;
501
+ VALUE hash = rb_hash_new();
502
+
503
+ GetDBM2(obj, dbmp, dbm);
504
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
505
+ val = dbm_fetch(dbm, key);
506
+ keystr = rb_tainted_str_new(key.dptr, key.dsize);
507
+ valstr = rb_tainted_str_new(val.dptr, val.dsize);
508
+ rb_hash_aset(hash, valstr, keystr);
509
+ }
510
+ return hash;
511
+ }
512
+
513
+ static VALUE fdbm_store(VALUE,VALUE,VALUE);
514
+
515
+ static VALUE
516
+ update_i(VALUE pair, VALUE dbm)
517
+ {
518
+ Check_Type(pair, T_ARRAY);
519
+ if (RARRAY_LEN(pair) < 2) {
520
+ rb_raise(rb_eArgError, "pair must be [key, value]");
521
+ }
522
+ fdbm_store(dbm, rb_ary_entry(pair, 0), rb_ary_entry(pair, 1));
523
+ return Qnil;
524
+ }
525
+
526
+ /*
527
+ * call-seq:
528
+ * dbm.update(obj)
529
+ *
530
+ * Updates the database with multiple values from the specified object.
531
+ * Takes any object which implements the each_pair method, including
532
+ * Hash and DBM objects.
533
+ */
534
+ static VALUE
535
+ fdbm_update(VALUE obj, VALUE other)
536
+ {
537
+ rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
538
+ return obj;
539
+ }
540
+
541
+ /*
542
+ * call-seq:
543
+ * dbm.replace(obj)
544
+ *
545
+ * Replaces the contents of the database with the contents of the specified
546
+ * object. Takes any object which implements the each_pair method, including
547
+ * Hash and DBM objects.
548
+ */
549
+ static VALUE
550
+ fdbm_replace(VALUE obj, VALUE other)
551
+ {
552
+ fdbm_clear(obj);
553
+ rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
554
+ return obj;
555
+ }
556
+
557
+ /*
558
+ * call-seq:
559
+ * dbm.store(key, value) -> value
560
+ * dbm[key] = value
561
+ *
562
+ * Stores the specified string value in the database, indexed via the
563
+ * string key provided.
564
+ */
565
+ static VALUE
566
+ fdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
567
+ {
568
+ datum key, val;
569
+ struct dbmdata *dbmp;
570
+ DBM *dbm;
571
+
572
+ fdbm_modify(obj);
573
+ keystr = rb_obj_as_string(keystr);
574
+ valstr = rb_obj_as_string(valstr);
575
+
576
+ key.dptr = RSTRING_PTR(keystr);
577
+ key.dsize = (int)RSTRING_LEN(keystr);
578
+
579
+ val.dptr = RSTRING_PTR(valstr);
580
+ val.dsize = (int)RSTRING_LEN(valstr);
581
+
582
+ GetDBM2(obj, dbmp, dbm);
583
+ dbmp->di_size = -1;
584
+ if (dbm_store(dbm, key, val, DBM_REPLACE)) {
585
+ #ifdef HAVE_DBM_CLEARERR
586
+ dbm_clearerr(dbm);
587
+ #endif
588
+ if (errno == EPERM) rb_sys_fail(0);
589
+ rb_raise(rb_eDBMError, "dbm_store failed");
590
+ }
591
+
592
+ return valstr;
593
+ }
594
+
595
+ /*
596
+ * call-seq:
597
+ * dbm.length -> integer
598
+ *
599
+ * Returns the number of entries in the database.
600
+ */
601
+ static VALUE
602
+ fdbm_length(VALUE obj)
603
+ {
604
+ datum key;
605
+ struct dbmdata *dbmp;
606
+ DBM *dbm;
607
+ int i = 0;
608
+
609
+ GetDBM2(obj, dbmp, dbm);
610
+ if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
611
+
612
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
613
+ i++;
614
+ }
615
+ dbmp->di_size = i;
616
+
617
+ return INT2FIX(i);
618
+ }
619
+
620
+ /*
621
+ * call-seq:
622
+ * dbm.empty?
623
+ *
624
+ * Returns true if the database is empty, false otherwise.
625
+ */
626
+ static VALUE
627
+ fdbm_empty_p(VALUE obj)
628
+ {
629
+ datum key;
630
+ struct dbmdata *dbmp;
631
+ DBM *dbm;
632
+ int i = 0;
633
+
634
+ GetDBM2(obj, dbmp, dbm);
635
+ if (dbmp->di_size < 0) {
636
+ dbm = dbmp->di_dbm;
637
+
638
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
639
+ i++;
640
+ }
641
+ }
642
+ else {
643
+ i = (int)dbmp->di_size;
644
+ }
645
+ if (i == 0) return Qtrue;
646
+ return Qfalse;
647
+ }
648
+
649
+ /*
650
+ * call-seq:
651
+ * dbm.each_value {|value| block} -> self
652
+ *
653
+ * Calls the block once for each value string in the database. Returns self.
654
+ */
655
+ static VALUE
656
+ fdbm_each_value(VALUE obj)
657
+ {
658
+ datum key, val;
659
+ struct dbmdata *dbmp;
660
+ DBM *dbm;
661
+
662
+ RETURN_ENUMERATOR(obj, 0, 0);
663
+
664
+ GetDBM2(obj, dbmp, dbm);
665
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
666
+ val = dbm_fetch(dbm, key);
667
+ rb_yield(rb_tainted_str_new(val.dptr, val.dsize));
668
+ GetDBM2(obj, dbmp, dbm);
669
+ }
670
+ return obj;
671
+ }
672
+
673
+ /*
674
+ * call-seq:
675
+ * dbm.each_key {|key| block} -> self
676
+ *
677
+ * Calls the block once for each key string in the database. Returns self.
678
+ */
679
+ static VALUE
680
+ fdbm_each_key(VALUE obj)
681
+ {
682
+ datum key;
683
+ struct dbmdata *dbmp;
684
+ DBM *dbm;
685
+
686
+ RETURN_ENUMERATOR(obj, 0, 0);
687
+
688
+ GetDBM2(obj, dbmp, dbm);
689
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
690
+ rb_yield(rb_tainted_str_new(key.dptr, key.dsize));
691
+ GetDBM2(obj, dbmp, dbm);
692
+ }
693
+ return obj;
694
+ }
695
+
696
+ /*
697
+ * call-seq:
698
+ * dbm.each_pair {|key,value| block} -> self
699
+ *
700
+ * Calls the block once for each [key, value] pair in the database.
701
+ * Returns self.
702
+ */
703
+ static VALUE
704
+ fdbm_each_pair(VALUE obj)
705
+ {
706
+ datum key, val;
707
+ DBM *dbm;
708
+ struct dbmdata *dbmp;
709
+ VALUE keystr, valstr;
710
+
711
+ RETURN_ENUMERATOR(obj, 0, 0);
712
+
713
+ GetDBM2(obj, dbmp, dbm);
714
+
715
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
716
+ val = dbm_fetch(dbm, key);
717
+ keystr = rb_tainted_str_new(key.dptr, key.dsize);
718
+ valstr = rb_tainted_str_new(val.dptr, val.dsize);
719
+ rb_yield(rb_assoc_new(keystr, valstr));
720
+ GetDBM2(obj, dbmp, dbm);
721
+ }
722
+
723
+ return obj;
724
+ }
725
+
726
+ /*
727
+ * call-seq:
728
+ * dbm.keys -> array
729
+ *
730
+ * Returns an array of all the string keys in the database.
731
+ */
732
+ static VALUE
733
+ fdbm_keys(VALUE obj)
734
+ {
735
+ datum key;
736
+ struct dbmdata *dbmp;
737
+ DBM *dbm;
738
+ VALUE ary;
739
+
740
+ GetDBM2(obj, dbmp, dbm);
741
+
742
+ ary = rb_ary_new();
743
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
744
+ rb_ary_push(ary, rb_tainted_str_new(key.dptr, key.dsize));
745
+ }
746
+
747
+ return ary;
748
+ }
749
+
750
+ /*
751
+ * call-seq:
752
+ * dbm.values -> array
753
+ *
754
+ * Returns an array of all the string values in the database.
755
+ */
756
+ static VALUE
757
+ fdbm_values(VALUE obj)
758
+ {
759
+ datum key, val;
760
+ struct dbmdata *dbmp;
761
+ DBM *dbm;
762
+ VALUE ary;
763
+
764
+ GetDBM2(obj, dbmp, dbm);
765
+ ary = rb_ary_new();
766
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
767
+ val = dbm_fetch(dbm, key);
768
+ rb_ary_push(ary, rb_tainted_str_new(val.dptr, val.dsize));
769
+ }
770
+
771
+ return ary;
772
+ }
773
+
774
+ /*
775
+ * call-seq:
776
+ * dbm.has_key?(key) -> boolean
777
+ *
778
+ * Returns true if the database contains the specified key, false otherwise.
779
+ */
780
+ static VALUE
781
+ fdbm_has_key(VALUE obj, VALUE keystr)
782
+ {
783
+ datum key, val;
784
+ struct dbmdata *dbmp;
785
+ DBM *dbm;
786
+
787
+ ExportStringValue(keystr);
788
+ key.dptr = RSTRING_PTR(keystr);
789
+ key.dsize = (int)RSTRING_LEN(keystr);
790
+
791
+ GetDBM2(obj, dbmp, dbm);
792
+ val = dbm_fetch(dbm, key);
793
+ if (val.dptr) return Qtrue;
794
+ return Qfalse;
795
+ }
796
+
797
+ /*
798
+ * call-seq:
799
+ * dbm.has_value?(value) -> boolean
800
+ *
801
+ * Returns true if the database contains the specified string value, false
802
+ * otherwise.
803
+ */
804
+ static VALUE
805
+ fdbm_has_value(VALUE obj, VALUE valstr)
806
+ {
807
+ datum key, val;
808
+ struct dbmdata *dbmp;
809
+ DBM *dbm;
810
+
811
+ ExportStringValue(valstr);
812
+ val.dptr = RSTRING_PTR(valstr);
813
+ val.dsize = (int)RSTRING_LEN(valstr);
814
+
815
+ GetDBM2(obj, dbmp, dbm);
816
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
817
+ val = dbm_fetch(dbm, key);
818
+ if (val.dsize == (int)RSTRING_LEN(valstr) &&
819
+ memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0)
820
+ return Qtrue;
821
+ }
822
+ return Qfalse;
823
+ }
824
+
825
+ /*
826
+ * call-seq:
827
+ * dbm.to_a -> array
828
+ *
829
+ * Converts the contents of the database to an array of [key, value] arrays,
830
+ * and returns it.
831
+ */
832
+ static VALUE
833
+ fdbm_to_a(VALUE obj)
834
+ {
835
+ datum key, val;
836
+ struct dbmdata *dbmp;
837
+ DBM *dbm;
838
+ VALUE ary;
839
+
840
+ GetDBM2(obj, dbmp, dbm);
841
+ ary = rb_ary_new();
842
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
843
+ val = dbm_fetch(dbm, key);
844
+ rb_ary_push(ary, rb_assoc_new(rb_tainted_str_new(key.dptr, key.dsize),
845
+ rb_tainted_str_new(val.dptr, val.dsize)));
846
+ }
847
+
848
+ return ary;
849
+ }
850
+
851
+ /*
852
+ * call-seq:
853
+ * dbm.to_hash -> hash
854
+ *
855
+ * Converts the contents of the database to an in-memory Hash object, and
856
+ * returns it.
857
+ */
858
+ static VALUE
859
+ fdbm_to_hash(VALUE obj)
860
+ {
861
+ datum key, val;
862
+ struct dbmdata *dbmp;
863
+ DBM *dbm;
864
+ VALUE hash;
865
+
866
+ GetDBM2(obj, dbmp, dbm);
867
+ hash = rb_hash_new();
868
+ for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) {
869
+ val = dbm_fetch(dbm, key);
870
+ rb_hash_aset(hash, rb_tainted_str_new(key.dptr, key.dsize),
871
+ rb_tainted_str_new(val.dptr, val.dsize));
872
+ }
873
+
874
+ return hash;
875
+ }
876
+
877
+ /*
878
+ * call-seq:
879
+ * dbm.reject {|key,value| block} -> Hash
880
+ *
881
+ * Converts the contents of the database to an in-memory Hash, then calls
882
+ * Hash#reject with the specified code block, returning a new Hash.
883
+ */
884
+ static VALUE
885
+ fdbm_reject(VALUE obj)
886
+ {
887
+ return rb_hash_delete_if(fdbm_to_hash(obj));
888
+ }
889
+
890
+ /*
891
+ * Documented by mathew meta@pobox.com.
892
+ * = Introduction
893
+ *
894
+ * The DBM class provides a wrapper to a Unix-style
895
+ * {dbm}[http://en.wikipedia.org/wiki/Dbm] or Database Manager library.
896
+ *
897
+ * Dbm databases do not have tables or columns; they are simple key-value
898
+ * data stores, like a Ruby Hash except not resident in RAM. Keys and values
899
+ * must be strings.
900
+ *
901
+ * The exact library used depends on how Ruby was compiled. It could be any
902
+ * of the following:
903
+ *
904
+ * - The original ndbm library is released in 4.3BSD.
905
+ * It is based on dbm library in Unix Version 7 but has different API to
906
+ * support multiple databases in a process.
907
+ * - {Berkeley DB}[http://en.wikipedia.org/wiki/Berkeley_DB] versions
908
+ * 1 thru 5, also known as BDB and Sleepycat DB, now owned by Oracle
909
+ * Corporation.
910
+ * - Berkeley DB 1.x, still found in FreeBSD and OpenBSD.
911
+ * - {gdbm}[http://www.gnu.org/software/gdbm/], the GNU implementation of dbm.
912
+ * - {qdbm}[http://fallabs.com/qdbm/index.html], another open source
913
+ * reimplementation of dbm.
914
+ *
915
+ * All of these dbm implementations have their own Ruby interfaces
916
+ * available, which provide richer (but varying) APIs.
917
+ *
918
+ * = Cautions
919
+ *
920
+ * Before you decide to use DBM, there are some issues you should consider:
921
+ *
922
+ * - Each implementation of dbm has its own file format. Generally, dbm
923
+ * libraries will not read each other's files. This makes dbm files
924
+ * a bad choice for data exchange.
925
+ *
926
+ * - Even running the same OS and the same dbm implementation, the database
927
+ * file format may depend on the CPU architecture. For example, files may
928
+ * not be portable between PowerPC and 386, or between 32 and 64 bit Linux.
929
+ *
930
+ * - Different versions of Berkeley DB use different file formats. A change to
931
+ * the OS may therefore break DBM access to existing files.
932
+ *
933
+ * - Data size limits vary between implementations. Original Berkeley DB was
934
+ * limited to 2GB of data. Dbm libraries also sometimes limit the total
935
+ * size of a key/value pair, and the total size of all the keys that hash
936
+ * to the same value. These limits can be as little as 512 bytes. That said,
937
+ * gdbm and recent versions of Berkeley DB do away with these limits.
938
+ *
939
+ * Given the above cautions, DBM is not a good choice for long term storage of
940
+ * important data. It is probably best used as a fast and easy alternative
941
+ * to a Hash for processing large amounts of data.
942
+ *
943
+ * = Example
944
+ *
945
+ * require 'dbm'
946
+ * db = DBM.open('rfcs', 666, DBM::CREATRW)
947
+ * db['822'] = 'Standard for the Format of ARPA Internet Text Messages'
948
+ * db['1123'] = 'Requirements for Internet Hosts - Application and Support'
949
+ * db['3068'] = 'An Anycast Prefix for 6to4 Relay Routers'
950
+ * puts db['822']
951
+ */
952
+ void
953
+ Init_dbm(void)
954
+ {
955
+ rb_cDBM = rb_define_class("DBM", rb_cObject);
956
+ /* Document-class: DBMError
957
+ * Exception class used to return errors from the dbm library.
958
+ */
959
+ rb_eDBMError = rb_define_class("DBMError", rb_eStandardError);
960
+ rb_include_module(rb_cDBM, rb_mEnumerable);
961
+
962
+ rb_define_alloc_func(rb_cDBM, fdbm_alloc);
963
+ rb_define_singleton_method(rb_cDBM, "open", fdbm_s_open, -1);
964
+
965
+ rb_define_method(rb_cDBM, "initialize", fdbm_initialize, -1);
966
+ rb_define_method(rb_cDBM, "close", fdbm_close, 0);
967
+ rb_define_method(rb_cDBM, "closed?", fdbm_closed, 0);
968
+ rb_define_method(rb_cDBM, "[]", fdbm_aref, 1);
969
+ rb_define_method(rb_cDBM, "fetch", fdbm_fetch_m, -1);
970
+ rb_define_method(rb_cDBM, "[]=", fdbm_store, 2);
971
+ rb_define_method(rb_cDBM, "store", fdbm_store, 2);
972
+ rb_define_method(rb_cDBM, "index", fdbm_index, 1);
973
+ rb_define_method(rb_cDBM, "key", fdbm_key, 1);
974
+ rb_define_method(rb_cDBM, "select", fdbm_select, 0);
975
+ rb_define_method(rb_cDBM, "values_at", fdbm_values_at, -1);
976
+ rb_define_method(rb_cDBM, "length", fdbm_length, 0);
977
+ rb_define_method(rb_cDBM, "size", fdbm_length, 0);
978
+ rb_define_method(rb_cDBM, "empty?", fdbm_empty_p, 0);
979
+ rb_define_method(rb_cDBM, "each", fdbm_each_pair, 0);
980
+ rb_define_method(rb_cDBM, "each_value", fdbm_each_value, 0);
981
+ rb_define_method(rb_cDBM, "each_key", fdbm_each_key, 0);
982
+ rb_define_method(rb_cDBM, "each_pair", fdbm_each_pair, 0);
983
+ rb_define_method(rb_cDBM, "keys", fdbm_keys, 0);
984
+ rb_define_method(rb_cDBM, "values", fdbm_values, 0);
985
+ rb_define_method(rb_cDBM, "shift", fdbm_shift, 0);
986
+ rb_define_method(rb_cDBM, "delete", fdbm_delete, 1);
987
+ rb_define_method(rb_cDBM, "delete_if", fdbm_delete_if, 0);
988
+ rb_define_method(rb_cDBM, "reject!", fdbm_delete_if, 0);
989
+ rb_define_method(rb_cDBM, "reject", fdbm_reject, 0);
990
+ rb_define_method(rb_cDBM, "clear", fdbm_clear, 0);
991
+ rb_define_method(rb_cDBM,"invert", fdbm_invert, 0);
992
+ rb_define_method(rb_cDBM,"update", fdbm_update, 1);
993
+ rb_define_method(rb_cDBM,"replace", fdbm_replace, 1);
994
+
995
+ rb_define_method(rb_cDBM, "include?", fdbm_has_key, 1);
996
+ rb_define_method(rb_cDBM, "has_key?", fdbm_has_key, 1);
997
+ rb_define_method(rb_cDBM, "member?", fdbm_has_key, 1);
998
+ rb_define_method(rb_cDBM, "has_value?", fdbm_has_value, 1);
999
+ rb_define_method(rb_cDBM, "key?", fdbm_has_key, 1);
1000
+ rb_define_method(rb_cDBM, "value?", fdbm_has_value, 1);
1001
+
1002
+ rb_define_method(rb_cDBM, "to_a", fdbm_to_a, 0);
1003
+ rb_define_method(rb_cDBM, "to_hash", fdbm_to_hash, 0);
1004
+
1005
+ /* Indicates that dbm_open() should open the database in read-only mode */
1006
+ rb_define_const(rb_cDBM, "READER", INT2FIX(O_RDONLY|RUBY_DBM_RW_BIT));
1007
+
1008
+ /* Indicates that dbm_open() should open the database in read/write mode */
1009
+ rb_define_const(rb_cDBM, "WRITER", INT2FIX(O_RDWR|RUBY_DBM_RW_BIT));
1010
+
1011
+ /* Indicates that dbm_open() should open the database in read/write mode,
1012
+ * and create it if it does not already exist
1013
+ */
1014
+ rb_define_const(rb_cDBM, "WRCREAT", INT2FIX(O_RDWR|O_CREAT|RUBY_DBM_RW_BIT));
1015
+
1016
+ /* Indicates that dbm_open() should open the database in read/write mode,
1017
+ * create it if it does not already exist, and delete all contents if it
1018
+ * does already exist.
1019
+ */
1020
+ rb_define_const(rb_cDBM, "NEWDB", INT2FIX(O_RDWR|O_CREAT|O_TRUNC|RUBY_DBM_RW_BIT));
1021
+
1022
+ #if defined(HAVE_DB_VERSION)
1023
+ /* The version of the dbm library, if using Berkeley DB */
1024
+ rb_define_const(rb_cDBM, "VERSION", rb_str_new2(db_version(NULL, NULL, NULL)));
1025
+ #elif defined(HAVE_GDBM_VERSION)
1026
+ /* since gdbm 1.9 */
1027
+ rb_define_const(rb_cDBM, "VERSION", rb_str_new2(gdbm_version));
1028
+ #elif defined(HAVE_LIBVAR_GDBM_VERSION)
1029
+ /* ndbm.h doesn't declare gdbm_version until gdbm 1.8.3.
1030
+ * See extconf.rb for more information. */
1031
+ {
1032
+ RUBY_EXTERN char *gdbm_version;
1033
+ rb_define_const(rb_cDBM, "VERSION", rb_str_new2(gdbm_version));
1034
+ }
1035
+ #elif defined(HAVE_DPVERSION)
1036
+ rb_define_const(rb_cDBM, "VERSION", rb_sprintf("QDBM %s", dpversion));
1037
+ #elif defined(_DB_H_)
1038
+ rb_define_const(rb_cDBM, "VERSION", rb_str_new2("Berkeley DB (unknown)"));
1039
+ #else
1040
+ rb_define_const(rb_cDBM, "VERSION", rb_str_new2("unknown"));
1041
+ #endif
1042
+ }