2001-11-10 [長年日記]
λ. 結論から書くと、「結合できない部品が現れるまで最後の文字を確定できない」事の帰結として、変換を終了する際のiconv_closeを呼ぶ前に、iconvをinbufをNULLにして呼び出して、状態を初期状態に戻す必要がある(はず)。
λ. 簡単な場合で考えよう。出力エンコーディングの「文字」は、入力エンコーディングではベースの部品に0個以上の結合部品を連結して表されるとする。そして、ベースの部品はアルファベットで、結合用の部品は記号で表す事にする。
で、iconvが「A ^ : B」という列を受け取ったとする。Bまで読んだ時点で結合できない部品に出会ったので、「A ^ :」で出力エンコーディングの「1文字」が決定できて「:」まで変換出来る。が、その後どうなるかが問題。バッファの中には存在しないけど、実際の「B」の後には結合部品が続いて入力されるかもしれないので、この時点で「B」を変換してしまうことは出来ないのね。では、どうするか… Bは単独でも正しい文字なのでEINVALを返すのは変だし、そもそもEINVALで突っ返したりすると、「B」の直後で変換が終了する場合に、どうやっても「B」を出力エンコーディングの1文字に変換できない。だから「B」を受理してiconv_tの「状態」を遷移すべき。(幸いにしてiconv_tは明示的に初期化/破棄されるので、ポインタを含む任意の情報を持たせることが出来る) そして、iconvがinbufをNULLにして呼ばれて、状態を初期状態に戻す際に、やっと「B」を1文字と確定できて出力できる。
λ. このinbufをNULLにしてのiconvの呼び出しって、glibcのinfoにもnecessaryと書かれているし、もともと必須なものだと思うけど、これってつい忘れがち…、っていうより実際忘れているソースの方が多く出回っていそうな気がする。I18Nハンドブックの例もこれをしてないし。
あ、I18Nハンドブックってのは「国際化プログラミング - I18N ハンドブック」(ISBN4-320-02904-6 通称「いっちまっえーん」)の事ね。
inbufをNULLにしての呼び出しがバグってるiconvなんかもあったりしますが。
そういえばmgeditというエディタのソースでは、inbufをNULLにしての呼び出しの部分に「Write the reset sequence. We don't realloc 'to' here in order not to hit a bug(?) in glibc-2.1.」と書かれてましたが、これかな…
同じくglibc-2.1で、outlenとかoutbufにでたらめな値を返すので長さ<br>が分からないというバグだったので、たぶんdon't realloc 'to'という<br>のはそれのような気がします。でもreallocしないだけでいいのかな…。<br><br>ruby/iconvではNULLを指定する前に一度空文字列を変換するという方法<br>で回避してますが、今確かめたら2.2.2では直ってるようです。