2011年6月20日月曜日

GNU screen JIS X 0213/UTF-8 拡張パッチの内部実装

説明ではくて単な愚痴ですが、パッチが何故がぐちゃぐちゃなのかを書いておきます。

GNU screen は最近は更新はされていないようですが、既に UTF-8 にも ISO-2022/EUC/SJIS にも対応しているので JIS X 0213 に対応させるのも簡単だろうという軽い気持ちで作業を始めたのですが、色々と大変なことに。


まず screen の内部の文字の持ち方ですが、image[] と呼ばれる文字コードの入ったバイト列と、font[] と呼ばれる文字集合の種類が入ったバイト列で表現されています。そして Unicode の場合には、何とその上位バイトを font[] に下位バイトを image[] に格納することで対応していたのです(←最初の困惑、酷いよー)。

格納領域が2バイトしかない以上、16ビットまでの基本多言語面(BMP)の文字しか扱えません。ところが JIS X 0213 で追加された漢字には 0x20000 以降の拡張漢字面(SIP)が多用されているのです。これではUTF-8への変換が実現できません。image[] に UTF-8 のまま拡張することも試しましが、この image[] 内の格納位置を画面の表示位置にそのまま対応させているようで位置がズレます。(←何という素朴な実装)。

このあたりで既に一から実装しなおしたくなってきていますが、そんな時間も取れなさそうなので突き進みます。実際に取った対応は font[] を char から short へ変更して Unicode は上位2バイトを font[] に格納し、下位1バイトを image[]に格納するように変更。(ユニコードは17面しか規定されていないので当面3バイトあれば足りる)。

そして Unicode と JIS X 0213 の対応表を追加しようとして、次の驚愕が、Unicode との文字コードの変換に、設定ファイルから読み込んだに対応表を頭から順序検索している事実が発覚。2分検索ですらない。日本語の文字とか1万以上あるのに、それを毎回表の頭から見つかるまで比較して変換するという。双方向への変換表が一つで済むのでメモリが少なくて済むというメリットがあるから、こうしたんだろうけど...いくら何でもメモリけちり過ぎ。遅くても実用になってるんだからいいのかな?、本当に? 見なきゃ良かった。(←だんだんやる気が失せてきています。)

しょうがないので変換テーブルを展開して直接引けるくように実装を置き換えました。ついでに JIS と ISO-8859 は最初かメモリ上に展開しておくようにしました。設定ファイルからの読み込みだとプロセスごとにメモリが必要になるので無駄だし。iconv() を使うという手もあったのだけど OS ごとに処理が違ったりするのがいなのと、例によって変換表の違いを吸収するための多対一マッピングを実装したかったのでそれはしないことに。

あとは目的の一つである utfwidth を追加しました。元々は Unicode 通常文字幅に固定で、cjk文字幅に対応させるためのパッチをコミットしている人もいたのですが、個人的に EUC-JIS-2004 とかとの互換文字幅も必要なので新たに実装しなおしました。utfwidth に ja (日本語互換文字幅)を指定した場合には副次効果として JIS X 0213 との変換で最低限必要なもの以外の文字合成が OFF も入れておきました。

他にも出力時に G1/GL のフォントがうまく切り変えられないとか、一部の UTF-8 の文字が変になるとか、色々少しづつパッチを当てて変更しているうちにこうなりました。

主にデバッグ用に加えた変更として
  • encoding コマンドを引き数無しで実行すると現在設定されている encoding を表示する。
  • -E オプションを追加。screen -x で接続する時に -E utf8 とかで端末の文字コードを指定できるように(最優先)。
  • defencoding コマンドの二つ目の引き数として、端末の文字コードのデフォルトも指定できるように(環境変数や -E オプションがない時のみ使用される)。

こんな感じで作ったパッチを全部当てて、しばらく使っていてます。今のところまともに動いてる気がするけど、多分バグはいっぱい残っているし、他にも色々と本質的な問題がありそうな状況です。

0 件のコメント:

コメントを投稿