diary of a madman RSSフィード

2009-03-04 Wed

[][] Debian lennyscreen の cjkwidth パッチが壊れてる件  Debian lenny の screen の cjkwidth パッチが壊れてる件を含むブックマーク

あるいは「Unicode の曖昧な文字幅(ambiguous width)問題 その3」。*1

Debian lennyscreen にはGNU screen bug #16666の cjkwidth パッチがあてられていて*2UTF-8 環境で aptitude とか w3m とかのレイアウトが崩れなくなるぜ……と思ったら全然ダメだった。

「なぜ?」と思って BTS への投稿(Bug#478884)とパッチの中身(40cjk_eastasian.dpatch)をよく見てみたら、、、これ、壊れてるね。ほとんど意味がない。

  1. cjkwidth コマンドを定義しているけど、無意味。
  2. 肝になる utf8_isdouble() 関数glibcwcswidth() を呼んでいるだけ。
    • GNU screen bug #16666 に添付されていた本来のパッチでは、グローバル変数 cjkwidth の値を見て、独自実装のテーブルを使って文字幅を判定していた。
    • 本筋とは関係ないけど、なぜ wcwidth() を使わなかったんだろう?
  3. glibcwcwidth() は、現時点では「East Asian Ambiguous Character Width」に対応していない。
    • 文字幅の定義は /usr/share/i18n/charmaps/UTF-8.gz に記述されているが、この定義は言語によらず固定、つまり en_US でも ja_JP でも同じ定義が使われる。

ということで、このパッチは何も解決してない。

Debian lenny において screen を使って「レイアウトが崩れる!」と気になる方は、etch までと同様に独自ビルド等で何とかすべし。(中途半端にパッチがあたっている分、以前より状況が複雑になってるけど)

なお、GNU Screen の本家 BTS の cjkwidth パッチは取り込まれたようなので、GNU Screen の将来のリリース(4.1.0)では問題なくなるはず。

DebianBTS に投稿しようかと思ったけど、glibc の wcwidth() で何が困るのかをうまく説明できる自信(と英語力)が無いので保留。

[][] glibc の wcwidth() の「曖昧な文字幅」についての動作  glibc の wcwidth() の「曖昧な文字幅」についての動作を含むブックマーク

glibcwcwidth() の動作を自分の手できちんと検証したことがなかったので実験してみた。対象バージョンは Debian lenny に含まれていた 2.7-18。

実験に使ったのは以下のプログラム

#define _XOPEN_SOURCE
#include <stdio.h>
#include <locale.h>
#include <wchar.h>

void print_wcwidth(wchar_t c)
{
  printf("wcwidth('%lc') == %d\n", c, wcwidth(c));
}

int main()
{
  setlocale(LC_CTYPE, "");
  print_wcwidth(0x41);
  print_wcwidth(0x3b1);
  print_wcwidth(0x3042);
  return 0;
}

これをコンパイルして、UTF-8ターミナルで実行してみると、以下のようになった。

 $ LC_CTYPE=ja_JP.UTF-8 ./a.out
wcwidth('A') == 1
wcwidth('α') == 1
wcwidth('あ') == 2

 $ LC_CTYPE=en_US.UTF-8 ./a.out
wcwidth('A') == 1
wcwidth('α') == 1
wcwidth('あ') == 2

 $ LC_CTYPE=ja_JP.EUC-JP ./a.out | nkf -Ew
wcwidth('A') == 1
wcwidth('α') == 2
wcwidth('あ') == 2

ということで、やはり glibcwcwidth() だと ja_JP.UTF-8 の時に哀しいことが起こることが分かった。

次に、UTF-8 charmap (/usr/share/i18n/charmaps/UTF-8.gz) の WIDTH...END WIDTH までを書き換えて ambiguous width な文字の幅を 2 に変更した後、locale-gen コマンドでロケールデータを更新すると、以下のようになった。

 $ LC_CTYPE=ja_JP.UTF-8 ./a.out
wcwidth('A') == 1
wcwidth('α') == 2
wcwidth('あ') == 2

 $ LC_CTYPE=en_US.UTF-8 ./a.out
wcwidth('A') == 1
wcwidth('α') == 2
wcwidth('あ') == 2

ということで、期待どおり CJK な人にはありがたい結果になることが分かった。代わりに en_US.UTF-8 で哀しいことが起こっているが、CJK な人だけが使っているホストなら charmap を書き換えるというのもアリかも。

なお、実際に書き換えて運用するなら dpkg-divert で本来のファイルを退避すべきかな。

 $ sudo dpkg-divert --rename --add /usr/share/i18n/charmaps/UTF-8.gz

(後日追記) glibc の wcwidth の話の続き を書いた。

*1:関連: id:macks:20061001Unicode の曖昧な文字幅問題 その2」

*2http://bugs.debian.org/478884

トラックバック - http://d.hatena.ne.jp/macks/20090304
Connection: close