I know I believe in nothing but it is my sweet nothing.:2007年08月30日分

2007/08/30(Thu)

[NetBSD] wc: filename: invalid byte sequence

tech-userlevelネタ
wizd(8)氏の添付したmp3ファイル(のようなもの)じゃde_DE.UTF-8ロケールでも再現しないねぇ。

ソース。

143		r = mbrtowc(wc, p, mblen, st);
144		if (r == (size_t)-1) {
145			warnx("%s: invalid byte sequence", file);
146			rval = 1;
147
148			/* XXX skip 1 byte */
149			mblen--;
150			p++;
151			memset(st, 0, sizeof(*st));
152			continue;
153		} else if (r == (size_t)-2)
154			break;
155		else if (r == 0)
156			r = 1;

そもそも*.mp3のよなバイナリをUTF-8だとしてmbrtowc(3)に喰わせれば
EILSEQになる可能性は高い、wc -cを使いましょうってことで。

↑の問題とは関係ないんだけど、今の実装だとmbrtowc(3)の使い方がちとマズイ。
注目は155~156行目、mbrtowc(3)の 仕様書には「戻り値として0が返ってきた場合
変換結果はnullワイド文字になる」と書いてある。

RETURN
    0
        If the next n or fewer bytes complete the character that corresponds
        to the null wide character (which is the value stored).


なので

のはとても正しいように思えるんだけど、実際には

もいっこバグ見つけた。

206		while ((len = read(fd, buf, MAXBSIZE)) > 0) {
207			if (dochar) {
208				size_t wlen;
209
210				r = do_mb(0, (char *)buf, (size_t)len,
211				    &st, &wlen, name);

テキストファイルからread(2)で読み込んだバイト列に'\0'つかない時
iconv(1)のISO-2022-JP問題と同様の問題が発生する悪寒、ひー。

さてどうやって修正しますかねぇ…
Citrus API直接呼ぶのもアレなので

285	if (dochar && r == (size_t)-2) {
286		warnx("%s: incomplete multibyte character", name);

285	if (dochar && r == (size_t)-2 && !mbsinit(&st)) {
286		warnx("%s: incomplete multibyte character", name);

とかしてmbsinit(3)によるチェックくらいでお茶を濁す?

ここまでの 暫定patch

まあテキストファイルであれば通常末尾にnewline(0xa)があることが多いんで
これを意図的に除去しない限りは

$ LC_ALL=ja_JP.ISO2022-JP wc -m hoge.txt
wc: hoge.txt: incomplete multibyte character

が発生することはないよなぁ、発生頻度は低そうなのでしばらく放置するかね。

*1:まあ問題になるのはVIQRとかzWなんかの7bit stateful encodingくらいだけど。
*2:なので'\0' != L'\0'でないUTF-16とかUTF-32のようなCESは扱うことができない。

RSS

壊れてたみたい、失礼しました。