Ruby-1.8.7_p160 iconv編 (3 failures, 1 errors)

最初はテストは省いてどんどんビルドして行こうと考えていたのだが、忘れてしまうのがおちなので、修正が必要な所は最初に対応することにした。

dbm, gdbmのエラーを除去した後にrunner.rbを実行すると、以下のように失敗3、エラー1となった。

  1) Failure:
test_s_open_create_new(TestGDBM) [./gdbm/test_gdbm.rb:87]:
<420> expected but was
<438>.

  2) Error:
test_translit_option(TestIconv::Option):
Iconv::InvalidEncoding: invalid encoding ("SHIFT_JIS//TRANSLIT", "EUC-JP//translit//ignore")
    ./iconv/test_option.rb:25:in `initialize'
    ./iconv/test_option.rb:25:in `new'
    ./iconv/test_option.rb:25:in `test_translit_option'

  3) Failure:
test_client_auth(OpenSSL::TestSSL)
    [./openssl/test_ssl.rb:216:in `test_client_auth'
     ./openssl/test_ssl.rb:129:in `call'
     ./openssl/test_ssl.rb:129:in `start_server'
     ./openssl/test_ssl.rb:215:in `test_client_auth']:
<OpenSSL::SSL::SSLError> exception expected but was
Class: <Errno::ECONNRESET>
Message: <"\212\371\221\266\202\314\220\332\221\261\202\315\203\212\203\202\201[\203g \203z\203X\203g\202\311\213\255\220\247\22
3I\202\311\220\330\222f\202\263\202\352\202\334\202\265\202\275\201B - SSL_connect">
---Backtrace---
./openssl/test_ssl.rb:219:in `connect'
./openssl/test_ssl.rb:219:in `test_client_auth'
./openssl/test_ssl.rb:216:in `test_client_auth'
./openssl/test_ssl.rb:129:in `call'
./openssl/test_ssl.rb:129:in `start_server'
./openssl/test_ssl.rb:215:in `test_client_auth'
---------------

  4) Failure:
test_system(TestSystem) [./ruby/test_system.rb:15]:
<"foobar\n"> expected but was
<"Setting environment for using Microsoft Visual Studio 2005 x86 tools.\nfoobar\n">.

2031 tests, 1346347 assertions, 3 failures, 1 errors

1)は前回言及したようにWindows固有の問題らしいので省く。以降、順に見ていこう。

  • iconv

2)は拡張ライブラリィiconvの初期化で発生しているようだ。iconvでは初期化時に変換後と前のエンコーディング名を指定するが、エラーになったテストコードを見ると初期化時に指定しているIANAエンコーディング名の後に何かサフィックスが付いているのが分かる。

iconv = Iconv.new('SHIFT_JIS//TRANSLIT', 'EUC-JP//translit//ignore')

これはなんだろうと思い同様にiconvを利用しているPHP等でも調べてみると、iconvにおける仕様らしく、
libiconv - GNU Project - Free Software Foundation (FSF)
php iconv - Manual

文字列 //TRANSLIT を out_charset に追加すると、翻字機能が有効になります。これは、指定された文字集合で 表せない文字を、見た目の似ている別の文字に置き換える機能です。 文字列 //IGNORE を追加すると、指定された文字集合で 表せない文字は黙って切り捨てられます。

ということらしい。
私はGNU libiconvをビルドして使用しているが、この実装を使っても"//TRANSLIT"、 "//IGNORE"を単独で使うには問題はないものの、二つを合わせて指定した"//TRANSLIT//IGNORE"のような形式ではやはりiconvは使用できなかった。

require 'iconv'
=> true
Iconv.iconv('SHIFT_JIS//TRANSLIT', 'EUC-JP//translit')
=> 
Iconv.iconv('SHIFT_JIS//TRANSLIT', 'EUC-JP//ignore')
=> 
Iconv.iconv('SHIFT_JIS//TRANSLIT', 'EUC-JP//translit//ignore')
Iconv::InvalidEncoding: invalid encoding ("SHIFT_JIS//TRANSLIT", "EUC-JP//translit//ignore")
	from (irb):5:in `iconv'
	from (irb):5
	from :0

iconvのドキュメントを見てみたが、それぞれは指定できると書いているものの重複して指定した場合のことは書いていない。
iconv_open
ならばとiconv.cのソースコードを見てみると、どうやら両方を指定する場合は検出できる順番が決まってるぽい。

iconv_t iconv_open (const char* tocode, const char* fromcode)
{
  struct conv_struct * cd;
  char buf[MAX_WORD_LENGTH+10+1];
  const char* cp;
  char* bp;
  :
  :
  for (to_wchar = 0;;) {
    /* Search tocode in the table. */
    :
    :
    if (bp-buf >= 10 && memcmp(bp-10,"//TRANSLIT",10)==0) {
      bp -= 10;
      *bp = '\0';
      transliterate = 1;
    }
    if (bp-buf >= 8 && memcmp(bp-8,"//IGNORE",8)==0) {
      bp -= 8;
      *bp = '\0';
      discard_ilseq = 1;
    }

これでは先に"//IGNORE"が見つかった場合はその後の"//TRANSLIT"を検出できない。試しに順序を変えてテストしてみた。

require 'iconv'
=> true
Iconv.iconv('SHIFT_JIS//TRANSLIT', 'EUC-JP//translit//ignore')
Iconv::InvalidEncoding: invalid encoding ("SHIFT_JIS//TRANSLIT", "EUC-JP//translit//ignore")
	from (irb):2:in `iconv'
	from (irb):2
Iconv.iconv('SHIFT_JIS//TRANSLIT', 'EUC-JP//ignore//translit')
=> []

うーん、やはりそうみたいだ。重複して指定する場合は"//IGNORE" "//TRANSLIT"の順で指定しないと"InvalidEncoding"になってしまう。
ベタではあるが、以下のように修正してビルドするとテストは通るようにできるが、これが正しい実装なのかどうか私には判断できない。

  • iconv.c.diff
@@ -241,6 +241,11 @@
       bp -= 8;
       *bp = '\0';
       discard_ilseq = 1;
+      if (bp-buf >= 10 && memcmp(bp-10,"//TRANSLIT",10)==0) {
+        bp -= 10;
+        *bp = '\0';
+        transliterate = 1;
+      }
     }
     if (buf[0] == '\0') {
       tocode = locale_charset();
@@ -310,6 +315,10 @@
     if (bp-buf >= 8 && memcmp(bp-8,"//IGNORE",8)==0) {
       bp -= 8;
       *bp = '\0';
+      if (bp-buf >= 10 && memcmp(bp-10,"//TRANSLIT",10)==0) {
+        bp -= 10;
+        *bp = '\0';
+      }
     }
     if (buf[0] == '\0') {
       fromcode = locale_charset();
  • ruby "iconv\test_option.rb"
E:\ruby-1.8.7-p160\test\iconv>ruby test_option.rb
Loaded suite test_option
Started
...
Finished in 0.001 seconds.

3 tests, 4 assertions, 0 failures, 0 errors

ちなみに、ruby-dev MLの神戸さんの投稿によると、この二つの拡張文字列はiconvにおける(GNUの)独自拡張仕様らしく使わないことを推奨している。
Nabble - ruby-dev jp - [ruby-dev36147] GNU iconv dependency