ふと、GDBM を read only で access したくなったのですが、GDBM.open には
gdbm_open の read_write 引数に GDBM_READER を渡す方法がないことに気が
つきました。

GDBM.open は常にとりあえず書き込み可能な open を試してみて、それが失敗
したら読み込みのみの open を試すようです。

しかし、application が read only と分かっている時に write mode での
open を行うと、なんかのひょうしにファイルを壊してしまうのが恐いので、
次のように GDBM::READER などを指定した場合には指定した通りの
read_write 引数を渡すようにするのはどうでしょうか。

ただし、GDBM_READER は 0 であり、そのままだと指定されたかどうか分から
ないため、今までの指定しなかった時の挙動と区別するために 0x20000000 を
加えてあります。

Index: ext/gdbm/gdbm.c
===================================================================
RCS file: /src/ruby/ext/gdbm/gdbm.c,v
retrieving revision 1.27
diff -u -r1.27 gdbm.c
--- ext/gdbm/gdbm.c	6 May 2003 06:51:30 -0000	1.27
+++ ext/gdbm/gdbm.c	15 Apr 2004 06:38:39 -0000
@@ -16,6 +16,8 @@
 
 static VALUE rb_cGDBM, rb_eGDBMError, rb_eGDBMFatalError;
 
+#define RUBY_GDBM_RW_BIT 0x20000000
+
 #define MY_BLOCK_SIZE (2048)
 #define MY_FATAL_FUNC rb_gdbm_fatal
 static void
@@ -100,16 +102,23 @@
 
     SafeStringValue(file);
 
-    dbm = 0;
-    if (mode >= 0)
-	dbm = gdbm_open(RSTRING(file)->ptr, MY_BLOCK_SIZE, 
-			GDBM_WRCREAT|flags, mode, MY_FATAL_FUNC);
-    if (!dbm)
+    if (flags & RUBY_GDBM_RW_BIT) {
+        flags &= ~RUBY_GDBM_RW_BIT;
 	dbm = gdbm_open(RSTRING(file)->ptr, MY_BLOCK_SIZE, 
-			GDBM_WRITER|flags, 0, MY_FATAL_FUNC);
-    if (!dbm)
-	dbm = gdbm_open(RSTRING(file)->ptr, MY_BLOCK_SIZE, 
-			GDBM_READER|flags, 0, MY_FATAL_FUNC);
+			flags, mode, MY_FATAL_FUNC);
+    }
+    else {
+        dbm = 0;
+        if (mode >= 0)
+            dbm = gdbm_open(RSTRING(file)->ptr, MY_BLOCK_SIZE, 
+                            GDBM_WRCREAT|flags, mode, MY_FATAL_FUNC);
+        if (!dbm)
+            dbm = gdbm_open(RSTRING(file)->ptr, MY_BLOCK_SIZE, 
+                            GDBM_WRITER|flags, 0, MY_FATAL_FUNC);
+        if (!dbm)
+            dbm = gdbm_open(RSTRING(file)->ptr, MY_BLOCK_SIZE, 
+                            GDBM_READER|flags, 0, MY_FATAL_FUNC);
+    }
 
     if (!dbm) {
 	if (mode == -1) return Qnil;
@@ -992,13 +1001,12 @@
     rb_define_method(rb_cGDBM, "to_a", fgdbm_to_a, 0);
     rb_define_method(rb_cGDBM, "to_hash", fgdbm_to_hash, 0);
 
-    /* flags for gdbm_opn() */
-    /*
-    rb_define_const(rb_cGDBM, "READER",  INT2FIX(GDBM_READER));
-    rb_define_const(rb_cGDBM, "WRITER",  INT2FIX(GDBM_WRITER));
-    rb_define_const(rb_cGDBM, "WRCREAT", INT2FIX(GDBM_WRCREAT));
-    rb_define_const(rb_cGDBM, "NEWDB",   INT2FIX(GDBM_NEWDB));
-    */
+    /* flags for gdbm_open() */
+    rb_define_const(rb_cGDBM, "READER",  INT2FIX(GDBM_READER|RUBY_GDBM_RW_BIT));
+    rb_define_const(rb_cGDBM, "WRITER",  INT2FIX(GDBM_WRITER|RUBY_GDBM_RW_BIT));
+    rb_define_const(rb_cGDBM, "WRCREAT", INT2FIX(GDBM_WRCREAT|RUBY_GDBM_RW_BIT));
+    rb_define_const(rb_cGDBM, "NEWDB",   INT2FIX(GDBM_NEWDB|RUBY_GDBM_RW_BIT));
+
     rb_define_const(rb_cGDBM, "FAST", INT2FIX(GDBM_FAST));
     /* this flag is obsolete in gdbm 1.8.
        On gdbm 1.8, fast mode is default behavior. */

ちなみに、gdbm には freeze も効きません。

% ruby -rgdbm -e 'GDBM.open("zz.dbm", 0644) {|dbm| dbm.freeze; dbm["a"] = "a"; p dbm["a"]}'  
"a"
-- 
[田中 哲][たなか あきら][Tanaka Akira]