From 0dc342de848a642ecce8db697b8fecd83a63e117 Mon Sep 17 00:00:00 2001 From: yugui Date: Mon, 25 Aug 2008 15:02:05 +0000 Subject: added tag v1_9_0_4 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_9_0_4@18845 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- trunk/ext/readline/readline.c | 1299 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1299 insertions(+) create mode 100644 trunk/ext/readline/readline.c (limited to 'trunk/ext/readline/readline.c') 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 (current maintainer) + +************************************************/ + +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif + +#include "ruby/config.h" +#include +#include +#include +#ifdef HAVE_READLINE_READLINE_H +#include +#endif +#ifdef HAVE_READLINE_HISTORY_H +#include +#endif +#ifdef HAVE_EDITLINE_READLINE_H +#include +#endif + +#include "ruby/ruby.h" +#include "ruby/io.h" +#include "ruby/signal.h" + +#ifdef HAVE_UNISTD_H +#include +#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 . + */ + +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 +} -- cgit v1.2.3