diff options
Diffstat (limited to 'jni/ruby/ext/readline/readline.c')
| -rw-r--r-- | jni/ruby/ext/readline/readline.c | 1996 | 
1 files changed, 1996 insertions, 0 deletions
| diff --git a/jni/ruby/ext/readline/readline.c b/jni/ruby/ext/readline/readline.c new file mode 100644 index 0000000..835e346 --- /dev/null +++ b/jni/ruby/ext/readline/readline.c @@ -0,0 +1,1996 @@ +/************************************************ + +  readline.c - GNU Readline module + +  $Author: naruse $ +  created at: Wed Jan 20 13:59:32 JST 1999 + +  Copyright (C) 1997-2008  Shugo Maeda +  Copyright (C) 2008-2013  Kouji Takao + +  $Id: readline.c 49299 2015-01-17 07:50:07Z naruse $ + +  Contact: +   - Kouji Takao <kouji dot takao at gmail dot com> (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 "internal.h" +#include "ruby/io.h" +#include "ruby/thread.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +static VALUE mReadline; + +#define EDIT_LINE_LIBRARY_VERSION "EditLine wrapper" +#ifndef USE_INSERT_IGNORE_ESCAPE +# if !defined(HAVE_EDITLINE_READLINE_H) && defined(RL_PROMPT_START_IGNORE) && defined(RL_PROMPT_END_IGNORE) +#  define USE_INSERT_IGNORE_ESCAPE 1 +# else +#  define USE_INSERT_IGNORE_ESCAPE 0 +# endif +#endif + +#define COMPLETION_PROC "completion_proc" +#define COMPLETION_CASE_FOLD "completion_case_fold" +static ID completion_proc, completion_case_fold; +#if USE_INSERT_IGNORE_ESCAPE +static ID id_orig_prompt, id_last_prompt; +#endif +#if defined(HAVE_RL_PRE_INPUT_HOOK) +static ID id_pre_input_hook; +#endif +#if defined(HAVE_RL_SPECIAL_PREFIXES) +static ID id_special_prefixes; +#endif + +#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 int (*history_replace_offset_func)(int); +#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER +static int readline_completion_append_character; +#endif + +static char **readline_attempted_completion_function(const char *text, +                                                     int start, int end); + +#define OutputStringValue(str) do {\ +    SafeStringValue(str);\ +    (str) = rb_str_conv_enc((str), rb_enc_get(str), rb_locale_encoding());\ +} while (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 + * + * Documented by Kouji Takao <kouji dot takao at gmail dot com>. + */ + +static VALUE readline_instream; +static VALUE readline_outstream; +static FILE *readline_rl_instream; +static FILE *readline_rl_outstream; + +#if defined HAVE_RL_GETC_FUNCTION + +#ifndef HAVE_RL_GETC +#define rl_getc(f) EOF +#endif + +struct getc_struct { +  FILE *input; +  int fd; +  int ret; +  int err; +}; + +static int +getc_body(struct getc_struct *p) +{ +    char ch; +    ssize_t ss; + +#if defined(_WIN32) +    { +        INPUT_RECORD ir; +        int n; +        static int prior_key = '0'; +        for (;;) { +            if (prior_key > 0xff) { +                prior_key = rl_getc(p->input); +                return prior_key; +            } +            if (PeekConsoleInput((HANDLE)_get_osfhandle(p->fd), &ir, 1, &n)) { +                if (n == 1) { +                    if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) { +                        prior_key = rl_getc(p->input); +                        return prior_key; +                    } else { +                        ReadConsoleInput((HANDLE)_get_osfhandle(p->fd), &ir, 1, &n); +                    } +                } else { +                    HANDLE h = (HANDLE)_get_osfhandle(p->fd); +                    rb_w32_wait_events(&h, 1, INFINITE); +                } +            } else { +                break; +            } +        } +    } +#endif + +    ss = read(p->fd, &ch, 1); +    if (ss == 0) { +        errno = 0; +        return EOF; +    } +    if (ss != 1) +        return EOF; +    return (unsigned char)ch; +} + +static void * +getc_func(void *data1) +{ +    struct getc_struct *p = data1; +    errno = 0; +    p->ret = getc_body(p); +    p->err = errno; +    return NULL; +} + +static int +readline_getc(FILE *input) +{ +    struct getc_struct data; +    if (input == NULL) /* editline may give NULL as input. */ +        input = stdin; +    data.input = input; +    data.fd = fileno(input); +  again: +    data.ret = EOF; +    data.err = EINTR; /* getc_func is not called if already interrupted. */ +    rb_thread_call_without_gvl2(getc_func, &data, RUBY_UBF_IO, NULL); +    if (data.ret == EOF) { +        if (data.err == 0) { +            return EOF; +        } +        if (data.err == EINTR) { +            rb_thread_check_ints(); +            goto again; +        } +        if (data.err == EWOULDBLOCK || data.err == EAGAIN) { +            int ret; +            if (fileno(input) != data.fd) +                rb_bug("readline_getc: input closed unexpectedly or memory corrupted"); +            ret = rb_wait_for_single_fd(data.fd, RB_WAITFD_IN, NULL); +            if (ret != -1 || errno == EINTR) +                goto again; +            rb_sys_fail("rb_wait_for_single_fd"); +        } +        errno = data.err; +        rb_sys_fail("read"); +    } +    return data.ret; +} + +#elif defined HAVE_RL_EVENT_HOOK +#define BUSY_WAIT 0 + +static int readline_event(void); +static int +readline_event(void) +{ +#if BUSY_WAIT +    rb_thread_schedule(); +#else +    rb_wait_for_single_fd(fileno(rl_instream), RB_WAITFD_IN, NULL); +    return 0; +#endif +} +#endif + +#if USE_INSERT_IGNORE_ESCAPE +static VALUE +insert_ignore_escape(VALUE self, VALUE prompt) +{ +    VALUE last_prompt, orig_prompt = rb_attr_get(self, id_orig_prompt); +    int ignoring = 0; +    const char *s0, *s, *e; +    long len; +    static const char ignore_code[2] = {RL_PROMPT_START_IGNORE, RL_PROMPT_END_IGNORE}; + +    prompt = rb_str_new_shared(prompt); +    last_prompt = rb_attr_get(self, id_last_prompt); +    if (orig_prompt == prompt) return last_prompt; +    len = RSTRING_LEN(prompt); +    if (NIL_P(last_prompt)) { +        last_prompt = rb_str_tmp_new(len); +    } + +    s = s0 = RSTRING_PTR(prompt); +    e = s0 + len; +    rb_str_set_len(last_prompt, 0); +    while (s < e && *s) { +        switch (*s) { +          case RL_PROMPT_START_IGNORE: +            ignoring = -1; +            rb_str_cat(last_prompt, s0, ++s - s0); +            s0 = s; +            break; +          case RL_PROMPT_END_IGNORE: +            ignoring = 0; +            rb_str_cat(last_prompt, s0, ++s - s0); +            s0 = s; +            break; +          case '\033': +            if (++s < e && *s == '[') { +                rb_str_cat(last_prompt, s0, s - s0 - 1); +                s0 = s - 1; +                while (++s < e && *s) { +                    if (ISALPHA(*(unsigned char *)s)) { +                        if (!ignoring) { +                            ignoring = 1; +                            rb_str_cat(last_prompt, ignore_code+0, 1); +                        } +                        rb_str_cat(last_prompt, s0, ++s - s0); +                        s0 = s; +                        break; +                    } +                    else if (!(('0' <= *s && *s <= '9') || *s == ';')) { +                        break; +                    } +                } +            } +            break; +          default: +            if (ignoring > 0) { +                ignoring = 0; +                rb_str_cat(last_prompt, ignore_code+1, 1); +            } +            s++; +            break; +        } +    } +    if (ignoring > 0) { +        ignoring = 0; +        rb_str_cat(last_prompt, ignore_code+1, 1); +    } +    rb_str_cat(last_prompt, s0, s - s0); + +    rb_ivar_set(self, id_orig_prompt, prompt); +    rb_ivar_set(self, id_last_prompt, last_prompt); + +    return last_prompt; +} +#endif + +static VALUE +readline_get(VALUE prompt) +{ +#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER +    readline_completion_append_character = rl_completion_append_character; +#endif +    return (VALUE)readline((char *)prompt); +} + +static void +clear_rl_instream(void) +{ +    if (readline_rl_instream) { +        fclose(readline_rl_instream); +        if (rl_instream == readline_rl_instream) +            rl_instream = NULL; +        readline_rl_instream = NULL; +    } +    readline_instream = Qfalse; +} + +static void +clear_rl_outstream(void) +{ +    if (readline_rl_outstream) { +        fclose(readline_rl_outstream); +        if (rl_outstream == readline_rl_outstream) +            rl_outstream = NULL; +        readline_rl_outstream = NULL; +    } +    readline_outstream = Qfalse; +} + +static void +prepare_readline(void) +{ +    static int initialized = 0; +    if (!initialized) { +	rl_initialize(); +	initialized = 1; +    } + +    if (readline_instream) { +        rb_io_t *ifp; +        rb_io_check_initialized(ifp = RFILE(rb_io_taint_check(readline_instream))->fptr); +        if (ifp->fd < 0) { +            clear_rl_instream(); +            rb_raise(rb_eIOError, "closed readline input"); +        } +    } + +    if (readline_outstream) { +        rb_io_t *ofp; +        rb_io_check_initialized(ofp = RFILE(rb_io_taint_check(readline_outstream))->fptr); +        if (ofp->fd < 0) { +            clear_rl_outstream(); +            rb_raise(rb_eIOError, "closed readline output"); +        } +    } +} + +/* + * 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 one of below conditions are satisfied. + * 1. stdin was closed. + * 2. stdout was closed. + * + * This method supports thread. Switches 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 + */ +static VALUE +readline_readline(int argc, VALUE *argv, VALUE self) +{ +    VALUE tmp, add_hist, result; +    char *prompt = NULL; +    char *buff; +    int status; + +    if (rb_scan_args(argc, argv, "02", &tmp, &add_hist) > 0) { +        OutputStringValue(tmp); +#if USE_INSERT_IGNORE_ESCAPE +        tmp = insert_ignore_escape(self, tmp); +        rb_str_locktmp(tmp); +#endif +        prompt = RSTRING_PTR(tmp); +    } + +    prepare_readline(); + +#ifdef _WIN32 +    rl_prep_terminal(1); +#endif +    buff = (char*)rb_protect(readline_get, (VALUE)prompt, &status); +#if USE_INSERT_IGNORE_ESCAPE +    if (prompt) { +        rb_str_unlocktmp(tmp); +    } +#endif +    if (status) { +#if defined HAVE_RL_CLEANUP_AFTER_SIGNAL +        /* restore terminal mode and signal handler*/ +#if defined HAVE_RL_FREE_LINE_STATE +        rl_free_line_state(); +#endif +        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_locale_str_new_cstr(buff); +    } +    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. + */ +static VALUE +readline_s_set_input(VALUE self, VALUE input) +{ +    rb_io_t *ifp; +    int fd; +    FILE *f; + +    if (NIL_P(input)) { +        clear_rl_instream(); +    } +    else { +        Check_Type(input, T_FILE); +        GetOpenFile(input, ifp); +        clear_rl_instream(); +        fd = rb_cloexec_dup(ifp->fd); +        if (fd == -1) +            rb_sys_fail("dup"); +        f = fdopen(fd, "r"); +        if (f == NULL) { +            int save_errno = errno; +            close(fd); +            errno = save_errno; +            rb_sys_fail("fdopen"); +        } +        rl_instream = readline_rl_instream = f; +        readline_instream = input; +    } +    return input; +} + +/* + * call-seq: + *   Readline.output = output + * + * Specifies a File object +output+ that is output stream for + * Readline.readline method. + */ +static VALUE +readline_s_set_output(VALUE self, VALUE output) +{ +    rb_io_t *ofp; +    int fd; +    FILE *f; + +    if (NIL_P(output)) { +        clear_rl_outstream(); +    } +    else { +        Check_Type(output, T_FILE); +        GetOpenFile(output, ofp); +        clear_rl_outstream(); +        fd = rb_cloexec_dup(ofp->fd); +        if (fd == -1) +            rb_sys_fail("dup"); +        f = fdopen(fd, "w"); +        if (f == NULL) { +            int save_errno = errno; +            close(fd); +            errno = save_errno; +            rb_sys_fail("fdopen"); +        } +        rl_outstream = readline_rl_outstream = f; +        readline_outstream = output; +    } +    return output; +} + +#if defined(HAVE_RL_PRE_INPUT_HOOK) +/* + * call-seq: + *   Readline.pre_input_hook = proc + * + * Specifies a Proc object +proc+ to call after the first prompt has + * been printed and just before readline starts reading input + * characters. + * + * See GNU Readline's rl_pre_input_hook variable. + * + * Raises ArgumentError if +proc+ does not respond to the call method. + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_set_pre_input_hook(VALUE self, VALUE proc) +{ +    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, id_pre_input_hook, proc); +} + +/* + * call-seq: + *   Readline.pre_input_hook -> proc + * + * Returns a Proc object +proc+ to call after the first prompt has + * been printed and just before readline starts reading input + * characters. The default is nil. + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_get_pre_input_hook(VALUE self) +{ +    return rb_attr_get(mReadline, id_pre_input_hook); +} + +static int +readline_pre_input_hook(void) +{ +    VALUE proc; + +    proc = rb_attr_get(mReadline, id_pre_input_hook); +    if (!NIL_P(proc)) +        rb_funcall(proc, rb_intern("call"), 0); +    return 0; +} +#else +#define readline_s_set_pre_input_hook rb_f_notimplement +#define readline_s_get_pre_input_hook rb_f_notimplement +#endif + +#if defined(HAVE_RL_INSERT_TEXT) +/* + * call-seq: + *   Readline.insert_text(string) -> self + * + * Insert text into the line at the current cursor position. + * + * See GNU Readline's rl_insert_text function. + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_insert_text(VALUE self, VALUE str) +{ +    OutputStringValue(str); +    rl_insert_text(RSTRING_PTR(str)); +    return self; +} +#else +#define readline_s_insert_text rb_f_notimplement +#endif + +#if defined(HAVE_RL_DELETE_TEXT) +/* + * call-seq: + *   Readline.delete_text([start[, length]]) -> self + *   Readline.delete_text(start..end)        -> self + *   Readline.delete_text()                  -> self + * + * Delete text between start and end in the current line. + * + * See GNU Readline's rl_delete_text function. + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_delete_text(int argc, VALUE *argv, VALUE self) +{ +    rb_check_arity(argc, 0, 2); +    if (rl_line_buffer) { +        char *p, *ptr = rl_line_buffer; +        long beg = 0, len = strlen(rl_line_buffer); +        struct RString fakestr; +        VALUE str = rb_setup_fake_str(&fakestr, ptr, len, rb_locale_encoding()); +        OBJ_FREEZE(str); +        if (argc == 2) { +            beg = NUM2LONG(argv[0]); +            len = NUM2LONG(argv[1]); +          num_pos: +            p = rb_str_subpos(str, beg, &len); +            if (!p) rb_raise(rb_eArgError, "invalid index"); +            beg = p - ptr; +        } +        else if (argc == 1) { +            len = rb_str_strlen(str); +            if (!rb_range_beg_len(argv[0], &beg, &len, len, 1)) { +                beg = NUM2LONG(argv[0]); +                goto num_pos; +            } +        } +        rl_delete_text(rb_long2int(beg), rb_long2int(beg + len)); +    } +    return self; +} +#else +#define readline_s_delete_text rb_f_notimplement +#endif + +#if defined(HAVE_RL_REDISPLAY) +/* + * call-seq: + *   Readline.redisplay -> self + * + * Change what's displayed on the screen to reflect the current + * contents. + * + * See GNU Readline's rl_redisplay function. + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_redisplay(VALUE self) +{ +    rl_redisplay(); +    return self; +} +#else +#define readline_s_redisplay rb_f_notimplement +#endif + +/* + * 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. + * + * The default completion is used if +proc+ is nil. + * + * The String that is passed to the Proc depends on the + * Readline.completer_word_break_characters property.  By default the word + * under the cursor is passed to the Proc.  For example, if the input is "foo + * bar" then only "bar" would be passed to the completion Proc. + * + * Upon successful completion the Readline.completion_append_character will be + * appended to the input so the user can start working on their next argument. + * + * = Examples + * + * == Completion for a Static List + * + *   require 'readline' + * + *   LIST = [ + *     'search', 'download', 'open', + *     'help', 'history', 'quit', + *     'url', 'next', 'clear', + *     'prev', 'past' + *   ].sort + * + *   comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) } + * + *   Readline.completion_append_character = " " + *   Readline.completion_proc = comp + * + *   while line = Readline.readline('> ', true) + *     p line + *   end + * + * == Completion For Directory Contents + * + *   require 'readline' + * + *   Readline.completion_append_character = " " + *   Readline.completion_proc = Proc.new do |str| + *     Dir[str+'*'].grep(/^#{Regexp.escape(str)}/) + *   end + * + *   while line = Readline.readline('> ', true) + *     p line + *   end + * + * = Autocomplete strategies + * + * When working with auto-complete there are some strategies that work well. + * To get some ideas you can take a look at the + * completion.rb[http://svn.ruby-lang.org/repos/ruby/trunk/lib/irb/completion.rb] + * file for irb. + * + * The common strategy is to take a list of possible completions and filter it + * down to those completions that start with the user input.  In the above + * examples Enumerator.grep is used.  The input is escaped to prevent Regexp + * special characters from interfering with the matching. + * + * It may also be helpful to use the Abbrev library to generate completions. + * + * Raises ArgumentError if +proc+ does not respond to the call method. + */ +static VALUE +readline_s_set_completion_proc(VALUE self, VALUE proc) +{ +    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. + */ +static VALUE +readline_s_get_completion_proc(VALUE self) +{ +    return rb_attr_get(mReadline, completion_proc); +} + +/* + * call-seq: + *   Readline.completion_case_fold = bool + * + * Sets whether or not to ignore case on completion. + */ +static VALUE +readline_s_set_completion_case_fold(VALUE self, VALUE val) +{ +    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." + */ +static VALUE +readline_s_get_completion_case_fold(VALUE self) +{ +    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. + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_get_line_buffer(VALUE self) +{ +    if (rl_line_buffer == NULL) +        return Qnil; +    return rb_locale_str_new_cstr(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 + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_get_point(VALUE self) +{ +    return INT2NUM(rl_point); +} + +/* + * call-seq: + *   Readline.point = int + * + * Set the index of the current cursor position in + * +Readline.line_buffer+. + * + * Raises NotImplementedError if the using readline library does not support. + * + * See +Readline.point+. + */ +static VALUE +readline_s_set_point(VALUE self, VALUE pos) +{ +    rl_point = NUM2INT(pos); +    return pos; +} +#else +#define readline_s_get_point rb_f_notimplement +#define readline_s_set_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; +    long i, matches; +    rb_encoding *enc; +    VALUE encobj; + +    proc = rb_attr_get(mReadline, completion_proc); +    if (NIL_P(proc)) +        return NULL; +#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER +    rl_completion_append_character = readline_completion_append_character; +#endif +#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_locale_str_new_cstr(text)); +    if (!RB_TYPE_P(ary, T_ARRAY)) +        ary = rb_Array(ary); +    matches = RARRAY_LEN(ary); +    if (matches == 0) return NULL; +    result = (char**)malloc((matches + 2)*sizeof(char*)); +    if (result == NULL) rb_memerror(); +    enc = rb_locale_encoding(); +    encobj = rb_enc_from_encoding(enc); +    for (i = 0; i < matches; i++) { +        temp = rb_obj_as_string(RARRAY_PTR(ary)[i]); +        StringValueCStr(temp);  /* must be NUL-terminated */ +        rb_enc_check(encobj, temp); +        result[i + 1] = (char*)malloc(RSTRING_LEN(temp) + 1); +        if (result[i + 1]  == NULL) rb_memerror(); +        strcpy(result[i + 1], RSTRING_PTR(temp)); +    } +    result[matches + 1] = NULL; + +    if (matches == 1) { +        result[0] = strdup(result[1]); +    } +    else { +        const char *result1 = result[1]; +        long low = strlen(result1); + +        for (i = 1; i < matches; ++i) { +            register int c1, c2; +            long i1, i2, l2; +            int n1, n2; +            const char *p2 = result[i + 1]; + +            l2 = strlen(p2); +            for (i1 = i2 = 0; i1 < low && i2 < l2; i1 += n1, i2 += n2) { +                c1 = rb_enc_codepoint_len(result1 + i1, result1 + low, &n1, enc); +                c2 = rb_enc_codepoint_len(p2 + i2, p2 + l2, &n2, enc); +                if (case_fold) { +                    c1 = rb_tolower(c1); +                    c2 = rb_tolower(c2); +                } +                if (c1 != c2) break; +            } + +            low = i1; +        } +        result[0] = (char*)malloc(low + 1); +        if (result[0]  == NULL) rb_memerror(); +        strncpy(result[0], result[1], low); +        result[0][low] = '\0'; +    } + +    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. + */ +static VALUE +readline_s_set_screen_size(VALUE self, VALUE rows, VALUE columns) +{ +    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. + */ +static VALUE +readline_s_get_screen_size(VALUE self) +{ +    int rows, columns; +    VALUE res; + +    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. + */ +static VALUE +readline_s_vi_editing_mode(VALUE self) +{ +    rl_vi_editing_mode(1,0); +    return Qnil; +} +#else +#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. + */ +static VALUE +readline_s_vi_editing_mode_p(VALUE self) +{ +    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. + */ +static VALUE +readline_s_emacs_editing_mode(VALUE self) +{ +    rl_emacs_editing_mode(1,0); +    return Qnil; +} +#else +#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. + */ +static VALUE +readline_s_emacs_editing_mode_p(VALUE self) +{ +    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. + */ +static VALUE +readline_s_set_completion_append_character(VALUE self, VALUE str) +{ +    if (NIL_P(str)) { +        rl_completion_append_character = '\0'; +    } +    else { +        OutputStringValue(str); +        if (RSTRING_LEN(str) == 0) { +            rl_completion_append_character = '\0'; +        } else { +            rl_completion_append_character = RSTRING_PTR(str)[0]; +        } +    } +    return self; +} +#else +#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. + */ +static VALUE +readline_s_get_completion_append_character(VALUE self) +{ +    char buf[1]; + +    if (rl_completion_append_character == '\0') +        return Qnil; + +    buf[0] = (char) rl_completion_append_character; +    return rb_locale_str_new(buf, 1); +} +#else +#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. + */ +static VALUE +readline_s_set_basic_word_break_characters(VALUE self, VALUE str) +{ +    static char *basic_word_break_characters = NULL; + +    OutputStringValue(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 +#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. + */ +static VALUE +readline_s_get_basic_word_break_characters(VALUE self, VALUE str) +{ +    if (rl_basic_word_break_characters == NULL) +        return Qnil; +    return rb_locale_str_new_cstr(rl_basic_word_break_characters); +} +#else +#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. + */ +static VALUE +readline_s_set_completer_word_break_characters(VALUE self, VALUE str) +{ +    static char *completer_word_break_characters = NULL; + +    OutputStringValue(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 +#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. + */ +static VALUE +readline_s_get_completer_word_break_characters(VALUE self, VALUE str) +{ +    if (rl_completer_word_break_characters == NULL) +        return Qnil; +    return rb_locale_str_new_cstr(rl_completer_word_break_characters); +} +#else +#define readline_s_get_completer_word_break_characters rb_f_notimplement +#endif + +#if defined(HAVE_RL_SPECIAL_PREFIXES) +/* + * call-seq: + *   Readline.special_prefixes = string + * + * Sets the list of characters that are word break characters, but + * should be left in text when it is passed to the completion + * function. Programs can use this to help determine what kind of + * completing to do. For instance, Bash sets this variable to "$@" so + * that it can complete shell variables and hostnames. + * + * See GNU Readline's rl_special_prefixes variable. + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_set_special_prefixes(VALUE self, VALUE str) +{ +    if (!NIL_P(str)) { +        OutputStringValue(str); +        str = rb_str_dup_frozen(str); +        rb_obj_hide(str); +    } +    rb_ivar_set(mReadline, id_special_prefixes, str); +    if (NIL_P(str)) { +        rl_special_prefixes = NULL; +    } +    else { +        rl_special_prefixes = RSTRING_PTR(str); +    } +    return self; +} + +/* + * call-seq: + *   Readline.special_prefixes -> string + * + * Gets the list of characters that are word break characters, but + * should be left in text when it is passed to the completion + * function. + * + * See GNU Readline's rl_special_prefixes variable. + * + * Raises NotImplementedError if the using readline library does not support. + */ +static VALUE +readline_s_get_special_prefixes(VALUE self) +{ +    VALUE str; +    if (rl_special_prefixes == NULL) return Qnil; +    str = rb_ivar_get(mReadline, id_special_prefixes); +    if (!NIL_P(str)) { +        str = rb_str_dup_frozen(str); +        rb_obj_reveal(str, rb_cString); +    } +    return str; +} +#else +#define readline_s_set_special_prefixes rb_f_notimplement +#define readline_s_get_special_prefixes 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. + */ +static VALUE +readline_s_set_basic_quote_characters(VALUE self, VALUE str) +{ +    static char *basic_quote_characters = NULL; + +    OutputStringValue(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 +#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. + */ +static VALUE +readline_s_get_basic_quote_characters(VALUE self, VALUE str) +{ +    if (rl_basic_quote_characters == NULL) +        return Qnil; +    return rb_locale_str_new_cstr(rl_basic_quote_characters); +} +#else +#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. + */ +static VALUE +readline_s_set_completer_quote_characters(VALUE self, VALUE str) +{ +    static char *completer_quote_characters = NULL; + +    OutputStringValue(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 +#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. + */ +static VALUE +readline_s_get_completer_quote_characters(VALUE self, VALUE str) +{ +    if (rl_completer_quote_characters == NULL) +        return Qnil; +    return rb_locale_str_new_cstr(rl_completer_quote_characters); +} +#else +#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. + */ +static VALUE +readline_s_set_filename_quote_characters(VALUE self, VALUE str) +{ +    static char *filename_quote_characters = NULL; + +    OutputStringValue(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 +#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. + */ +static VALUE +readline_s_get_filename_quote_characters(VALUE self, VALUE str) +{ +    if (rl_filename_quote_characters == NULL) +        return Qnil; +    return rb_locale_str_new_cstr(rl_filename_quote_characters); +} +#else +#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. + */ +static VALUE +readline_s_refresh_line(VALUE self) +{ +    prepare_readline(); +    rl_refresh_line(0, 0); +    return Qnil; +} +#else +#define readline_s_refresh_line rb_f_notimplement +#endif + +static VALUE +hist_to_s(VALUE self) +{ +    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, VALUE index) +{ +    HIST_ENTRY *entry = NULL; +    int i; + +    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_locale_str_new_cstr(entry->line); +} + +#ifdef HAVE_REPLACE_HISTORY_ENTRY +static VALUE +hist_set(VALUE self, VALUE index, VALUE str) +{ +    HIST_ENTRY *entry = NULL; +    int i; + +    i = NUM2INT(index); +    OutputStringValue(str); +    if (i < 0) { +        i += history_length; +    } +    if (i >= 0) { +        entry = replace_history_entry(history_replace_offset_func(i), RSTRING_PTR(str), NULL); +    } +    if (entry == NULL) { +        rb_raise(rb_eIndexError, "invalid index"); +    } +    return str; +} +#else +#define hist_set rb_f_notimplement +#endif + +static VALUE +hist_push(VALUE self, VALUE str) +{ +    OutputStringValue(str); +    add_history(RSTRING_PTR(str)); +    return self; +} + +static VALUE +hist_push_method(int argc, VALUE *argv, VALUE self) +{ +    VALUE str; + +    while (argc--) { +        str = *argv++; +        OutputStringValue(str); +        add_history(RSTRING_PTR(str)); +    } +    return self; +} + +static VALUE +rb_remove_history(int index) +{ +#ifdef HAVE_REMOVE_HISTORY +    HIST_ENTRY *entry; +    VALUE val; + +    entry = remove_history(index); +    if (entry) { +        val = rb_locale_str_new_cstr(entry->line); +        free((void *) entry->line); +        free(entry); +        return val; +    } +    return Qnil; +#else +    rb_notimplement(); + +    UNREACHABLE; +#endif +} + +static VALUE +hist_pop(VALUE self) +{ +    if (history_length > 0) { +        return rb_remove_history(history_length - 1); +    } else { +        return Qnil; +    } +} + +static VALUE +hist_shift(VALUE self) +{ +    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); + +    for (i = 0; i < history_length; i++) { +        entry = history_get(history_get_offset_func(i)); +        if (entry == NULL) +            break; +        rb_yield(rb_locale_str_new_cstr(entry->line)); +    } +    return self; +} + +static VALUE +hist_length(VALUE self) +{ +    return INT2NUM(history_length); +} + +static VALUE +hist_empty_p(VALUE self) +{ +    return history_length == 0 ? Qtrue : Qfalse; +} + +static VALUE +hist_delete_at(VALUE self, VALUE index) +{ +    int i; + +    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); +} + +#ifdef HAVE_CLEAR_HISTORY +static VALUE +hist_clear(VALUE self) +{ +    clear_history(); +    return self; +} +#else +#define hist_clear rb_f_notimplement +#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_locale_str_new_cstr(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_locale_str_new_cstr(matches[i])); +            free(matches[i]); +        } +        free(matches); +        if (RARRAY_LEN(result) >= 2) +            rb_ary_shift(result); +    } +    else { +        result = Qnil; +    } +    return result; +} + +void +Init_readline(void) +{ +    VALUE history, fcomp, ucomp, version; + +    /* Allow conditional parsing of the ~/.inputrc file. */ +    rl_readline_name = (char *)"Ruby"; + +#if defined HAVE_RL_GETC_FUNCTION +    /* libedit check rl_getc_function only when rl_initialize() is called, */ +    /* and using_history() call rl_initialize(). */ +    /* This assignment should be placed before using_history() */ +    rl_getc_function = readline_getc; +#elif defined HAVE_RL_EVENT_HOOK +    rl_event_hook = readline_event; +#endif + +    using_history(); + +    completion_proc = rb_intern(COMPLETION_PROC); +    completion_case_fold = rb_intern(COMPLETION_CASE_FOLD); +#if defined(HAVE_RL_PRE_INPUT_HOOK) +    id_pre_input_hook = rb_intern("pre_input_hook"); +#endif +#if defined(HAVE_RL_SPECIAL_PREFIXES) +    id_special_prefixes = rb_intern("special_prefixes"); +#endif + +    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, "line_buffer", +                               readline_s_get_line_buffer, 0); +    rb_define_singleton_method(mReadline, "point", +                               readline_s_get_point, 0); +    rb_define_singleton_method(mReadline, "point=", +                               readline_s_set_point, 1); +    rb_define_singleton_method(mReadline, "set_screen_size", +                               readline_s_set_screen_size, 2); +    rb_define_singleton_method(mReadline, "get_screen_size", +                               readline_s_get_screen_size, 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); +    rb_define_singleton_method(mReadline, "refresh_line", +                               readline_s_refresh_line, 0); +    rb_define_singleton_method(mReadline, "pre_input_hook=", +                               readline_s_set_pre_input_hook, 1); +    rb_define_singleton_method(mReadline, "pre_input_hook", +                               readline_s_get_pre_input_hook, 0); +    rb_define_singleton_method(mReadline, "insert_text", +                               readline_s_insert_text, 1); +    rb_define_singleton_method(mReadline, "delete_text", +                               readline_s_delete_text, -1); +    rb_define_singleton_method(mReadline, "redisplay", +                               readline_s_redisplay, 0); +    rb_define_singleton_method(mReadline, "special_prefixes=", +                               readline_s_set_special_prefixes, 1); +    rb_define_singleton_method(mReadline, "special_prefixes", +                               readline_s_get_special_prefixes, 0); + +#if USE_INSERT_IGNORE_ESCAPE +    CONST_ID(id_orig_prompt, "orig_prompt"); +    CONST_ID(id_last_prompt, "last_prompt"); +#endif + +    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; +    history_replace_offset_func = history_get_offset_0; +#if defined HAVE_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; +        } +#ifdef HAVE_REPLACE_HISTORY_ENTRY +        if (replace_history_entry(0, "a", NULL) == NULL) { +            history_replace_offset_func = history_get_offset_history_base; +        } +#endif +#ifdef HAVE_CLEAR_HISTORY +        clear_history(); +#else +        { +            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; +#if defined(HAVE_RL_PRE_INPUT_HOOK) +    rl_pre_input_hook = (rl_hook_func_t *)readline_pre_input_hook; +#endif +#ifdef HAVE_RL_CATCH_SIGNALS +    rl_catch_signals = 0; +#endif +#ifdef HAVE_RL_CLEAR_SIGNALS +    rl_clear_signals(); +#endif + +    rb_gc_register_address(&readline_instream); +    rb_gc_register_address(&readline_outstream); +} | 
