Macのrubyのreadlineに不具合がある!?(6)
私のパッチを ruby-dev に報告したあと、以下のことが ruby-dev([ruby-dev:35532]、[ruby-dev:35534]) で挙がった。
- libedit の動作(history_base - 1)がバグなのか。バグなら将来のバージョンで修正されるのでは?
- libedit において、 history_get や remove_history が history_base - 1 で正しく動作するのか?
ということで、早速、GNU Readline と libedit の動作を確認した。
以下のコードを作成し、GNU Readline、 Mac OSXの libedit、最新の libedit で動作を確認した。
#include <stdio.h> #ifdef HAVE_EDITLINE_READLINE_H #include <editline/readline.h> #else #include <readline/readline.h> #include <readline/history.h> #endif /* HAVE_EDITLINE_READLINE_H */ int main(int argc, char **argv) { HIST_ENTRY *entry; int i; using_history(); printf("history_base: %d\n", history_base); add_history("1"); add_history("2"); add_history("3"); printf("added 1, 2, 3 to history\n"); printf("history_base: %d\n", history_base); printf("history_length: %d\n", history_length); entry = history_get(history_base); printf("history_get(history_base): %s\n", entry->line); for (i = 0; i <= history_length; i++) { entry = history_get(i); printf("history_get(%d): %s\n", i, entry ? entry->line : "NULL"); } return 0; }
以下、実行結果。
---- no212_on-macosx.gnu_readline ---- history_base: 1 added 1, 2, 3 to history history_base: 1 history_length: 3 history_get(history_base): 1 history_get(0): NULL history_get(1): 1 history_get(2): 2 history_get(3): 3 ---- no212_on-macosx.editline ---- history_base: 1 added 1, 2, 3 to history history_base: 1 history_length: 3 history_get(history_base): 2 history_get(0): 1 history_get(1): 2 history_get(2): 3 history_get(3): NULL ---- no212_on-macosx.editline_20080712-2.11 ---- history_base: 1 added 1, 2, 3 to history history_base: 1 history_length: 3 history_get(history_base): 2 history_get(0): 1 history_get(1): 2 history_get(2): 3 history_get(3): NULL
上記から次のことが分かった。
- GNU Readline と libedit の history_base の値は 1 であるため等しい。
- GNU Readline はヒストリのインデックスが 1 から始まる。history_base が 1 なので history_base から始まるってことなのかも。
- libedit は 0 からヒストリのインデックスが 0 から始まる。history_base とは関係ない。というか GNU Readline と非互換だ。
- 1番目のヒストリを取得するには GNU Readline は 1、libedit は 0 を指定する必要がある。
ちなみに history_base のコメントには 「probably never subject to change」とあるので、history_base は定数ではないのですが、 1 が固定で指定されていることを想定しても良いと考えています。
libedit になんでこんな非互換が入っているのか不明ですが、回避する必要がありそうです。また、今更 libedit の挙動は変わらないような気がします。
ということで、Ruby の Readline モジュール(というか Readline::HISTORY) は以下の方針で修正してみます。
- Readlineのコンパイル時には特別なことはしない。
- Readlineの初期化時に GNU Readline とリンクしていると判断した場合、history_offset を history_base に設定する。そうでない場合、history_offset を 0 に設定する。
- ヒストリの取得や削除などのメソッドでは、 history_offset を考慮する。
パッチはあとで。