I know I believe in nothing but it is my sweet nothing.:2008年07月25日分

2008/07/25(Fri)

昨日

現場の人たちとオサレ系中華料理屋。
お一人様\4500は全て凝った内装の償却に回るようで
出てきた料理は人生の中でワースト3に入るレベルの不味さ。

[NetBSD] i18n fold(1)

new developerのahoka氏からuserlandもがしがしi18nするぜーとメールもらた。
まずfold(1)を 書き直してたぜぃ! レビューおながいちゅーことなんだが
これwcwidth > 1の場合の動作が壊れまくりんぐ。

んなわけで他実装の挙動から。
coreutil-6.9-17.fc8のfold(1)はいまどき珍しいことにi18n化されとらんのな。
fold --cjkwidthとかアレなものが生えてないかwktkした俺の期待を返せwww

Solaris 8での動作。

$ cat test.txt
ハヒフヘホ
$ fold -w 2 < test.txt
ハ
ヒ
フ
ヘ
ホ
$ fold -w 3 < test.txt
ハ
ヒ
フ
ヘ
ホ
$ fold -w 4 < test.txt
ハヒ
フヘ
ホ
$ fold -w 5 < test.txt
ハヒ
フヘ
ホ

一方、ahoka氏のfold(1)。

$ cat test.txt
ハヒフヘホ
$ fold -w 2 < test.txt




ホ
$ fold -w 3 < test.txt
ハ
フ
ホ
$ fold -w 4 < test.txt
ハ
フ
ホ
$ fold -w 5 < test.txt
ハヒ
ヘホ

この出力結果見て

カップスタ~食べたその日か~ら~

と歌いだす香具師はオッサン。

軽くソース流し読み。
getchar(3) -> getwchar(3)への書換しとるが、これじゃ-bオプションだめぢゃん。
getchar(3)しつつmbrtowc(3)使わないと何バイト読んだか計算できないってば。
いきなりオワタ\(^o^)/ ぜんぶかきなおしです*1

そいとfwrite(3)代わりにwprintf(3)使うのはダメ。
理由はformat stringのparseコストで無駄に性能が落ちるのが一点。
それとwprintf(L"%.*ls")ちゅーのはL'\0'まで出力してるわけで
multibyte stateは必ず初期化されてしまう、つまりstateful encodingの場合
initial stateに戻す為のescape sequenceが冗長に出力されてしまうのよね。
まぁwprintf(L"%.*ls\n")のように改行を含んでる部分はすでに
initial stateに戻ってるので問題ないといえなくもないけど
ここはやっぱりパラノイアでいこうぜ。

あとどーでもいいけどfold-m10nって、i18nかm17nの間違いだよな。
まぁcoreでもi17nとかtypoすることあるのでこれはまぁしゃあねぇか。
つか既にjoerg氏が 指摘済み*2だったにゃ。

*1:OpenSolarisのfold.cもひどいね、getwc(=getwchar)しつつ-bオプションの場合は
wctomb(3)でmultibyteに戻してからbyte-size計算しちょる、これstateful encodingだめっす。

*2:what does m10n stand for, 実はかつてDEC社内ではmusterbationの略として(ここはuncyclopediaじゃありません

i18n fold(1) その2

fold(width)の中の

        while ((ch = getwchar()) != WEOF) {
            ...
        }

        size_t bytes, n, len, width;
        mbstate_t st;
        char buf[BUFSIZ], *s;
        wchar_t wc;

        bytes = 0;
        mbrtowc(NULL, NULL, 0, &st);
        while ((n = fread(&buf[0], 1, BUFSIZ, stdin)) > 0) {
                for (s = &buf[0]; n > 0; s += len, n -= len, bytes = 0) {
                        len = mbrtowc(&wc, s, n, &st);
                        if (len == (size_t)-2) {
                                bytes += n;
                                break;
                        } else if (len == (size_t)-1) {
                                abort();
                        } if (wc == L'\0') {
                                while (s[len++] != '\0');
                        }
                        bytes += len;
                        width = wcwidth(wc);
                        ...
                }
        }

ややこしいけど、こんな感じでぐるぐるする必要があるやね。

[NetBSD] i18n fold(1) その3

よく考えたら-bで80byteで折り返しするとゆーのは
input側を80byte数えるのではなくoutput側なのだよな。
ちゅうわけで入力に冗長なescape sequenceが合った場合
そいつはカウントしなくておkなはずだ、うぉぉぉ勘違い。

つまりSolarisのやり方でおk、ahoka氏のfold(1)の場合new_column_withd()で

    char dummy[MB_LEN_MAX];
    size_t n;

    if (!count_bytes) {
        ....
    } else {
        n = wctomb(&dummy[0], wc);
        _DIAGASSERT(n != (size_t)-1);
       col += n;
    }

ってな感じでいけるはず。
まぁでもstateful encodingの場合はL'\n'の直前の
escape sequenceの長さをカウントしなけりゃならんし
もうちょいゴニョゴニョせんとだめだけど。