自作したnewlisp.elの挙動がおかしかった原因が少し分かったのでメモ。
newlispのREPLはデフォルトで行編集ライブラリ(readline)が使えるが、 複数行を入力する場合は[cmd]~[/cmd]タグでS式を囲まなければいけないのが newlispの面倒な仕様。
newlisp.elの newlisp-eval関数はその辺を考慮してプロセスに文字列を 送るようにしているのだけど、なぜか効果が無いようでREPLバッファに 入力が溜まってしまう現象がよく起きていた。
(defun newlisp-eval (str-sexp)
"Eval newlisp s-expression."
(interactive "snewLISP Eval: ")
(let ((proc (newlisp-process)))
(labels ((sendln (str)
(comint-send-string proc (concat str "\n"))))
(cond
((string-match "\n" str-sexp) ; 複数行の場合
(sendln "[cmd]")
(sendln str-sexp)
(sendln "[/cmd]"))
(:else
(sendln str-sexp))))
(newlisp-show-repl t)))
newlispのソースを眺めたり、gdbでちまちまとステップ実行してみて気づいたのは
- 1行目は readline関数
- 1行目が[cmd]タグならば残りは fgets関数
というトリッキー(?)な方法でREPLを回しているらしいこと (newlisp.cのmain関数とexecuteCommandLine関数を参照)。
readlineもfgetsも基本的には標準入力(stdin)から読み込むのだが、 どうもこの辺のバッファリングでコケている気がする。 試しにfgetではなく[cmd]タグ以降もreadline関数で読み込みをしてみると 入力が溜まる問題は解決するみたい。
以下パッチ。
--- newlisp_orig.c 2010-05-19 08:00:00.000000000 +0900
+++ newlisp.c 2010-05-19 09:00:00.000000000 +0900
@@ -993,7 +993,8 @@
void executeCommandLine(char * command, UINT outDevice, STREAM * cmdStream)
{
STREAM stream;
-char buff[MAX_LINE];
+ /* char buff[MAX_LINE]; */
+ char *buff;
int batchMode;
batchMode = (memcmp(command, "[cmd]", 5) == 0);
@@ -1033,7 +1034,11 @@
if(cmdStream != NULL && batchMode)
{
openStrStream(cmdStream, 1024, TRUE);
+#ifdef READLINE
+ while ((buff = readline("")) != NULL)
+#else
while(fgets(buff, MAX_LINE - 1, IOchannel) != NULL)
+#endif
{
if(memcmp(buff, "[/cmd]", 6) == 0)
{
@@ -1048,6 +1053,10 @@
return;
}
writeStreamStr(cmdStream, buff, 0);
+#ifdef READLINE
+ writeStreamStr(cmdStream, "\n", 1);
+ freeMemory(buff);
+#endif
}
closeStrStream(cmdStream);
if(!demonMode) exit(1);
…けれども上記のパッチではサーバモード(-d オプション)が働かなくなって しまうため解決した事にはなってない。困った。
0 件のコメント:
コメントを投稿