summaryrefslogtreecommitdiff
path: root/trunk/ext/readline
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/ext/readline')
-rw-r--r--trunk/ext/readline/.cvsignore4
-rw-r--r--trunk/ext/readline/README10
-rw-r--r--trunk/ext/readline/README.ja422
-rw-r--r--trunk/ext/readline/depend1
-rw-r--r--trunk/ext/readline/extconf.rb71
-rw-r--r--trunk/ext/readline/readline.c1299
6 files changed, 1807 insertions, 0 deletions
diff --git a/trunk/ext/readline/.cvsignore b/trunk/ext/readline/.cvsignore
new file mode 100644
index 0000000000..814345ece8
--- /dev/null
+++ b/trunk/ext/readline/.cvsignore
@@ -0,0 +1,4 @@
+Makefile
+mkmf.log
+*.def
+extconf.h
diff --git a/trunk/ext/readline/README b/trunk/ext/readline/README
new file mode 100644
index 0000000000..57c51b5f5d
--- /dev/null
+++ b/trunk/ext/readline/README
@@ -0,0 +1,10 @@
+The Readline module provides interface for GNU Readline.
+This module defines a number of methods to facilitate completion
+and accesses input history from the Ruby interpreter.
+This module supported Edit Line(libedit) too.
+libedit is compatible with GNU Readline.
+
+GNU Readline:: http://www.gnu.org/directory/readline.html
+libedit:: http://www.thrysoee.dk/editline/
+
+See RDoc for Readline module.
diff --git a/trunk/ext/readline/README.ja b/trunk/ext/readline/README.ja
new file mode 100644
index 0000000000..3c6c6f3d33
--- /dev/null
+++ b/trunk/ext/readline/README.ja
@@ -0,0 +1,422 @@
+GNU Readline によるコマンドライン入力インタフェースを提供するモジュール
+です。GNU Readline の互換ライブラリのひとつである Edit Line(libedit) も
+サポートしています。
+
+GNU Readline:: http://www.gnu.org/directory/readline.html
+libedit:: http://www.thrysoee.dk/editline/
+
+Readline.readline を使用してユーザからの入力を取得できます。このとき、
+GNU Readline のように入力の補完やEmacs のようなキー操作などができます。
+
+ require "readline"
+ while buf = Readline.readline("> ", true)
+ p buf
+ end
+
+ユーザが入力した内容を履歴(以下、ヒストリ)として記録することができます。
+定数 Readline::HISTORY を使用してヒストリにアクセスできます。
+
+ require "readline"
+ while buf = Readline.readline("> ", true)
+ p Readline::HISTORY.to_a
+ print("-> ", buf, "\n")
+ end
+
+使用するライブラリにより、いくつかのメソッドで例外 NotImplementedError
+が発生します。
+
+$SAFE が 4 の場合、多くのメソッドで例外 SecurityError が発生します。
+
+== Readline モジュール
+
+=== モジュール関数
+
+readline([prompt, [add_hist]]) -> String | nil
+
+ prompt を出力し、ユーザからのキー入力を待ちます。
+ エンターキーの押下などでユーザが文字列を入力し終えると、
+ 入力した文字列を返します。
+ このとき、add_hist が true であれば、入力した文字列をヒストリに追加します。
+
+ 何も入力していない状態で EOF(UNIX では ^D) を入力するなどで、
+ ユーザからの入力がない場合は nil を返します。
+
+ 次の条件を全て満たす場合、例外 IOError が発生します。
+ 1. 標準入力が tty でない。
+ 2. 標準入力をクローズしている。(isatty(2) の errno が EBADF である。)
+
+ 本メソッドはスレッドに対応しています。
+ 入力待ち状態のときはスレッドコンテキストの切替えが発生します。
+
+ 入力時には行内編集が可能で、vi モードと Emacs モードが用意されています。
+ デフォルトは Emacs モードです。
+
+ 本メソッドには注意事項があります。
+ 入力待ちの状態で ^C すると ruby インタプリタが終了し、端末状態を復帰しません。
+ これを回避するための例を3つ挙げます。
+
+ * ^CによるInterrupt例外を補足して、端末状態を復帰します:
+
+ require "readline"
+
+ stty_save = `stty -g`.chomp
+ begin
+ while buf = Readline.readline
+ p buf
+ end
+ rescue Interrupt
+ system("stty", stty_save)
+ exit
+ end
+ end
+ end
+
+ * INTシグナルを補足して、端末状態を復帰します:
+
+ require "readline"
+
+ stty_save = `stty -g`.chomp
+ trap("INT") { system "stty", stty_save; exit }
+
+ while buf = Readline.readline
+ p buf
+ end
+
+ * 単に ^C を無視する方法もあります:
+
+ require "readline"
+
+ trap("INT", "SIG_IGN")
+
+ while buf = Readline.readline
+ p buf
+ end
+
+ 入力履歴 Readline::HISTORY を使用して、空行や直前の入力と同じ内容は入力
+ 履歴に残さないということもできます。
+
+ require "readline"
+
+ while buf = Readline.readline("> ", true)
+ # p Readline::HISTORY.to_a
+ Readline::HISTORY.pop if /^\s*$/ =~ buf
+
+ begin
+ if Readline::HISTORY[Readline::HISTORY.length-2] == buf
+ Readline::HISTORY.pop
+ end
+ rescue IndexError
+ end
+
+ # p Readline::HISTORY.to_a
+ print "-> ", buf, "\n"
+ end
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+=== クラスメソッド
+
+Readline.input = input
+
+ Readline.readline メソッドで使用する入力用の File オブジェクト input
+ を指定します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.output = output
+
+ Readline.readline メソッドで使用する出力用の File オブジェクト
+ output を指定します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completion_proc = proc
+
+ ユーザからの入力を補完する時の候補を取得する Proc オブジェクト proc を
+ 指定します。proc は、次のものを想定しています。
+ 1. call メソッドを持ちます。
+ call メソッドを持たない場合、例外 ArgumentError が発生します。
+ 2. 引数にユーザからの入力文字列(注1)を取ります。
+ 3. 候補の文字列の配列を返します。
+
+ 注1:「/var/lib /v」の後で補完を行うと、
+ デフォルトでは proc の引数に「/v」が渡されます。
+ このように、ユーザが入力した文字列を
+ Readline.completer_word_break_characters に含まれる文字で区切ったも
+ のを単語とすると、カーソルがある単語の最初の文字から現在のカーソル位
+ 置までの文字列が proc の引数に渡されます。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completion_proc -> proc
+
+ ユーザからの入力を補完する時の候補を取得する Proc オブジェクト proc
+ を取得します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completion_case_fold = bool
+
+ ユーザの入力を補完する際、大文字と小文字を区別する/しないを指定します。
+ bool が真ならば区別しません。bool が偽ならば区別します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completion_case_fold -> bool
+
+ ユーザの入力を補完する際、大文字と小文字を区別する/しないを取得します。
+ bool が真ならば区別しません。bool が偽ならば区別します。
+
+ なお、Readline.completion_case_fold= メソッドで指定したオブジェクトを
+ そのまま取得するので、次のような動作をします。
+
+ require "readline"
+
+ Readline.completion_case_fold = "This is a String."
+ p Readline.completion_case_fold # => "This is a String."
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.vi_editing_mode -> nil
+
+ 編集モードを vi モードにします。
+ vi モードの詳細は、GNU Readline のマニュアルを参照してください。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.vi_editing_mode? -> bool
+
+ 編集モードが vi モードの場合、true を返します。そうでなければ false
+ を返します。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.emacs_editing_mode -> nil
+
+ 編集モードを Emacs モードにします。
+ デフォルトは Emacs モードです。
+ Emacs モードの詳細は、GNU Readline のマニュアルを参照してください。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.emacs_editing_mode? -> bool
+
+ 編集モードが Emacs モードの場合、true を返します。そうでなければ false
+ を返します。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completion_append_character = char
+
+ ユーザの入力の補完が完了した場合に、最後に付加する文字 char を指定し
+ ます。半角スペース「" "」などの単語を区切る文字を指定すれば、連続して
+ 入力する際に便利です。
+
+ 使用例:
+
+ require "readline"
+
+ Readline.readline("> ", true)
+ Readline.completion_append_character = " "
+
+ 実行例:
+
+ >
+ ここで "/var/li" を入力します。
+
+ > /var/li
+ ここで TAB キーを入力します。
+
+ > /var/lib
+ "b" が補完され、最後に " " が追加されるので、"/usr" を連続して入力できます。
+
+ > /var/lib /usr
+
+ なお、1文字しか指定することはできません。
+ 例えば、"string"を指定した場合は最初の文字である"s"だけを使用します。
+
+ require "readline"
+
+ Readline.completion_append_character = "string"
+ p Readline.completion_append_character # => "s"
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completion_append_character -> char
+
+ ユーザの入力の補完が完了した場合に、最後に付加する文字を取得します。
+ デフォルトは空白 (" ") です。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.basic_word_break_characters = string
+
+ ユーザの入力の補完を行う際、単語の区切りを示す複数の文字で構成される
+ 文字列 string を指定します。
+
+ GNU Readline のデフォルトの値は、Bash の補完処理で使用している文字列
+ " \t\n\"\\'`@$><=;|&{(" (スペースを含む) になっています。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.basic_word_break_characters -> string
+
+ ユーザの入力の補完を行う際、単語の区切りを示す複数の文字で構成される
+ 文字列を取得します。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completer_word_break_characters = string
+
+ ユーザの入力の補完を行う際、単語の区切りを示す複数の文字で構成される
+ 文字列 string を指定します。
+ Readline.basic_word_break_characters= との違いは、
+ GNU Readline の rl_complete_internal 関数で使用されることです。
+
+ GNU Readline のデフォルトの値は、
+ Readline.basic_word_break_characters と同じです。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completer_word_break_characters -> string
+
+ ユーザの入力の補完を行う際、単語の区切りを示す複数の文字で構成された
+ 文字列を取得します。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.basic_quote_characters = string
+
+ スペースなどの単語の区切りをクオートするための複数の文字で構成される
+ 文字列 string を指定します。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.basic_quote_characters -> string
+
+ スペースなどの単語の区切りをクオートするための複数の文字で構成される
+ 文字列を取得します。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completer_quote_characters = string
+
+ ユーザの入力の補完を行う際、スペースなどの単語の区切りを
+ クオートするための複数の文字で構成される文字列 string を指定します。
+ 指定した文字の間では、Readline.completer_word_break_characters=
+ で指定した文字列に含まれる文字も、普通の文字列として扱われます。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.completer_quote_characters -> string
+
+ ユーザの入力の補完を行う際、スペースなどの単語の区切りを
+ クオートするための複数の文字で構成される文字列を取得します。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.filename_quote_characters = string
+
+ ユーザの入力時にファイル名の補完を行う際、スペースなどの単語の区切りを
+ クオートするための複数の文字で構成される文字列 string を指定します。
+
+ GNU Readline のデフォルト値は nil です。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+Readline.filename_quote_characters -> string
+
+ ユーザの入力時にファイル名の補完を行う際、スペースなどの単語の区切りを
+ クオートするための複数の文字で構成される文字列を取得します。
+
+ サポートしていない環境では、例外 NotImplementedError が発生します。
+
+ $SAFE が 4 の場合、例外 SecurityError が発生します。
+
+=== クラス定数
+
+HISTORY
+
+ 定数 HISTORY を使用してヒストリにアクセスできます。
+ Enumerable モジュールを extend しており、
+ 配列のように振る舞うことができます。
+ 例えば、HISTORY[4] により 5 番目に入力した内容を取り出すことができます。
+
+ require "readline"
+
+ Readline::HISTORY.push("a", "b", "c", "d", "e")
+ p Readline::HISTORY[4] # => "e"
+
+ 実装しているメソッドを次に挙げます。
+ * HISTORY.to_s -> "HISTORY"
+ * HISTORY[index] -> string
+ * HISTORY[index] = string
+ * HISTORY.push(string[, string, ...]) -> self
+ * HISTORY << string -> self
+ * HISTORY.pop -> string
+ * HISTORY.shift -> string
+ * HISTORY.each -> Enumerator
+ * HISTORY.each { |i| } -> [string]
+ * HISTORY.length -> Integer
+ * HISTORY.empty? -> true or false
+ * HISTORY.delete_at(index) -> string
+ * HISTORY.clear -> self
+
+ サポートしていない環境では、次のメソッドで例外 NotImplementedError が
+ 発生します。
+ * HISTORY[index] = string
+ * HISTORY.pop -> string
+ * HISTORY.shift -> string
+ * HISTORY.delete_at(index) -> string
+ * HISTORY.clear -> self
+
+ $SAFE が 4 の場合、ヒストリにアクセスしたときに例外 SecurityError が
+ 発生します。
+
+FILENAME_COMPLETION_PROC
+
+ ファイル名の補完を行う call メソッドを持つオブジェクトです。
+
+ Readline.completion_proc= により、ユーザの入力時にファイル名の補完を
+ 行うように設定するために使用することを想定してます。
+
+USERNAME_COMPLETION_PROC
+
+ ユーザ名の補完を行う call メソッドを持つオブジェクトです。
+
+ Readline.completion_proc= により、ユーザの入力時にユーザ名の補完を行
+ うように設定するために使用することを想定してます。
+
+VERSION
+
+ 使用している GNU Readline または libedit のバージョンです。
diff --git a/trunk/ext/readline/depend b/trunk/ext/readline/depend
new file mode 100644
index 0000000000..6570c5a036
--- /dev/null
+++ b/trunk/ext/readline/depend
@@ -0,0 +1 @@
+readline.o: readline.c $(hdrdir)/ruby.h $(topdir)/config.h $(hdrdir)/defines.h
diff --git a/trunk/ext/readline/extconf.rb b/trunk/ext/readline/extconf.rb
new file mode 100644
index 0000000000..1d74e29124
--- /dev/null
+++ b/trunk/ext/readline/extconf.rb
@@ -0,0 +1,71 @@
+require "mkmf"
+
+$readline_headers = ["stdio.h"]
+
+def have_readline_header(header)
+ if have_header(header)
+ $readline_headers.push(header)
+ return true
+ else
+ return false
+ end
+end
+
+def have_readline_var(var)
+ return have_var(var, $readline_headers)
+end
+
+def have_readline_func(func)
+ return have_func(func, $readline_headers)
+end
+
+dir_config('curses')
+dir_config('ncurses')
+dir_config('termcap')
+dir_config("readline")
+enable_libedit = enable_config("libedit")
+have_library("user32", nil) if /cygwin/ === RUBY_PLATFORM
+have_library("ncurses", "tgetnum") ||
+ have_library("termcap", "tgetnum") ||
+ have_library("curses", "tgetnum")
+
+if enable_libedit
+ unless (have_readline_header("editline/readline.h") ||
+ have_readline_header("readline/readline.h")) &&
+ have_library("edit", "readline")
+ exit
+ end
+else
+ unless ((have_readline_header("readline/readline.h") &&
+ have_readline_header("readline/history.h")) &&
+ (have_library("readline", "readline") ||
+ have_library("edit", "readline"))) ||
+ (have_readline_header("editline/readline.h") &&
+ have_library("edit", "readline"))
+ exit
+ end
+end
+
+have_readline_func("rl_filename_completion_function")
+have_readline_func("rl_username_completion_function")
+have_readline_func("rl_completion_matches")
+have_readline_var("rl_deprep_term_function")
+have_readline_var("rl_completion_append_character")
+have_readline_var("rl_basic_word_break_characters")
+have_readline_var("rl_completer_word_break_characters")
+have_readline_var("rl_basic_quote_characters")
+have_readline_var("rl_completer_quote_characters")
+have_readline_var("rl_filename_quote_characters")
+have_readline_var("rl_attempted_completion_over")
+have_readline_var("rl_library_version")
+have_readline_var("rl_editing_mode")
+# workaround for native windows.
+/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && have_readline_var("rl_event_hook")
+have_readline_func("rl_cleanup_after_signal")
+have_readline_func("rl_clear_signals")
+have_readline_func("rl_vi_editing_mode")
+have_readline_func("rl_emacs_editing_mode")
+have_readline_func("replace_history_entry")
+have_readline_func("remove_history")
+have_readline_func("clear_history")
+create_makefile("readline")
diff --git a/trunk/ext/readline/readline.c b/trunk/ext/readline/readline.c
new file mode 100644
index 0000000000..41914dca01
--- /dev/null
+++ b/trunk/ext/readline/readline.c
@@ -0,0 +1,1299 @@
+/************************************************
+
+ readline.c - GNU Readline module
+
+ $Author$
+ created at: Wed Jan 20 13:59:32 JST 1999
+
+ Copyright (C) 1997-2008 Shugo Maeda
+ Copyright (C) 2008 TAKAO Kouji
+
+ $Id$
+
+ Contact:
+ - TAKAO Kouji <kouji at takao7 dot net> (current maintainer)
+
+************************************************/
+
+#ifdef RUBY_EXTCONF_H
+#include RUBY_EXTCONF_H
+#endif
+
+#include "ruby/config.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_READLINE_READLINE_H
+#include <readline/readline.h>
+#endif
+#ifdef HAVE_READLINE_HISTORY_H
+#include <readline/history.h>
+#endif
+#ifdef HAVE_EDITLINE_READLINE_H
+#include <editline/readline.h>
+#endif
+
+#include "ruby/ruby.h"
+#include "ruby/io.h"
+#include "ruby/signal.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+static VALUE mReadline;
+
+#define EDIT_LINE_LIBRARY_VERSION "EditLine wrapper"
+
+#define COMPLETION_PROC "completion_proc"
+#define COMPLETION_CASE_FOLD "completion_case_fold"
+static ID completion_proc, completion_case_fold;
+
+#ifndef HAVE_RL_FILENAME_COMPLETION_FUNCTION
+# define rl_filename_completion_function filename_completion_function
+#endif
+#ifndef HAVE_RL_USERNAME_COMPLETION_FUNCTION
+# define rl_username_completion_function username_completion_function
+#endif
+#ifndef HAVE_RL_COMPLETION_MATCHES
+# define rl_completion_matches completion_matches
+#endif
+
+static int (*history_get_offset_func)(int);
+
+static char **readline_attempted_completion_function(const char *text,
+ int start, int end);
+
+#ifdef HAVE_RL_EVENT_HOOK
+#define BUSY_WAIT 0
+
+/*
+ * Document-class: Readline
+ *
+ * The Readline module provides interface for GNU Readline.
+ * This module defines a number of methods to facilitate completion
+ * and accesses input history from the Ruby interpreter.
+ * This module supported Edit Line(libedit) too.
+ * libedit is compatible with GNU Readline.
+ *
+ * GNU Readline:: http://www.gnu.org/directory/readline.html
+ * libedit:: http://www.thrysoee.dk/editline/
+ *
+ * Reads one inputted line with line edit by Readline.readline method.
+ * At this time, the facilitatation completion and the key
+ * bind like Emacs can be operated like GNU Readline.
+ *
+ * require "readline"
+ * while buf = Readline.readline("> ", true)
+ * p buf
+ * end
+ *
+ * The content that the user input can be recorded to the history.
+ * The history can be accessed by Readline::HISTORY constant.
+ *
+ * require "readline"
+ * while buf = Readline.readline("> ", true)
+ * p Readline::HISTORY.to_a
+ * print("-> ", buf, "\n")
+ * end
+ *
+ * Most of methods raise SecurityError exception if $SAFE is 4.
+ *
+ * Documented by TAKAO Kouji <kouji at takao7 dot net>.
+ */
+
+static int readline_event(void);
+static int
+readline_event(void)
+{
+#if BUSY_WAIT
+ rb_thread_schedule();
+#else
+ fd_set rset;
+
+ FD_ZERO(&rset);
+ FD_SET(fileno(rl_instream), &rset);
+ rb_thread_select(fileno(rl_instream) + 1, &rset, NULL, NULL, NULL);
+ return 0;
+#endif
+}
+#endif
+
+/*
+ * call-seq:
+ * Readline.readline(prompt = "", add_hist = false) -> string or nil
+ *
+ * Shows the +prompt+ and reads the inputted line with line editing.
+ * The inputted line is added to the history if +add_hist+ is true.
+ *
+ * Returns nil when the inputted line is empty and user inputs EOF
+ * (Presses ^D on UNIX).
+ *
+ * Raises IOError exception if below conditions are satisfied.
+ * 1. stdin is not tty.
+ * 2. stdin was closed. (errno is EBADF after called isatty(2).)
+ *
+ * This method supports thread. Switchs the thread context when waits
+ * inputting line.
+ *
+ * Supports line edit when inputs line. Provides VI and Emacs editing mode.
+ * Default is Emacs editing mode.
+ *
+ * NOTE: Terminates ruby interpreter and does not return the terminal
+ * status after user pressed '^C' when wait inputting line.
+ * Give 3 examples that avoid it.
+ *
+ * * Catches the Interrupt exception by pressed ^C after returns
+ * terminal status:
+ *
+ * require "readline"
+ *
+ * stty_save = `stty -g`.chomp
+ * begin
+ * while buf = Readline.readline
+ * p buf
+ * end
+ * rescue Interrupt
+ * system("stty", stty_save)
+ * exit
+ * end
+ * end
+ * end
+ *
+ * * Catches the INT signal by pressed ^C after returns terminal
+ * status:
+ *
+ * require "readline"
+ *
+ * stty_save = `stty -g`.chomp
+ * trap("INT") { system "stty", stty_save; exit }
+ *
+ * while buf = Readline.readline
+ * p buf
+ * end
+ *
+ * * Ignores pressing ^C:
+ *
+ * require "readline"
+ *
+ * trap("INT", "SIG_IGN")
+ *
+ * while buf = Readline.readline
+ * p buf
+ * end
+ *
+ * Can make as follows with Readline::HISTORY constant.
+ * It does not record to the history if the inputted line is empty or
+ * the same it as last one.
+ *
+ * require "readline"
+ *
+ * while buf = Readline.readline("> ", true)
+ * # p Readline::HISTORY.to_a
+ * Readline::HISTORY.pop if /^\s*$/ =~ buf
+ *
+ * begin
+ * if Readline::HISTORY[Readline::HISTORY.length-2] == buf
+ * Readline::HISTORY.pop
+ * end
+ * rescue IndexError
+ * end
+ *
+ * # p Readline::HISTORY.to_a
+ * print "-> ", buf, "\n"
+ * end
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_readline(int argc, VALUE *argv, VALUE self)
+{
+ VALUE tmp, add_hist, result;
+ char *prompt = NULL;
+ char *buff;
+ int status;
+
+ rb_secure(4);
+ if (rb_scan_args(argc, argv, "02", &tmp, &add_hist) > 0) {
+ SafeStringValue(tmp);
+ prompt = RSTRING_PTR(tmp);
+ }
+
+ if (!isatty(0) && errno == EBADF) rb_raise(rb_eIOError, "stdin closed");
+
+ buff = (char*)rb_protect((VALUE(*)_((VALUE)))readline, (VALUE)prompt,
+ &status);
+ if (status) {
+#if defined HAVE_RL_CLEANUP_AFTER_SIGNAL
+ /* restore terminal mode and signal handler*/
+ rl_cleanup_after_signal();
+#elif defined HAVE_RL_DEPREP_TERM_FUNCTION
+ /* restore terminal mode */
+ if (rl_deprep_term_function != NULL) /* NULL in libedit. [ruby-dev:29116] */
+ (*rl_deprep_term_function)();
+ else
+#else
+ rl_deprep_terminal();
+#endif
+ rb_jump_tag(status);
+ }
+
+ if (RTEST(add_hist) && buff) {
+ add_history(buff);
+ }
+ if (buff) {
+ result = rb_tainted_str_new2(buff);
+ rb_enc_associate(result, rb_locale_encoding());
+ }
+ else
+ result = Qnil;
+ if (buff) free(buff);
+ return result;
+}
+
+/*
+ * call-seq:
+ * Readline.input = input
+ *
+ * Specifies a File object +input+ that is input stream for
+ * Readline.readline method.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_input(VALUE self, VALUE input)
+{
+ rb_io_t *ifp;
+
+ rb_secure(4);
+ Check_Type(input, T_FILE);
+ GetOpenFile(input, ifp);
+ rl_instream = rb_io_stdio_file(ifp);
+ return input;
+}
+
+/*
+ * call-seq:
+ * Readline.output = output
+ *
+ * Specifies a File object +output+ that is output stream for
+ * Readline.readline method.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_output(VALUE self, VALUE output)
+{
+ rb_io_t *ofp;
+
+ rb_secure(4);
+ Check_Type(output, T_FILE);
+ GetOpenFile(output, ofp);
+ rl_outstream = rb_io_stdio_file(ofp);
+ return output;
+}
+
+/*
+ * call-seq:
+ * Readline.completion_proc = proc
+ *
+ * Specifies a Proc object +proc+ to determine completion behavior. It
+ * should take input-string, and return an array of completion
+ * candidates.
+ *
+ * Raises ArgumentError exception if +proc+ does not respond to call method.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_completion_proc(VALUE self, VALUE proc)
+{
+ rb_secure(4);
+ if (!rb_respond_to(proc, rb_intern("call")))
+ rb_raise(rb_eArgError, "argument must respond to `call'");
+ return rb_ivar_set(mReadline, completion_proc, proc);
+}
+
+/*
+ * call-seq:
+ * Readline.completion_proc -> proc
+ *
+ * Returns the completion Proc object.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_get_completion_proc(VALUE self)
+{
+ rb_secure(4);
+ return rb_attr_get(mReadline, completion_proc);
+}
+
+/*
+ * call-seq:
+ * Readline.completion_case_fold = bool
+ *
+ * Sets whether or not to ignore case on completion.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_completion_case_fold(VALUE self, VALUE val)
+{
+ rb_secure(4);
+ return rb_ivar_set(mReadline, completion_case_fold, val);
+}
+
+/*
+ * call-seq:
+ * Readline.completion_case_fold -> bool
+ *
+ * Returns true if completion ignores case. If no, returns false.
+ *
+ * NOTE: Returns the same object that is specified by
+ * Readline.completion_case_fold= method.
+ *
+ * require "readline"
+ *
+ * Readline.completion_case_fold = "This is a String."
+ * p Readline.completion_case_fold # => "This is a String."
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_get_completion_case_fold(VALUE self)
+{
+ rb_secure(4);
+ return rb_attr_get(mReadline, completion_case_fold);
+}
+
+static char **
+readline_attempted_completion_function(const char *text, int start, int end)
+{
+ VALUE proc, ary, temp;
+ char **result;
+ int case_fold;
+ int i, matches;
+
+ proc = rb_attr_get(mReadline, completion_proc);
+ if (NIL_P(proc))
+ return NULL;
+#ifdef HAVE_RL_ATTEMPTED_COMPLETION_OVER
+ rl_attempted_completion_over = 1;
+#endif
+ case_fold = RTEST(rb_attr_get(mReadline, completion_case_fold));
+ ary = rb_funcall(proc, rb_intern("call"), 1, rb_tainted_str_new2(text));
+ if (TYPE(ary) != T_ARRAY)
+ ary = rb_Array(ary);
+ matches = RARRAY_LEN(ary);
+ if (matches == 0)
+ return NULL;
+ result = ALLOC_N(char *, matches + 2);
+ for (i = 0; i < matches; i++) {
+ temp = rb_obj_as_string(RARRAY_PTR(ary)[i]);
+ result[i + 1] = ALLOC_N(char, RSTRING_LEN(temp) + 1);
+ strcpy(result[i + 1], RSTRING_PTR(temp));
+ }
+ result[matches + 1] = NULL;
+
+ if (matches == 1) {
+ result[0] = strdup(result[1]);
+ }
+ else {
+ register int i = 1;
+ int low = 100000;
+
+ while (i < matches) {
+ register int c1, c2, si;
+
+ if (case_fold) {
+ for (si = 0;
+ (c1 = TOLOWER(result[i][si])) &&
+ (c2 = TOLOWER(result[i + 1][si]));
+ si++)
+ if (c1 != c2) break;
+ } else {
+ for (si = 0;
+ (c1 = result[i][si]) &&
+ (c2 = result[i + 1][si]);
+ si++)
+ if (c1 != c2) break;
+ }
+
+ if (low > si) low = si;
+ i++;
+ }
+ result[0] = ALLOC_N(char, low + 1);
+ strncpy(result[0], result[1], low);
+ result[0][low] = '\0';
+ }
+
+ return result;
+}
+
+/*
+ * call-seq:
+ * Readline.vi_editing_mode -> nil
+ *
+ * Specifies VI editing mode. See the manual of GNU Readline for
+ * details of VI editing mode.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_vi_editing_mode(VALUE self)
+{
+#ifdef HAVE_RL_VI_EDITING_MODE
+ rb_secure(4);
+ rl_vi_editing_mode(1,0);
+ return Qnil;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_VI_EDITING_MODE */
+}
+
+/*
+ * call-seq:
+ * Readline.vi_editing_mode? -> bool
+ *
+ * Returns true if vi mode is active. Returns false if not.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_vi_editing_mode_p(VALUE self)
+{
+#ifdef HAVE_RL_EDITING_MODE
+ rb_secure(4);
+ return rl_editing_mode == 0 ? Qtrue : Qfalse;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_EDITING_MODE */
+}
+
+/*
+ * call-seq:
+ * Readline.emacs_editing_mode -> nil
+ *
+ * Specifies Emacs editing mode. The default is this mode. See the
+ * manual of GNU Readline for details of Emacs editing mode.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_emacs_editing_mode(VALUE self)
+{
+#ifdef HAVE_RL_EMACS_EDITING_MODE
+ rb_secure(4);
+ rl_emacs_editing_mode(1,0);
+ return Qnil;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_EMACS_EDITING_MODE */
+}
+
+/*
+ * call-seq:
+ * Readline.emacs_editing_mode? -> bool
+ *
+ * Returns true if emacs mode is active. Returns false if not.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_emacs_editing_mode_p(VALUE self)
+{
+#ifdef HAVE_RL_EDITING_MODE
+ rb_secure(4);
+ return rl_editing_mode == 1 ? Qtrue : Qfalse;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_EDITING_MODE */
+}
+
+/*
+ * call-seq:
+ * Readline.completion_append_character = char
+ *
+ * Specifies a character to be appended on completion.
+ * Nothing will be appended if an empty string ("") or nil is
+ * specified.
+ *
+ * For example:
+ * require "readline"
+ *
+ * Readline.readline("> ", true)
+ * Readline.completion_append_character = " "
+ *
+ * Result:
+ * >
+ * Input "/var/li".
+ *
+ * > /var/li
+ * Press TAB key.
+ *
+ * > /var/lib
+ * Completes "b" and appends " ". So, you can continuously input "/usr".
+ *
+ * > /var/lib /usr
+ *
+ * NOTE: Only one character can be specified. When "string" is
+ * specified, sets only "s" that is the first.
+ *
+ * require "readline"
+ *
+ * Readline.completion_append_character = "string"
+ * p Readline.completion_append_character # => "s"
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_completion_append_character(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
+ rb_secure(4);
+ if (NIL_P(str)) {
+ rl_completion_append_character = '\0';
+ }
+ else {
+ SafeStringValue(str);
+ if (RSTRING_LEN(str) == 0) {
+ rl_completion_append_character = '\0';
+ } else {
+ rl_completion_append_character = RSTRING_PTR(str)[0];
+ }
+ }
+ return self;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_COMPLETION_APPEND_CHARACTER */
+}
+
+/*
+ * call-seq:
+ * Readline.completion_append_character -> char
+ *
+ * Returns a string containing a character to be appended on
+ * completion. The default is a space (" ").
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_get_completion_append_character(VALUE self)
+{
+#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
+ VALUE str;
+
+ rb_secure(4);
+ if (rl_completion_append_character == '\0')
+ return Qnil;
+
+ str = rb_str_new("", 1);
+ RSTRING_PTR(str)[0] = rl_completion_append_character;
+ return str;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_COMPLETION_APPEND_CHARACTER */
+}
+
+/*
+ * call-seq:
+ * Readline.basic_word_break_characters = string
+ *
+ * Sets the basic list of characters that signal a break between words
+ * for the completer routine. The default is the characters which
+ * break words for completion in Bash: "\t\n\"\\'`@$><=;|&{(".
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_basic_word_break_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_BASIC_WORD_BREAK_CHARACTERS
+ static char *basic_word_break_characters = NULL;
+
+ rb_secure(4);
+ SafeStringValue(str);
+ if (basic_word_break_characters == NULL) {
+ basic_word_break_characters =
+ ALLOC_N(char, RSTRING_LEN(str) + 1);
+ }
+ else {
+ REALLOC_N(basic_word_break_characters, char, RSTRING_LEN(str) + 1);
+ }
+ strncpy(basic_word_break_characters,
+ RSTRING_PTR(str), RSTRING_LEN(str));
+ basic_word_break_characters[RSTRING_LEN(str)] = '\0';
+ rl_basic_word_break_characters = basic_word_break_characters;
+ return self;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_BASIC_WORD_BREAK_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.basic_word_break_characters -> string
+ *
+ * Gets the basic list of characters that signal a break between words
+ * for the completer routine.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_get_basic_word_break_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_BASIC_WORD_BREAK_CHARACTERS
+ rb_secure(4);
+ if (rl_basic_word_break_characters == NULL)
+ return Qnil;
+ return rb_tainted_str_new2(rl_basic_word_break_characters);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_BASIC_WORD_BREAK_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.completer_word_break_characters = string
+ *
+ * Sets the basic list of characters that signal a break between words
+ * for rl_complete_internal(). The default is the value of
+ * Readline.basic_word_break_characters.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_completer_word_break_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_COMPLETER_WORD_BREAK_CHARACTERS
+ static char *completer_word_break_characters = NULL;
+
+ rb_secure(4);
+ SafeStringValue(str);
+ if (completer_word_break_characters == NULL) {
+ completer_word_break_characters =
+ ALLOC_N(char, RSTRING_LEN(str) + 1);
+ }
+ else {
+ REALLOC_N(completer_word_break_characters, char, RSTRING_LEN(str) + 1);
+ }
+ strncpy(completer_word_break_characters,
+ RSTRING_PTR(str), RSTRING_LEN(str));
+ completer_word_break_characters[RSTRING_LEN(str)] = '\0';
+ rl_completer_word_break_characters = completer_word_break_characters;
+ return self;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_COMPLETER_WORD_BREAK_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.completer_word_break_characters -> string
+ *
+ * Gets the basic list of characters that signal a break between words
+ * for rl_complete_internal().
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_get_completer_word_break_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_COMPLETER_WORD_BREAK_CHARACTERS
+ rb_secure(4);
+ if (rl_completer_word_break_characters == NULL)
+ return Qnil;
+ return rb_tainted_str_new2(rl_completer_word_break_characters);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_COMPLETER_WORD_BREAK_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.basic_quote_characters = string
+ *
+ * Sets a list of quote characters which can cause a word break.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_basic_quote_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_BASIC_QUOTE_CHARACTERS
+ static char *basic_quote_characters = NULL;
+
+ rb_secure(4);
+ SafeStringValue(str);
+ if (basic_quote_characters == NULL) {
+ basic_quote_characters =
+ ALLOC_N(char, RSTRING_LEN(str) + 1);
+ }
+ else {
+ REALLOC_N(basic_quote_characters, char, RSTRING_LEN(str) + 1);
+ }
+ strncpy(basic_quote_characters,
+ RSTRING_PTR(str), RSTRING_LEN(str));
+ basic_quote_characters[RSTRING_LEN(str)] = '\0';
+ rl_basic_quote_characters = basic_quote_characters;
+
+ return self;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_BASIC_QUOTE_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.basic_quote_characters -> string
+ *
+ * Gets a list of quote characters which can cause a word break.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_get_basic_quote_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_BASIC_QUOTE_CHARACTERS
+ rb_secure(4);
+ if (rl_basic_quote_characters == NULL)
+ return Qnil;
+ return rb_tainted_str_new2(rl_basic_quote_characters);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_BASIC_QUOTE_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.completer_quote_characters = string
+ *
+ * Sets a list of characters which can be used to quote a substring of
+ * the line. Completion occurs on the entire substring, and within
+ * the substring Readline.completer_word_break_characters are treated
+ * as any other character, unless they also appear within this list.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_completer_quote_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_COMPLETER_QUOTE_CHARACTERS
+ static char *completer_quote_characters = NULL;
+
+ rb_secure(4);
+ SafeStringValue(str);
+ if (completer_quote_characters == NULL) {
+ completer_quote_characters =
+ ALLOC_N(char, RSTRING_LEN(str) + 1);
+ }
+ else {
+ REALLOC_N(completer_quote_characters, char, RSTRING_LEN(str) + 1);
+ }
+ strncpy(completer_quote_characters, RSTRING_PTR(str), RSTRING_LEN(str));
+ completer_quote_characters[RSTRING_LEN(str)] = '\0';
+ rl_completer_quote_characters = completer_quote_characters;
+
+ return self;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_COMPLETER_QUOTE_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.completer_quote_characters -> string
+ *
+ * Gets a list of characters which can be used to quote a substring of
+ * the line.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_get_completer_quote_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_COMPLETER_QUOTE_CHARACTERS
+ rb_secure(4);
+ if (rl_completer_quote_characters == NULL)
+ return Qnil;
+ return rb_tainted_str_new2(rl_completer_quote_characters);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_COMPLETER_QUOTE_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.filename_quote_characters = string
+ *
+ * Sets a list of characters that cause a filename to be quoted by the completer
+ * when they appear in a completed filename. The default is nil.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_set_filename_quote_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_FILENAME_QUOTE_CHARACTERS
+ static char *filename_quote_characters = NULL;
+
+ rb_secure(4);
+ SafeStringValue(str);
+ if (filename_quote_characters == NULL) {
+ filename_quote_characters =
+ ALLOC_N(char, RSTRING_LEN(str) + 1);
+ }
+ else {
+ REALLOC_N(filename_quote_characters, char, RSTRING_LEN(str) + 1);
+ }
+ strncpy(filename_quote_characters, RSTRING_PTR(str), RSTRING_LEN(str));
+ filename_quote_characters[RSTRING_LEN(str)] = '\0';
+ rl_filename_quote_characters = filename_quote_characters;
+
+ return self;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_FILENAME_QUOTE_CHARACTERS */
+}
+
+/*
+ * call-seq:
+ * Readline.filename_quote_characters -> string
+ *
+ * Gets a list of characters that cause a filename to be quoted by the completer
+ * when they appear in a completed filename.
+ *
+ * Raises NotImplementedError if the using readline library does not support.
+ *
+ * Raises SecurityError exception if $SAFE is 4.
+ */
+static VALUE
+readline_s_get_filename_quote_characters(VALUE self, VALUE str)
+{
+#ifdef HAVE_RL_FILENAME_QUOTE_CHARACTERS
+ rb_secure(4);
+ if (rl_filename_quote_characters == NULL)
+ return Qnil;
+ return rb_tainted_str_new2(rl_filename_quote_characters);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif /* HAVE_RL_FILENAME_QUOTE_CHARACTERS */
+}
+
+static VALUE
+hist_to_s(VALUE self)
+{
+ return rb_str_new2("HISTORY");
+}
+
+static int
+history_get_offset_history_base(int offset)
+{
+ return history_base + offset;
+}
+
+static int
+history_get_offset_0(int offset)
+{
+ return offset;
+}
+
+static VALUE
+hist_get(VALUE self, VALUE index)
+{
+ HIST_ENTRY *entry = NULL;
+ int i;
+
+ rb_secure(4);
+ i = NUM2INT(index);
+ if (i < 0) {
+ i += history_length;
+ }
+ if (i >= 0) {
+ entry = history_get(history_get_offset_func(i));
+ }
+ if (entry == NULL) {
+ rb_raise(rb_eIndexError, "invalid index");
+ }
+ return rb_tainted_str_new2(entry->line);
+}
+
+static VALUE
+hist_set(VALUE self, VALUE index, VALUE str)
+{
+#ifdef HAVE_REPLACE_HISTORY_ENTRY
+ HIST_ENTRY *entry = NULL;
+ int i;
+
+ rb_secure(4);
+ i = NUM2INT(index);
+ SafeStringValue(str);
+ if (i < 0) {
+ i += history_length;
+ }
+ if (i >= 0) {
+ entry = replace_history_entry(i, RSTRING_PTR(str), NULL);
+ }
+ if (entry == NULL) {
+ rb_raise(rb_eIndexError, "invalid index");
+ }
+ return str;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+static VALUE
+hist_push(VALUE self, VALUE str)
+{
+ rb_secure(4);
+ SafeStringValue(str);
+ add_history(RSTRING_PTR(str));
+ return self;
+}
+
+static VALUE
+hist_push_method(int argc, VALUE *argv, VALUE self)
+{
+ VALUE str;
+
+ rb_secure(4);
+ while (argc--) {
+ str = *argv++;
+ SafeStringValue(str);
+ add_history(RSTRING_PTR(str));
+ }
+ return self;
+}
+
+static VALUE
+rb_remove_history(int index)
+{
+#ifdef HAVE_REMOVE_HISTORY
+ HIST_ENTRY *entry;
+ VALUE val;
+
+ rb_secure(4);
+ entry = remove_history(index);
+ if (entry) {
+ val = rb_tainted_str_new2(entry->line);
+ free((void *) entry->line);
+ free(entry);
+ return val;
+ }
+ return Qnil;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+static VALUE
+hist_pop(VALUE self)
+{
+ rb_secure(4);
+ if (history_length > 0) {
+ return rb_remove_history(history_length - 1);
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE
+hist_shift(VALUE self)
+{
+ rb_secure(4);
+ if (history_length > 0) {
+ return rb_remove_history(0);
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE
+hist_each(VALUE self)
+{
+ HIST_ENTRY *entry;
+ int i;
+
+ RETURN_ENUMERATOR(self, 0, 0);
+
+ rb_secure(4);
+ for (i = 0; i < history_length; i++) {
+ entry = history_get(history_get_offset_func(i));
+ if (entry == NULL)
+ break;
+ rb_yield(rb_tainted_str_new2(entry->line));
+ }
+ return self;
+}
+
+static VALUE
+hist_length(VALUE self)
+{
+ rb_secure(4);
+ return INT2NUM(history_length);
+}
+
+static VALUE
+hist_empty_p(VALUE self)
+{
+ rb_secure(4);
+ return history_length == 0 ? Qtrue : Qfalse;
+}
+
+static VALUE
+hist_delete_at(VALUE self, VALUE index)
+{
+ int i;
+
+ rb_secure(4);
+ i = NUM2INT(index);
+ if (i < 0)
+ i += history_length;
+ if (i < 0 || i > history_length - 1) {
+ rb_raise(rb_eIndexError, "invalid index");
+ }
+ return rb_remove_history(i);
+}
+
+static VALUE
+hist_clear(VALUE self)
+{
+#ifdef HAVE_CLEAR_HISTORY
+ rb_secure(4);
+ clear_history();
+ return self;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+static VALUE
+filename_completion_proc_call(VALUE self, VALUE str)
+{
+ VALUE result;
+ char **matches;
+ int i;
+
+ matches = rl_completion_matches(StringValuePtr(str),
+ rl_filename_completion_function);
+ if (matches) {
+ result = rb_ary_new();
+ for (i = 0; matches[i]; i++) {
+ rb_ary_push(result, rb_tainted_str_new2(matches[i]));
+ free(matches[i]);
+ }
+ free(matches);
+ if (RARRAY_LEN(result) >= 2)
+ rb_ary_shift(result);
+ }
+ else {
+ result = Qnil;
+ }
+ return result;
+}
+
+static VALUE
+username_completion_proc_call(VALUE self, VALUE str)
+{
+ VALUE result;
+ char **matches;
+ int i;
+
+ matches = rl_completion_matches(StringValuePtr(str),
+ rl_username_completion_function);
+ if (matches) {
+ result = rb_ary_new();
+ for (i = 0; matches[i]; i++) {
+ rb_ary_push(result, rb_tainted_str_new2(matches[i]));
+ free(matches[i]);
+ }
+ free(matches);
+ if (RARRAY_LEN(result) >= 2)
+ rb_ary_shift(result);
+ }
+ else {
+ result = Qnil;
+ }
+ return result;
+}
+
+void
+Init_readline()
+{
+ VALUE history, fcomp, ucomp, version;
+
+ /* Allow conditional parsing of the ~/.inputrc file. */
+ rl_readline_name = "Ruby";
+
+ using_history();
+
+ completion_proc = rb_intern(COMPLETION_PROC);
+ completion_case_fold = rb_intern(COMPLETION_CASE_FOLD);
+
+ mReadline = rb_define_module("Readline");
+ rb_define_module_function(mReadline, "readline",
+ readline_readline, -1);
+ rb_define_singleton_method(mReadline, "input=",
+ readline_s_set_input, 1);
+ rb_define_singleton_method(mReadline, "output=",
+ readline_s_set_output, 1);
+ rb_define_singleton_method(mReadline, "completion_proc=",
+ readline_s_set_completion_proc, 1);
+ rb_define_singleton_method(mReadline, "completion_proc",
+ readline_s_get_completion_proc, 0);
+ rb_define_singleton_method(mReadline, "completion_case_fold=",
+ readline_s_set_completion_case_fold, 1);
+ rb_define_singleton_method(mReadline, "completion_case_fold",
+ readline_s_get_completion_case_fold, 0);
+ rb_define_singleton_method(mReadline, "vi_editing_mode",
+ readline_s_vi_editing_mode, 0);
+ rb_define_singleton_method(mReadline, "vi_editing_mode?",
+ readline_s_vi_editing_mode_p, 0);
+ rb_define_singleton_method(mReadline, "emacs_editing_mode",
+ readline_s_emacs_editing_mode, 0);
+ rb_define_singleton_method(mReadline, "emacs_editing_mode?",
+ readline_s_emacs_editing_mode_p, 0);
+ rb_define_singleton_method(mReadline, "completion_append_character=",
+ readline_s_set_completion_append_character, 1);
+ rb_define_singleton_method(mReadline, "completion_append_character",
+ readline_s_get_completion_append_character, 0);
+ rb_define_singleton_method(mReadline, "basic_word_break_characters=",
+ readline_s_set_basic_word_break_characters, 1);
+ rb_define_singleton_method(mReadline, "basic_word_break_characters",
+ readline_s_get_basic_word_break_characters, 0);
+ rb_define_singleton_method(mReadline, "completer_word_break_characters=",
+ readline_s_set_completer_word_break_characters, 1);
+ rb_define_singleton_method(mReadline, "completer_word_break_characters",
+ readline_s_get_completer_word_break_characters, 0);
+ rb_define_singleton_method(mReadline, "basic_quote_characters=",
+ readline_s_set_basic_quote_characters, 1);
+ rb_define_singleton_method(mReadline, "basic_quote_characters",
+ readline_s_get_basic_quote_characters, 0);
+ rb_define_singleton_method(mReadline, "completer_quote_characters=",
+ readline_s_set_completer_quote_characters, 1);
+ rb_define_singleton_method(mReadline, "completer_quote_characters",
+ readline_s_get_completer_quote_characters, 0);
+ rb_define_singleton_method(mReadline, "filename_quote_characters=",
+ readline_s_set_filename_quote_characters, 1);
+ rb_define_singleton_method(mReadline, "filename_quote_characters",
+ readline_s_get_filename_quote_characters, 0);
+
+ history = rb_obj_alloc(rb_cObject);
+ rb_extend_object(history, rb_mEnumerable);
+ rb_define_singleton_method(history,"to_s", hist_to_s, 0);
+ rb_define_singleton_method(history,"[]", hist_get, 1);
+ rb_define_singleton_method(history,"[]=", hist_set, 2);
+ rb_define_singleton_method(history,"<<", hist_push, 1);
+ rb_define_singleton_method(history,"push", hist_push_method, -1);
+ rb_define_singleton_method(history,"pop", hist_pop, 0);
+ rb_define_singleton_method(history,"shift", hist_shift, 0);
+ rb_define_singleton_method(history,"each", hist_each, 0);
+ rb_define_singleton_method(history,"length", hist_length, 0);
+ rb_define_singleton_method(history,"size", hist_length, 0);
+ rb_define_singleton_method(history,"empty?", hist_empty_p, 0);
+ rb_define_singleton_method(history,"delete_at", hist_delete_at, 1);
+ rb_define_singleton_method(history,"clear", hist_clear, 0);
+
+ /*
+ * The history buffer. It extends Enumerable module, so it behaves
+ * just like an array.
+ * For example, gets the fifth content that the user input by
+ * HISTORY[4].
+ */
+ rb_define_const(mReadline, "HISTORY", history);
+
+ fcomp = rb_obj_alloc(rb_cObject);
+ rb_define_singleton_method(fcomp, "call",
+ filename_completion_proc_call, 1);
+ /*
+ * The Object with the call method that is a completion for filename.
+ * This is sets by Readline.completion_proc= method.
+ */
+ rb_define_const(mReadline, "FILENAME_COMPLETION_PROC", fcomp);
+
+ ucomp = rb_obj_alloc(rb_cObject);
+ rb_define_singleton_method(ucomp, "call",
+ username_completion_proc_call, 1);
+ /*
+ * The Object with the call method that is a completion for usernames.
+ * This is sets by Readline.completion_proc= method.
+ */
+ rb_define_const(mReadline, "USERNAME_COMPLETION_PROC", ucomp);
+ history_get_offset_func = history_get_offset_history_base;
+#if defined HAVE_RL_LIBRARY_VERSION
+ version = rb_str_new2(rl_library_version);
+#if defined HAVE_CLEAR_HISTORY
+ if (strncmp(rl_library_version, EDIT_LINE_LIBRARY_VERSION,
+ strlen(EDIT_LINE_LIBRARY_VERSION)) == 0) {
+ add_history("1");
+ if (history_get(history_get_offset_func(0)) == NULL) {
+ history_get_offset_func = history_get_offset_0;
+ }
+ clear_history();
+ }
+#endif
+#else
+ version = rb_str_new2("2.0 or prior version");
+#endif
+ /* Version string of GNU Readline or libedit. */
+ rb_define_const(mReadline, "VERSION", version);
+
+ rl_attempted_completion_function = readline_attempted_completion_function;
+#ifdef HAVE_RL_EVENT_HOOK
+ rl_event_hook = readline_event;
+#endif
+#ifdef HAVE_RL_CLEAR_SIGNALS
+ rl_clear_signals();
+#endif
+}