text: /************************************************
  
    readline.c - GNU Readline module
  
    $Author: nagachika $
    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 45772 2014-05-01 11:55:03Z nagachika $
  
    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 "ruby/ruby.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;
  }
  
  /*
   * 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);
      }
  
      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");
          }
      }
  
  #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 = (VALUE)&fakestr;
  
          fakestr.basic.flags = T_STRING | RSTRING_NOEMBED;
          fakestr.as.heap.ptr = ptr;
          fakestr.as.heap.len = len;
          rb_enc_associate(str, 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)
  {
      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()
  {
      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);
  }
  