Date: Wed, 7 Nov 2018 00:01:54 +0900 From: =?utf-8?B?5YaF6JekIOelkOS4gOmDjg==?= <naito.yuichiro@gmail.com> To: WATANABE Kazuhiro <CQG00620@nifty.ne.jp> Cc: FreeBSD-users-jp <freebsd-users-jp@freebsd.org> Subject: [FreeBSD-users-jp 96345] Re: =?utf-8?b?RnJlZUJTRC0xMS4yIOOBriBqYV9KUC5ldWNKUCDnkrDlooM=?= Message-ID: <45FE3A88-FE74-4F73-800B-598A18AE5E6D@gmail.com> In-Reply-To: <201811061314.wA6DErDn002299@conssluserg-02.nifty.com> References: <20181014194410.b466d0bbf0e976ffbcab2969@mogami.com> <20181024115101.f6049ef61a82a1fdbab1a404@mogami.com> <201811061314.wA6DErDn002299@conssluserg-02.nifty.com>
next in thread | previous in thread | raw e-mail | index | archive | help
内藤です。
> FreeBSD ワークショップでも話が出た (出ない?) ようですが、
私がワークショップで話した内容は次の通りです。
11.2のソースのlib/libedit/read.c の L350-L378 に以下のコードがあり、
read(2) で1バイトずつ読み込んでは mbrtowc で wchar_t に変換するコードがあります。
switch (ct_mbrtowc(cp, cbuf, cbp)) {
case (size_t)-1:
if (cbp > 1) {
/*
* Invalid sequence, discard all bytes
* except the last one.
*/
cbuf[0] = cbuf[cbp - 1];
cbp = 0;
break;
} else {
/* Invalid byte, discard it. */
cbp = 0;
goto again;
}
case (size_t)-2:
/*
* We don't support other multibyte charsets.
* The second condition shouldn't happen
* and is here merely for additional safety.
*/
if ((el->el_flags & CHARSET_IS_UTF8) == 0 ||
cbp >= MB_LEN_MAX) {
errno = EILSEQ;
*cp = L'\0';
return -1;
}
/* Incomplete sequence, read another byte. */
goto again;
mbrtowc()ではlocaleに従いバイト列を wchar_t に変換するわけですが、
eucJP や UTF-8 では当然1バイト読んだだけでは変換できません。
2バイト目や3バイト目が必要なため、mbrtowc()は -2 を返し、
続きを読み込んでくれと返しますが、呼び出し元では
el_flags に CHARSET_IS_UTF8 のフラグが立っていないとリトライせずに
エラー終了します。
libedit のこの関数(read_char)がエラー終了すると /bin/sh は標準入力が
閉じられたのと同じ動作をして終了してしまいます。
このCHARSET_IS_UTF8 がどこで立てられているのかと言うと、
lib/libedit/el.c:L97-105 の部分なのですが、
/*
* Initialize all the modules. Order is important!!!
*/
el->el_flags = 0;
if (setlocale(LC_CTYPE, NULL) != NULL){
if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)
el->el_flags |= CHARSET_IS_UTF8;
}
この処理は /bin/sh の起動直後で ~/.profile が読み込まれる前に実行されます。
そのため、/bin/sh の起動時に locale が UTF-8 だと UTF-8 は扱えますが、
ログインシェルのように /bin/sh の起動時の locale が C だと UTF-8 が通りません。
試しに /etc/login.conf で locale を UTF-8 にすると /bin/sh の起動時に locale が
設定されるので、UTF-8が通るようになります。
また、見ての通り、locale が eucJP の場合も CHARSET_IS_UTF8 が立つことは
ありません。
ここで、locale を一切無視してに常に CHARSET_IS_UTF8 を立てるようにすると、
とりあえず eucJP も UTF-8 も通りましたが、eucJP の場合でヒストリに
ゴミが入る問題が手元では発生しましたので、使える状態ににはありませんでした。
と、ここまでがワークショップで話した内容です。
その後なのですが、私個人が eucJP を使いたいとは思っていないこともあり
調査は進んでいません。
UTF-8 を通すだけのパッチならば、先ほどの CHARSET_IS_UTF8 の初期化位置を
直せば良いので簡単なのですが、それでは解決になってませんから。。。
また、この内容では平林さんの /bin/sh の parser.h が問題だという話は
全く無関係に思いますし、渡辺さんの UTF-8 では入力できたという報告とも
矛盾します。
はてさて、真実はどこにといった感じもあり、あんまり進展していません。。。
--
内藤 祐一郎
naito.yuichiro@gmail.com
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?45FE3A88-FE74-4F73-800B-598A18AE5E6D>
