libedit(editline)の闇を垣間見る

[ruby-dev:35551] [Ruby 1.8 - Bug #212] Issues with Readline in Mac OS X

話の流れをまとめると次のような感じ。

  • Mac OSX で Ruby の Readline モジュールのヒストリ機能がなんか変ですよ
  • 高尾さんがパッチを作って下さった
  • なんか色々大丈夫?

それで高尾さんが状況の確認用として作成されたのが次のテストコードというわけです。

#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;
}

この結果は 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

しかし Mac OSX 10.5 (libedit) の場合には次のようになるそうです。

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

というわけで history_get の返り値がずれてしまっています。それで前に GNU Realine をつらつらと眺めていた縁あってちょっと試してみたところ、libedit はやっぱり何かが変。

[ruby-dev:35553] Re: [Ruby 1.8 - Bug #212] Issues with Readline in Mac OS X

Ubuntu 8.04 のlibedit(2.9.cvs.20050518 となってて相当古い)を使ってこのテストコードを動かすと 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

しかしほぼ最新版に相当すると思われる http://www.thrysoee.dk/editline/ からダウンロードした libedit-20080712-2.11.tar.gz を使用すると Mac OSX 10.5 で報告された挙動と同一になります。

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

これにより libedit の 2005 年から現在までの変更点のどこかがおかしいと察せられるわけです。私は 2007/08/12 に修正された

http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libedit/readline.c.diff?r1=1.71&r2=1.72&sortby=date

(NetBSDCSV)の

-	if (history(h, &ev, H_NEXT_EVENT, num))
+	if (history(h, &ev, H_NEXT_EVENT, num + 1))

がマズいんじゃないのかと思うのですが、この変更点のコメントって

patches from Axel Thimm

しか書いてなくって、これが何の意図による変更なのかが全然分からないのですよね。私は NetBSD について全く分からないのでこれ以上は手が出せません(ひょっとしてBSD系の環境ではこれでうまく動くのでしょうか)。この辺について詳しい方はおられませんでしょうか。というかそもそも Mac OSX の libedit がこれを基にしているのかどうかも疑わしいので困ります。

この種の挙動の差異がもしひょっとすると libedit の実装次第だったりバージョン次第だったりする場合には、Ruby の Readline モジュールに下手に修正を加えるわけにはいかなかったりしませんでしょうか。でも修正しないと Mac OSX 10.5 で現実に困ってる人はどうすればいいんだろうとも思うので、とても悩ましいことになります。

ということまでメールに書いて ruby-dev に送ったわけなのですが、M.Suzuki さんが Mac OSX 10.4 でテストコードを試された結果はさらに衝撃的なものに。

[ruby-dev:35557] Re: [Ruby 1.8 - Bug #212] Issues with Readline in Mac OS X

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): NULL
history_get(2): NULL
history_get(3): NULL

なんというあり得ない結果!絶望的ですよね。

このように libedit は深い闇に包まれてしまっている気がするのですが、この辺の挙動の違い、BSD な方々は困られていたりはしないのでしょうか。皆さんも是非とも各種環境かつ各種バージョンの libedit でテストコードを試してみて下さい。なにか分かったら教えて頂けますとスッキリするので何卒よろしくお願い致します。

しかしこういった差異を調べる作業って実に文系的ですよね。文献学をやってる人達の背中をふと思い出しました。