Revision: 2919 http://trac.macosforge.org/projects/ruby/changeset/2919 Author: lsansonetti@apple.com Date: 2009-10-29 12:41:32 -0700 (Thu, 29 Oct 2009) Log Message: ----------- merge to recent CRuby readline (patch contributed by kouji at takao7.net) Modified Paths: -------------- MacRuby/trunk/ext/readline/extconf.rb MacRuby/trunk/ext/readline/readline.c Modified: MacRuby/trunk/ext/readline/extconf.rb =================================================================== --- MacRuby/trunk/ext/readline/extconf.rb 2009-10-29 05:45:05 UTC (rev 2918) +++ MacRuby/trunk/ext/readline/extconf.rb 2009-10-29 19:41:32 UTC (rev 2919) @@ -25,30 +25,32 @@ dir_config("readline") enable_libedit = true have_library("user32", nil) if /cygwin/ === RUBY_PLATFORM -have_library("ncurses") || - have_library("termcap") || - have_library("curses") +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") + have_library("edit", "readline") exit end else unless ((have_readline_header("readline/readline.h") && have_readline_header("readline/history.h")) && - (have_library("readline") || - have_library("edit"))) || - (have_readline_header("editline/readline.h") && - have_library("edit")) + (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_getc_function") have_readline_func("rl_filename_completion_function") -have_readline_func("username_completion_function") +have_readline_func("rl_username_completion_function") have_readline_func("rl_completion_matches") +have_readline_func("rl_refresh_line") have_readline_var("rl_deprep_term_function") have_readline_var("rl_completion_append_character") have_readline_var("rl_basic_word_break_characters") @@ -58,12 +60,20 @@ 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") +have_readline_var("rl_line_buffer") +have_readline_var("rl_point") # workaround for native windows. -#/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && have_readline_var("rl_event_hook") +/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && have_readline_var("rl_event_hook") +/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && have_readline_var("rl_catch_sigwinch") +/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && have_readline_var("rl_catch_signals") have_readline_func("rl_cleanup_after_signal") have_readline_func("rl_clear_signals") +have_readline_func("rl_set_screen_size") +have_readline_func("rl_get_screen_size") 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") Modified: MacRuby/trunk/ext/readline/readline.c =================================================================== --- MacRuby/trunk/ext/readline/readline.c 2009-10-29 05:45:05 UTC (rev 2918) +++ MacRuby/trunk/ext/readline/readline.c 2009-10-29 19:41:32 UTC (rev 2919) @@ -1,6 +1,20 @@ -/* readline.c -- GNU Readline module - Copyright (C) 1997-2001 Shugo Maeda */ +/************************************************ + readline.c - GNU Readline module + + $Author: akr $ + created at: Wed Jan 20 13:59:32 JST 1999 + + Copyright (C) 1997-2008 Shugo Maeda + Copyright (C) 2008-2009 TAKAO Kouji + + $Id: readline.c 25189 2009-10-02 12:04:37Z akr $ + + Contact: + - TAKAO Kouji <kouji at takao7 dot net> (current maintainer) + +************************************************/ + #ifdef RUBY_EXTCONF_H #include RUBY_EXTCONF_H #endif @@ -19,9 +33,8 @@ #include <editline/readline.h> #endif -#include "ruby.h" +#include "ruby/ruby.h" #include "ruby/io.h" -#include "ruby/signal.h" #ifdef HAVE_UNISTD_H #include <unistd.h> @@ -44,6 +57,8 @@ 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; @@ -58,10 +73,86 @@ # 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 +#if WITH_OBJC +#define OutputStringValue(str) SafeStringValue(str) +#else +#define OutputStringValue(str) do {\ + SafeStringValue(str);\ + str = rb_str_conv_enc(str, rb_enc_get(str), rb_locale_encoding());\ +} while (0)\ + +#endif + +#if WITH_OBJC +#define rb_locale_str_new_cstr(str) rb_tainted_str_new2(str) +#define rb_locale_str_new(str, len) rb_tainted_str_new(str, len) +#define rb_str_new_cstr(str) rb_tainted_str_new2(str) +#endif + +/* + * 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>. + */ + +#if defined HAVE_RL_GETC_FUNCTION +static VALUE readline_instream; +static ID id_getbyte; + +int rl_getc(FILE *); + +static int readline_getc(FILE *); +static int +readline_getc(FILE *input) +{ + rb_io_t *ifp = 0; + VALUE c; + if (!readline_instream) return rl_getc(input); + GetOpenFile(readline_instream, ifp); +#if WITH_OBJC + if (fileno(rl_instream) != ifp->fd) return rl_getc(input); +#else + if (rl_instream != ifp->stdio_file) return rl_getc(input); +#endif + c = rb_funcall(readline_instream, id_getbyte, 0, 0); + if (NIL_P(c)) return EOF; + return NUM2CHR(c); +} +#elif defined HAVE_RL_EVENT_HOOK #define BUSY_WAIT 0 static int readline_event(void); @@ -81,17 +172,98 @@ } #endif -static HIST_ENTRY* -readline_history_get(int idx) +static VALUE +readline_get(VALUE prompt) { - int readline_idx = history_base + idx - 1; - if (readline_idx < 0) - { - rb_raise(rb_eIndexError, "invalid index"); - } - return history_get(readline_idx); + return (VALUE)readline((char *)prompt); } +/* + * 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(VALUE self, SEL sel, int argc, VALUE *argv) { @@ -102,17 +274,21 @@ rb_secure(4); if (rb_scan_args(argc, argv, "02", &tmp, &add_hist) > 0) { - SafeStringValue(tmp); + OutputStringValue(tmp); prompt = RSTRING_PTR(tmp); } - if (!isatty(0) && errno == EBADF) rb_raise(rb_eIOError, "stdin closed"); + if (!isatty(0) && errno == EBADF) rb_raise(rb_eIOError, "closed stdin"); - buff = (char*)rb_protect((VALUE(*)_((VALUE)))readline, (VALUE)prompt, - &status); +#ifdef _WIN32 + rl_prep_terminal(1); +#endif + buff = (char*)rb_protect((VALUE(*)_((VALUE)))readline_get, (VALUE)prompt, + &status); if (status) { #if defined HAVE_RL_CLEANUP_AFTER_SIGNAL /* restore terminal mode and signal handler*/ + rl_free_line_state(); rl_cleanup_after_signal(); #elif defined HAVE_RL_DEPREP_TERM_FUNCTION /* restore terminal mode */ @@ -129,10 +305,7 @@ add_history(buff); } if (buff) { - result = rb_tainted_str_new2(buff); -#if !WITH_OBJC - rb_enc_associate(result, rb_locale_encoding()); -#endif + result = rb_locale_str_new_cstr(buff); } else result = Qnil; @@ -140,6 +313,15 @@ 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, SEL sel, VALUE input) { @@ -148,10 +330,26 @@ rb_secure(4); Check_Type(input, T_FILE); GetOpenFile(input, ifp); +#if WITH_OBJC rl_instream = fdopen(ifp->fd, "r+"); +#else + rl_instream = rb_io_stdio_file(ifp); +#endif +#ifdef HAVE_RL_GETC_FUNCTION + readline_instream = input; +#endif 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, SEL sel, VALUE output) { @@ -160,19 +358,45 @@ rb_secure(4); Check_Type(output, T_FILE); GetOpenFile(output, ofp); +#if WITH_OBJC rl_outstream = fdopen(ofp->fd, "w+"); +#else + rl_outstream = rb_io_stdio_file(ofp); +#endif 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. + * + * Set default if +proc+ is nil. + * + * 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, SEL sel, VALUE proc) { rb_secure(4); - if (!rb_respond_to(proc, rb_intern("call"))) + if (!NIL_P(proc) && !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, SEL sel) { @@ -180,6 +404,14 @@ 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, SEL sel, VALUE val) { @@ -187,6 +419,22 @@ 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, SEL sel) { @@ -194,13 +442,61 @@ return rb_attr_get(mReadline, completion_case_fold); } +#ifdef HAVE_RL_LINE_BUFFER +/* + * call-seq: + * Readline.line_buffer -> string + * + * Returns the full line that is being edited. This is useful from + * within the complete_proc for determining the context of the + * completion request. + * + * The length of +Readline.line_buffer+ and GNU Readline's rl_end are + * same. + */ +static VALUE +readline_s_get_line_buffer(VALUE self, SEL sel) +{ + rb_secure(4); + if (rl_line_buffer == NULL) + return Qnil; + return rb_tainted_str_new2(rl_line_buffer); +} +#else +#define readline_s_get_line_buffer rb_f_notimplement +#endif + +#ifdef HAVE_RL_POINT +/* + * call-seq: + * Readline.point -> int + * + * Returns the index of the current cursor position in + * +Readline.line_buffer+. + * + * The index in +Readline.line_buffer+ which matches the start of + * input-string passed to completion_proc is computed by subtracting + * the length of input-string from +Readline.point+. + * + * start = (the length of input-string) - Readline.point + */ +static VALUE +readline_s_get_point(VALUE self, SEL sel) +{ + rb_secure(4); + return INT2NUM(rl_point); +} +#else +#define readline_s_get_point rb_f_notimplement +#endif + 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; + long i, matches; proc = rb_attr_get(mReadline, completion_proc); if (NIL_P(proc)) @@ -209,7 +505,7 @@ 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)); + ary = rb_funcall(proc, rb_intern("call"), 1, rb_locale_str_new_cstr(text)); if (TYPE(ary) != T_ARRAY) ary = rb_Array(ary); matches = RARRAY_LEN(ary); @@ -217,7 +513,7 @@ return NULL; result = ALLOC_N(char *, matches + 2); for (i = 0; i < matches; i++) { - temp = rb_obj_as_string(RARRAY_AT(ary, 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)); } @@ -258,42 +554,196 @@ return result; } +#ifdef HAVE_RL_SET_SCREEN_SIZE +/* + * call-seq: + * Readline.set_screen_size(rows, columns) -> self + * + * Set terminal size to +rows+ and +columns+. + * + * See GNU Readline's rl_set_screen_size function. + * + * Raises NotImplementedError if the using readline library does not support. + * + * Raises SecurityError exception if $SAFE is 4. + */ static VALUE +readline_s_set_screen_size(VALUE self, SEL sel, VALUE rows, VALUE columns) +{ + rb_secure(4); + rl_set_screen_size(NUM2INT(rows), NUM2INT(columns)); + return self; +} +#else +#define readline_s_set_screen_size rb_f_notimplement +#endif + +#ifdef HAVE_RL_GET_SCREEN_SIZE +/* + * call-seq: + * Readline.get_screen_size -> [rows, columns] + * + * Returns the terminal's rows and columns. + * + * See GNU Readline's rl_get_screen_size function. + * + * Raises NotImplementedError if the using readline library does not support. + * + * Raises SecurityError exception if $SAFE is 4. + */ +static VALUE +readline_s_get_screen_size(VALUE self, SEL sel) +{ + int rows, columns; + VALUE res; + + rb_secure(4); + rl_get_screen_size(&rows, &columns); + res = rb_ary_new(); + rb_ary_push(res, INT2NUM(rows)); + rb_ary_push(res, INT2NUM(columns)); + return res; +} +#else +#define readline_s_get_screen_size rb_f_notimplement +#endif + +#ifdef HAVE_RL_VI_EDITING_MODE +/* + * 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, SEL sel) { -#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 */ +#define readline_s_vi_editing_mode rb_f_notimplement +#endif + +#ifdef HAVE_RL_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, SEL sel) +{ + rb_secure(4); + return rl_editing_mode == 0 ? Qtrue : Qfalse; } +#else +#define readline_s_vi_editing_mode_p rb_f_notimplement +#endif +#ifdef HAVE_RL_EMACS_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, SEL sel) { -#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 */ +#define readline_s_emacs_editing_mode rb_f_notimplement +#endif + +#ifdef HAVE_RL_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, SEL sel) +{ + rb_secure(4); + return rl_editing_mode == 1 ? Qtrue : Qfalse; } +#else +#define readline_s_emacs_editing_mode_p rb_f_notimplement +#endif +#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER +/* + * 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, SEL sel, VALUE str) { -#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rb_secure(4); if (NIL_P(str)) { rl_completion_append_character = '\0'; } else { - SafeStringValue(str); + OutputStringValue(str); if (RSTRING_LEN(str) == 0) { rl_completion_append_character = '\0'; } else { @@ -301,38 +751,59 @@ } } return self; +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_COMPLETION_APPEND_CHARACTER */ -} +#define readline_s_set_completion_append_character rb_f_notimplement +#endif +#ifdef 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, SEL sel) { -#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER - VALUE str; + char buf[1]; rb_secure(4); if (rl_completion_append_character == '\0') return Qnil; - str = rb_str_new((char *)&rl_completion_append_character, 1); - return str; + buf[0] = (char) rl_completion_append_character; + return rb_locale_str_new(buf, 1); +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_COMPLETION_APPEND_CHARACTER */ -} +#define readline_s_get_completion_append_character rb_f_notimplement +#endif +#ifdef HAVE_RL_BASIC_WORD_BREAK_CHARACTERS +/* + * 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, SEL sel, VALUE str) { -#ifdef HAVE_RL_BASIC_WORD_BREAK_CHARACTERS static char *basic_word_break_characters = NULL; rb_secure(4); - SafeStringValue(str); + OutputStringValue(str); if (basic_word_break_characters == NULL) { basic_word_break_characters = ALLOC_N(char, RSTRING_LEN(str) + 1); @@ -345,34 +816,55 @@ 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 */ -} +#define readline_s_set_basic_word_break_characters rb_f_notimplement +#endif +#ifdef 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, SEL sel, 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); + return rb_locale_str_new_cstr(rl_basic_word_break_characters); +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_BASIC_WORD_BREAK_CHARACTERS */ -} +#define readline_s_get_basic_word_break_characters rb_f_notimplement +#endif +#ifdef HAVE_RL_COMPLETER_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, SEL sel, VALUE str) { -#ifdef HAVE_RL_COMPLETER_WORD_BREAK_CHARACTERS static char *completer_word_break_characters = NULL; rb_secure(4); - SafeStringValue(str); + OutputStringValue(str); if (completer_word_break_characters == NULL) { completer_word_break_characters = ALLOC_N(char, RSTRING_LEN(str) + 1); @@ -385,34 +877,53 @@ 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 */ -} +#define readline_s_set_completer_word_break_characters rb_f_notimplement +#endif +#ifdef 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, SEL sel, 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); + return rb_locale_str_new_cstr(rl_completer_word_break_characters); +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_COMPLETER_WORD_BREAK_CHARACTERS */ -} +#define readline_s_get_completer_word_break_characters rb_f_notimplement +#endif +#ifdef HAVE_RL_BASIC_QUOTE_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, SEL sel, VALUE str) { -#ifdef HAVE_RL_BASIC_QUOTE_CHARACTERS static char *basic_quote_characters = NULL; rb_secure(4); - SafeStringValue(str); + OutputStringValue(str); if (basic_quote_characters == NULL) { basic_quote_characters = ALLOC_N(char, RSTRING_LEN(str) + 1); @@ -426,34 +937,55 @@ rl_basic_quote_characters = basic_quote_characters; return self; +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_BASIC_QUOTE_CHARACTERS */ -} +#define readline_s_set_basic_quote_characters rb_f_notimplement +#endif +#ifdef 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, SEL sel, 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); + return rb_locale_str_new_cstr(rl_basic_quote_characters); +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_BASIC_QUOTE_CHARACTERS */ -} +#define readline_s_get_basic_quote_characters rb_f_notimplement +#endif +#ifdef HAVE_RL_COMPLETER_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, SEL sel, VALUE str) { -#ifdef HAVE_RL_COMPLETER_QUOTE_CHARACTERS static char *completer_quote_characters = NULL; rb_secure(4); - SafeStringValue(str); + OutputStringValue(str); if (completer_quote_characters == NULL) { completer_quote_characters = ALLOC_N(char, RSTRING_LEN(str) + 1); @@ -466,34 +998,54 @@ rl_completer_quote_characters = completer_quote_characters; return self; +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_COMPLETER_QUOTE_CHARACTERS */ -} +#define readline_s_set_completer_quote_characters rb_f_notimplement +#endif +#ifdef 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, SEL sel, 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); + return rb_locale_str_new_cstr(rl_completer_quote_characters); +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_COMPLETER_QUOTE_CHARACTERS */ -} +#define readline_s_get_completer_quote_characters rb_f_notimplement +#endif +#ifdef HAVE_RL_FILENAME_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, SEL sel, VALUE str) { -#ifdef HAVE_RL_FILENAME_QUOTE_CHARACTERS static char *filename_quote_characters = NULL; rb_secure(4); - SafeStringValue(str); + OutputStringValue(str); if (filename_quote_characters == NULL) { filename_quote_characters = ALLOC_N(char, RSTRING_LEN(str) + 1); @@ -506,36 +1058,77 @@ rl_filename_quote_characters = filename_quote_characters; return self; +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_FILENAME_QUOTE_CHARACTERS */ -} +#define readline_s_set_filename_quote_characters rb_f_notimplement +#endif +#ifdef 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, SEL sel, 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); + return rb_locale_str_new_cstr(rl_filename_quote_characters); +} #else - rb_notimplement(); - return Qnil; /* not reached */ -#endif /* HAVE_RL_FILENAME_QUOTE_CHARACTERS */ +#define readline_s_get_filename_quote_characters rb_f_notimplement +#endif + +#ifdef HAVE_RL_REFRESH_LINE +/* + * call-seq: + * Readline.refresh_line -> nil + * + * Clear the current input line. + * + * Raises SecurityError exception if $SAFE is 4. + */ +static VALUE +readline_s_refresh_line(VALUE self, SEL sel) +{ + rb_secure(4); + rl_refresh_line(0, 0); + return Qnil; } +#else +#define readline_s_refresh_line rb_f_notimplement +#endif static VALUE hist_to_s(VALUE self, SEL sel) { - return rb_str_new2("HISTORY"); + return rb_str_new_cstr("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, SEL sel, VALUE index) { - HIST_ENTRY *entry; + HIST_ENTRY *entry = NULL; int i; rb_secure(4); @@ -543,42 +1136,45 @@ if (i < 0) { i += history_length; } - entry = readline_history_get(i); + 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); + return rb_locale_str_new_cstr(entry->line); } +#ifdef HAVE_REPLACE_HISTORY_ENTRY static VALUE hist_set(VALUE self, SEL sel, VALUE index, VALUE str) { -#ifdef HAVE_REPLACE_HISTORY_ENTRY - HIST_ENTRY *entry; + HIST_ENTRY *entry = NULL; int i; rb_secure(4); i = NUM2INT(index); - SafeStringValue(str); + OutputStringValue(str); if (i < 0) { i += history_length; } - entry = replace_history_entry(i, RSTRING_PTR(str), NULL); + 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 */ +#define hist_set rb_f_notimplement #endif -} static VALUE hist_push(VALUE self, SEL sel, VALUE str) { rb_secure(4); - SafeStringValue(str); + OutputStringValue(str); add_history(RSTRING_PTR(str)); return self; } @@ -591,7 +1187,7 @@ rb_secure(4); while (argc--) { str = *argv++; - SafeStringValue(str); + OutputStringValue(str); add_history(RSTRING_PTR(str)); } return self; @@ -607,8 +1203,8 @@ rb_secure(4); entry = remove_history(index); if (entry) { - val = rb_tainted_str_new2(entry->line); - free((void *)entry->line); + val = rb_locale_str_new_cstr(entry->line); + free((void *) entry->line); free(entry); return val; } @@ -651,10 +1247,10 @@ rb_secure(4); for (i = 0; i < history_length; i++) { - entry = readline_history_get(i); + entry = history_get(history_get_offset_func(i)); if (entry == NULL) break; - rb_yield(rb_tainted_str_new2(entry->line)); + rb_yield(rb_locale_str_new_cstr(entry->line)); } return self; } @@ -688,7 +1284,19 @@ return rb_remove_history(i); } +#ifdef HAVE_CLEAR_HISTORY static VALUE +hist_clear(VALUE self, SEL sel) +{ + rb_secure(4); + clear_history(); + return self; +} +#else +#define hist_clear rb_f_notimplement +#endif + +static VALUE filename_completion_proc_call(VALUE self, SEL sel, VALUE str) { VALUE result; @@ -700,7 +1308,7 @@ if (matches) { result = rb_ary_new(); for (i = 0; matches[i]; i++) { - rb_ary_push(result, rb_tainted_str_new2(matches[i])); + rb_ary_push(result, rb_locale_str_new_cstr(matches[i])); free(matches[i]); } free(matches); @@ -725,7 +1333,7 @@ if (matches) { result = rb_ary_new(); for (i = 0; matches[i]; i++) { - rb_ary_push(result, rb_tainted_str_new2(matches[i])); + rb_ary_push(result, rb_locale_str_new_cstr(matches[i])); free(matches[i]); } free(matches); @@ -741,10 +1349,10 @@ void Init_readline() { - VALUE history, fcomp, ucomp; + VALUE history, fcomp, ucomp, version; /* Allow conditional parsing of the ~/.inputrc file. */ - rl_readline_name = "Ruby"; + rl_readline_name = (char *)"Ruby"; using_history(); @@ -766,10 +1374,22 @@ readline_s_set_completion_case_fold, 1); rb_objc_define_method(*(VALUE *)mReadline, "completion_case_fold", readline_s_get_completion_case_fold, 0); + rb_objc_define_method(*(VALUE *)mReadline, "line_buffer", + readline_s_get_line_buffer, 0); + rb_objc_define_method(*(VALUE *)mReadline, "point", + readline_s_get_point, 0); + rb_objc_define_method(*(VALUE *)mReadline, "set_screen_size", + readline_s_set_screen_size, 2); + rb_objc_define_method(*(VALUE *)mReadline, "get_screen_size", + readline_s_get_screen_size, 0); rb_objc_define_method(*(VALUE *)mReadline, "vi_editing_mode", readline_s_vi_editing_mode, 0); + rb_objc_define_method(*(VALUE *)mReadline, "vi_editing_mode?", + readline_s_vi_editing_mode_p, 0); rb_objc_define_method(*(VALUE *)mReadline, "emacs_editing_mode", readline_s_emacs_editing_mode, 0); + rb_objc_define_method(*(VALUE *)mReadline, "emacs_editing_mode?", + readline_s_emacs_editing_mode_p, 0); rb_objc_define_method(*(VALUE *)mReadline, "completion_append_character=", readline_s_set_completion_append_character, 1); rb_objc_define_method(*(VALUE *)mReadline, "completion_append_character", @@ -794,6 +1414,8 @@ readline_s_set_filename_quote_characters, 1); rb_objc_define_method(*(VALUE *)mReadline, "filename_quote_characters", readline_s_get_filename_quote_characters, 0); + rb_objc_define_method(*(VALUE *)mReadline, "refresh_line", + readline_s_refresh_line, 0); history = rb_obj_alloc(rb_cObject); rb_extend_object(history, rb_mEnumerable); @@ -809,27 +1431,79 @@ rb_objc_define_method(*(VALUE *)history, "size", hist_length, 0); rb_objc_define_method(*(VALUE *)history, "empty?", hist_empty_p, 0); rb_objc_define_method(*(VALUE *)history, "delete_at", hist_delete_at, 1); + rb_objc_define_method(*(VALUE *)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_objc_define_method(rb_singleton_class(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_objc_define_method(rb_singleton_class(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 - rb_define_const(mReadline, "VERSION", rb_str_new2(rl_library_version)); + version = rb_str_new_cstr(rl_library_version); +#if defined HAVE_CLEAR_HISTORY || defined HAVE_REMOVE_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; + } +#if !defined HAVE_CLEAR_HISTORY + clear_history(); #else - rb_define_const(mReadline, "VERSION", rb_str_new2("2.0 or prior version")); + { + HIST_ENTRY *entry = remove_history(0); + if (entry) { + free((char *)entry->line); + free(entry); + } + } #endif + } +#endif +#else + version = rb_str_new_cstr("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; +#if defined HAVE_RL_GETC_FUNCTION + rl_getc_function = (int (*)(FILE *))readline_getc; +#if WITH_OBJC + id_getbyte = rb_intern("getbyte"); +#else + id_getbyte = rb_intern_const("getbyte"); #endif +#elif defined HAVE_RL_EVENT_HOOK + rl_event_hook = readline_event; + #endif +#ifdef HAVE_RL_CATCH_SIGNALS + rl_catch_signals = 0; +#endif +#ifdef HAVE_RL_CATCH_SIGWINCH + rl_catch_sigwinch = 0; +#endif #ifdef HAVE_RL_CLEAR_SIGNALS rl_clear_signals(); #endif