2010/03/01(Mon)
○[NetBSD] libedit I18N への道(その5)
昨日の補足だけ。
どうにも春の頭痛胃痛ダブル祭りが絶賛開催中なもんでして…ちうことで
バッファリングとかするとバグらせる悪寒、俺に必要なのはバッファリンに違いない。
なんで余計なことは libc にお任せの高水準API使って書き直しちゃるぞーということで。
今回のケースでは元々動作が怪しいので尊重すべきは
ワイド文字対応コードが入る前のソースであるからして、すべてバックアウトした状態
つまり
rev1.30をベースに作業することにしますか。
その1、vi(1)に渡す一時ファイルを作成する部分のオリジナル。
1013 fd = mkstemp(tempfile);
1014 if (fd < 0)
1015 return CC_ERROR;
1016 cp = el->el_line.buffer;
1017 write(fd, cp, (size_t)(el->el_line.lastchar - cp));
1018 write(fd, "\n", 1);
1019 pid = fork();
説明の必要ありませんね?
書き直したコード。
1016 fd = mkstemp(tempfile);
1017 if (fd < 0)
1018 return CC_ERROR;
1019 fp = fdopen(fd, "w+");
1020 if (fp == NULL) {
1021 close(fd);
1022 return CC_ERROR;
1023 }
1024 for (cp = el->el_line.buffer; cp < el->el_line.lastchar; ++cp) {
1025 fputwc(*cp, fp);
1026 if (ferror(fp))
1027 goto fatal;
1028 }
1029 fputwc(L'\n', fp);
1030 if (ferror(fp))
1031 goto fatal;
1032 fflush(fp);
1033 pid = fork();
1034 switch (pid) {
1035 case -1:
1036 goto fatal;
1037 case 0:
1038 fclose(fp);
1039 execlp("vi", "vi", tempfile, (char *)NULL);
1040 exit(0);
1041 /*NOTREACHED*/
- 1019行目: fdopen(3)を使うことでmkstemp(3)で開いた一時ファイルを FILE と結び付けます
- 1024~1028〃: el_line の持つワイド文字列バッファの先頭から改行部まで、fputwc(3)を使用して
この一時ファイルのストリームに書き込みします。 - 1029~1031〃: 改行コードを以下略
- fork(2)する前にストリーム中のバッファをfflush(3)で強制的に書き込みします。
これを忘れると、fork(2)によって親から子プロセスに FILE とその内部のバッファももコピーされるので
1038行目、子プロセスがfclose(3)をすることで書き込みの重複が発生することに注意。
という感じ。
その2、vi(1)が編集した一時ファイルを読み込む部分のオリジナル。
1031 while (waitpid(pid, &status, 0) != pid)
1032 continue;
1033 lseek(fd, (off_t)0, SEEK_SET);
1034 st = read(fd, cp, (size_t)(el->el_line.limit - cp));
1035 if (st > 0 && cp[st - 1] == '\n')
1036 st--;
1037 el->el_line.cursor = cp;
1038 el->el_line.lastchar = cp + st;
1039 break;
説明の(ry
書き直し(ry
1043 while (waitpid(pid, &status, 0) != pid)
1044 continue;
1045 unlink(tempfile);
1046 rewind(fp);
1047 for (cp = el->el_line.buffer; cp < el->el_line.limit; ++cp) {
1048 ch = fgetwc(fp);
1049 if (ferror(fp))
1050 goto fatal;
1051 if (feof(fp))
1052 break;
1053 *cp = ch;
1054 }
1055 if (cp != el->el_line.buffer && *(cp - 1) == L'\n')
1056 --cp;
1057 el->el_line.cursor = el->el_line.buffer;
1058 el->el_line.lastchar = cp;
1059 break;
- 1047~1054〃: el_line の持つワイド文字列バッファの先頭から末尾まで、fgetwc(3)を使用して
この一時ファイルのストリームから読み込んだワイド文字を書き込みます - 1051〃: ファイルの末尾に達すれば終了
あとは-DWIDECHARなしでbuildする場合のためにchartype.hに
#ifdef WIDECHAR
#define ct_fgetwc fgetwc
#define ct_fputwc fputwc
#else
#define ct_fgetwc fgetc
#define ct_fputwc fputc
#endif
としときましょう。
ただしオリジナルのコードと比較すると -UWIDECHAR の場合には read/write 一発でなく
fgetc/fputc の複数回の呼び出しになるので system call の呼び出し回数は libc が
適切にバッファリングでセーブするとはいえ、ライブラリ関数の呼び出し回数が今度は増えちゃいますやね。
もし性能にシビアな要求がある場合は、vi.c 側で ifndef WIDECHAR の場合は fread/fwrite を
使うことも検討する必要があります、まぁ今回はせいぜい一行(1024文字)までなんでやらんけど。
とりあえずこれでLANG=ja_JP.eucJPで起動したsh(1)でset -o viし、ESC + v押下で
historyをvi(1)上から編集できるのですがvi_histedit()から先でバグってるようで
マルチバイトの場合、実行されるhistoryが不正に切り詰められちゃってる模様。
多分ワイド文字数とバイト数がどっかでごっちゃなんだろうなー。
[C locale の場合]
vmware$ LANG=C sh
vmware$ set -o vi
vmware$ <ESC> v
\xa4\xa2\xa4\xa4\xa4\xa6\xa4\xa8\xa4\xaa
~
~
~
:w!
vmware$
あいうえお: not found
vmware$
[ja_JP.eucJP locale の場合]
vmware$ LANG=ja_JP.eucJP sh
vmware$ set -o vi
vmware$ <ESC> v
あいうえお
~
~
~
:w!
vmware$
あいう: not found
vmware$
現時点での差分は こちら。
次回はこの切り詰め問題を追っかける予定ですがいつもの通り未定 :D