# rbreadline.rb -- a general facility for reading lines of input
#   with emacs style editing and completion.

#
#  Inspired by GNU Readline, translation to Ruby
#  Copyright (C) 2009 by Park Heesob phasis@gmail.com
#

class Fixnum
   def ord; self; end
end

module RbReadline
   require 'etc'

   RL_LIBRARY_VERSION = "5.2"
   RL_READLINE_VERSION  = 0x0502
   RB_READLINE_VERSION = "0.4.0"

   EOF = "\xFF"
   ESC = "\C-["
   PAGE = "\C-L"
   SPACE = "\x20"
   RETURN = "\C-M"
   ABORT_CHAR = "\C-G"
   TAB = "\t"
   RUBOUT = "\x7f"
   NEWLINE = "\n"

   DEFAULT_BUFFER_SIZE = 256
   DEFAULT_MAX_KILLS = 10

   MB_FIND_NONZERO = 1
   MB_FIND_ANY = 0
   MB_LEN_MAX = 4

   DEFAULT_INPUTRC = "~/.inputrc"
   SYS_INPUTRC = "/etc/inputrc"

   UpCase = 1
   DownCase = 2
   CapCase = 3

   # Possible history errors passed to hist_error.
   EVENT_NOT_FOUND = 0
   BAD_WORD_SPEC  = 1
   SUBST_FAILED   = 2
   BAD_MODIFIER   = 3
   NO_PREV_SUBST  = 4

   # Possible definitions for history starting point specification.
   ANCHORED_SEARCH = 1
   NON_ANCHORED_SEARCH = 0

   # Possible definitions for what style of writing the history file we want.
   HISTORY_APPEND = 0
   HISTORY_OVERWRITE = 1

   # Input error; can be returned by (*rl_getc_function) if readline is reading
   #   a top-level command (RL_ISSTATE (RL_STATE_READCMD)).
   READERR = 0xFE.chr

   # Definitions available for use by readline clients.
   RL_PROMPT_START_IGNORE    = 1.chr
   RL_PROMPT_END_IGNORE    = 2.chr

   # Possible values for do_replace argument to rl_filename_quoting_function,
   #   called by rl_complete_internal.
   NO_MATCH      =  0
   SINGLE_MATCH  =  1
   MULT_MATCH    =  2

   # Callback data for reading numeric arguments
   NUM_SAWMINUS   = 0x01
   NUM_SAWDIGITS = 0x02
   NUM_READONE = 0x04

   # A context for reading key sequences longer than a single character when
   #   using the callback interface.
   KSEQ_DISPATCHED = 0x01
   KSEQ_SUBSEQ = 0x02
   KSEQ_RECURSIVE = 0x04

   # Possible state values for rl_readline_state
   RL_STATE_NONE        = 0x000000        # no state before first call

   RL_STATE_INITIALIZING =    0x000001    # initializing
   RL_STATE_INITIALIZED  =    0x000002    # initialization done
   RL_STATE_TERMPREPPED  =    0x000004    # terminal is prepped
   RL_STATE_READCMD =    0x000008    # reading a command key
   RL_STATE_METANEXT =    0x000010    # reading input after ESC
   RL_STATE_DISPATCHING =    0x000020    # dispatching to a command
   RL_STATE_MOREINPUT    = 0x000040    # reading more input in a command function
   RL_STATE_ISEARCH    = 0x000080    # doing incremental search
   RL_STATE_NSEARCH    = 0x000100    # doing non-inc search
   RL_STATE_SEARCH        = 0x000200    # doing a history search
   RL_STATE_NUMERICARG    = 0x000400    # reading numeric argument
   RL_STATE_MACROINPUT    = 0x000800    # getting input from a macro
   RL_STATE_MACRODEF    = 0x001000    # defining keyboard macro
   RL_STATE_OVERWRITE    = 0x002000    # overwrite mode
   RL_STATE_COMPLETING    = 0x004000    # doing completion
   RL_STATE_SIGHANDLER    = 0x008000    # in readline sighandler
   RL_STATE_UNDOING    = 0x010000    # doing an undo
   RL_STATE_INPUTPENDING =    0x020000    # rl_execute_next called
   RL_STATE_TTYCSAVED     = 0x040000    # tty special chars saved
   RL_STATE_CALLBACK     = 0x080000    # using the callback interface
   RL_STATE_VIMOTION     = 0x100000    # reading vi motion arg
   RL_STATE_MULTIKEY     = 0x200000    # reading multiple-key command
   RL_STATE_VICMDONCE     = 0x400000    # entered vi command mode at least once

   RL_STATE_DONE        = 0x800000    # done accepted line

   NO_BELL = 0
   AUDIBLE_BELL = 1
   VISIBLE_BELL = 2
   # The actions that undo knows how to undo.  Notice that UNDO_DELETE means
   #   to insert some text, and UNDO_INSERT means to delete some text.   I.e.,
   #   the code tells undo what to undo, not how to undo it.
   UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END  = 0,1,2,3

   # Definitions used when searching the line for characters.
   # NOTE: it is necessary that opposite directions are inverses
   FTO   =  1     # forward to
   BTO   = -1     # backward to
   FFIND =  2     # forward find
   BFIND = -2     # backward find

   # Possible values for the found_quote flags word used by the completion
   #   functions.  It says what kind of (shell-like) quoting we found anywhere
   #   in the line.
   RL_QF_SINGLE_QUOTE   = 0x01
   RL_QF_DOUBLE_QUOTE   = 0x02
   RL_QF_BACKSLASH      = 0x04
   RL_QF_OTHER_QUOTE = 0x08

   KEYMAP_SIZE = 257
   ANYOTHERKEY = KEYMAP_SIZE-1

   @hConsoleHandle = nil
   @MessageBeep = nil

   RL_IM_INSERT    = 1
   RL_IM_OVERWRITE    = 0
   RL_IM_DEFAULT    = RL_IM_INSERT
   @no_mode = -1
   @vi_mode = 0
   @emacs_mode = 1
   ISFUNC = 0
   ISKMAP = 1
   ISMACR = 2

   HISTORY_WORD_DELIMITERS    = " \t\n;&()|<>"
   HISTORY_QUOTE_CHARACTERS   = "\"'`"

   RL_SEARCH_ISEARCH = 0x01      # incremental search
   RL_SEARCH_NSEARCH = 0x02      # non-incremental search
   RL_SEARCH_CSEARCH = 0x04      # intra-line char search

   # search flags
   SF_REVERSE     = 0x01
   SF_FOUND    = 0x02
   SF_FAILED      = 0x04

   @slashify_in_quotes = "\\`\"$"

   @sigint_proc = nil
   @sigint_blocked = false

   @rl_prep_term_function = :rl_prep_terminal
   @rl_deprep_term_function = :rl_deprep_terminal

   @_rl_history_saved_point = -1

   @rl_max_kills =  DEFAULT_MAX_KILLS
   @rl_kill_ring = nil
   @rl_kill_index = 0
   @rl_kill_ring_length = 0

   @pending_bytes = ''
   @stored_count = 0

   @_rl_isearch_terminators = nil
   @_rl_iscxt = nil
   @last_isearch_string = nil
   @last_isearch_string_len = 0
   @default_isearch_terminators = "\033\012"
   @_rl_history_preserve_point = false
   @terminal_prepped = false
   @otio = nil

   @msg_saved_prompt = false

   @_rl_nscxt = nil
   @noninc_search_string = nil
   @noninc_history_pos = 0
   @prev_line_found = nil

   rl_history_search_len = 0
   rl_history_search_pos = 0
   history_search_string = nil
   history_string_size = 0

   @_rl_tty_chars = Struct.new(:t_eol,:t_eol2,:t_erase,:t_werase,:t_kill,:t_reprint,:t_intr,:t_eof,
   :t_quit,:t_susp,:t_dsusp,:t_start,:t_stop,:t_lnext,:t_flush,:t_status).new
   @_rl_last_tty_chars = nil

   @_keyboard_input_timeout = 0.001

   # Variables exported by this file.
   # The character that represents the start of a history expansion
   #   request.  This is usually `!'.
   @history_expansion_char = "!"

   # The character that invokes word substitution if found at the start of
   #   a line.  This is usually `^'.
   @history_subst_char = "^"

   # During tokenization, if this character is seen as the first character
   #   of a word, then it, and all subsequent characters upto a newline are
   #   ignored.  For a Bourne shell, this should be '#'.  Bash special cases
   #   the interactive comment character to not be a comment delimiter.
   @history_comment_char = 0.chr

   # The list of characters which inhibit the expansion of text if found
   #   immediately following history_expansion_char.
   @history_no_expand_chars = " \t\n\r="

   # If set to a non-zero value, single quotes inhibit history expansion.
   #   The default is 0.
   @history_quotes_inhibit_expansion = 0

   # Used to split words by history_tokenize_internal.
   @history_word_delimiters = HISTORY_WORD_DELIMITERS

   # If set, this points to a function that is called to verify that a
   #   particular history expansion should be performed.
   @history_inhibit_expansion_function = nil

   @rl_event_hook = nil
   # The visible cursor position.  If you print some text, adjust this.
   # NOTE: _rl_last_c_pos is used as a buffer index when not in a locale
   #   supporting multibyte characters, and an absolute cursor position when
   #   in such a locale.  This is an artifact of the donated multibyte support.
   #   Care must be taken when modifying its value.
   @_rl_last_c_pos = 0
   @_rl_last_v_pos = 0

   @cpos_adjusted = false
   @cpos_buffer_position = 0

   # Number of lines currently on screen minus 1.
   @_rl_vis_botlin = 0

   # Variables used only in this file.
   # The last left edge of text that was displayed.  This is used when
   #   doing horizontal scrolling.  It shifts in thirds of a screenwidth.
   @last_lmargin = 0

   # The line display buffers.  One is the line currently displayed on
   #   the screen.  The other is the line about to be displayed.
   @visible_line = nil
   @invisible_line = nil

   # A buffer for `modeline' messages.
   @msg_buf = 0.chr * 128

   # Non-zero forces the redisplay even if we thought it was unnecessary.
   @forced_display = false

   # Default and initial buffer size.  Can grow.
   @line_size = 1024

   # Variables to keep track of the expanded prompt string, which may
   #   include invisible characters.

   @local_prompt = nil
   @local_prompt_prefix = nil
   @local_prompt_len = 0
   @prompt_visible_length = 0
   @prompt_prefix_length = 0

   # The number of invisible characters in the line currently being
   #   displayed on the screen.
   @visible_wrap_offset = 0

   # The number of invisible characters in the prompt string.  Static so it
   #   can be shared between rl_redisplay and update_line
   @wrap_offset = 0

   @prompt_last_invisible = 0

   # The length (buffer offset) of the first line of the last (possibly
   #   multi-line) buffer displayed on the screen.
   @visible_first_line_len = 0

   # Number of invisible characters on the first physical line of the prompt.
   #   Only valid when the number of physical characters in the prompt exceeds
   #   (or is equal to) _rl_screenwidth.
   @prompt_invis_chars_first_line = 0

   @prompt_last_screen_line = 0

   @prompt_physical_chars = 0

   # Variables to save and restore prompt and display information.

   # These are getting numerous enough that it's time to create a struct.

   @saved_local_prompt = nil
   @saved_local_prefix = nil
   @saved_last_invisible =  0
   @saved_visible_length = 0
   @saved_prefix_length = 0
   @saved_local_length = 0
   @saved_invis_chars_first_line = 0
   @saved_physical_chars = 0

   @inv_lbreaks = nil
   @vis_lbreaks = nil
   @_rl_wrapped_line = nil

   @term_buffer = nil
   @term_string_buffer = nil

   @tcap_initialized = false

   # While we are editing the history, this is the saved
   #   version of the original line.
   @_rl_saved_line_for_history = nil

   # An array of HIST_ENTRY.  This is where we store the history.
   @the_history = nil
   @history_base = 1

   # Non-zero means that we have enforced a limit on the amount of
   #   history that we save.
   @history_stifled = false

   # If HISTORY_STIFLED is non-zero, then this is the maximum number of
   #   entries to remember.
   @history_max_entries = 0
   @max_input_history = 0  # backwards compatibility

   # The current location of the interactive history pointer.  Just makes
   #   life easier for outside callers.
   @history_offset = 0

   # The number of strings currently stored in the history list.
   @history_length = 0

   @_rl_vi_last_command = 'i' # default `.' puts you in insert mode

   # Non-zero means enter insertion mode.
   @_rl_vi_doing_insert = 0

   # Command keys which do movement for xxx_to commands.
   @vi_motion = " hl^$0ftFT;,%wbeWBE|"

   # Keymap used for vi replace characters.  Created dynamically since
   #   rarely used.
   @vi_replace_map = nil

   # The number of characters inserted in the last replace operation.
   @vi_replace_count = 0

   # If non-zero, we have text inserted after a c[motion] command that put
   #   us implicitly into insert mode.  Some people want this text to be
   #   attached to the command so that it is `redoable' with `.'.
   @vi_continued_command = false
   @vi_insert_buffer = nil
   @vi_insert_buffer_size = 0

   @_rl_vi_last_repeat = 1
   @_rl_vi_last_arg_sign = 1
   @_rl_vi_last_motion = 0
   @_rl_vi_last_search_char = 0
   @_rl_vi_last_replacement = 0

   @_rl_vi_last_key_before_insert = 0

   @vi_redoing = 0

   # Text modification commands.  These are the `redoable' commands.
   @vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~"

   # Arrays for the saved marks.
   @vi_mark_chars = Array.new(26,-1)

   @emacs_standard_keymap = {
      "\C-@" => :rl_set_mark  ,
      "\C-a" => :rl_beg_of_line  ,
      "\C-b" => :rl_backward_char  ,
      "\C-d" => :rl_delete  ,
      "\C-e" => :rl_end_of_line  ,
      "\C-f" => :rl_forward_char  ,
      "\C-g" => :rl_abort  ,
      "\C-h" => :rl_rubout  ,
      "\C-i" => :rl_complete  ,
      "\C-j" => :rl_newline  ,
      "\C-k" => :rl_kill_line  ,
      "\C-l" => :rl_clear_screen  ,
      "\C-m" => :rl_newline  ,
      "\C-n" => :rl_get_next_history  ,
      "\C-p" => :rl_get_previous_history  ,
      "\C-q" => :rl_quoted_insert  ,
      "\C-r" => :rl_reverse_search_history  ,
      "\C-s" => :rl_forward_search_history  ,
      "\C-t" => :rl_transpose_chars  ,
      "\C-u" => :rl_unix_line_discard  ,
      "\C-v" => :rl_quoted_insert  ,
      "\C-w" => :rl_unix_word_rubout  ,
      "\C-y" => :rl_yank  ,
      "\C-]" => :rl_char_search  ,
      "\C-_" => :rl_undo_command  ,
      "\x7F" => :rl_rubout ,
      "\e\C-g" => :rl_abort  ,
      "\e\C-h" => :rl_backward_kill_word  ,
      "\e\C-i" => :rl_tab_insert  ,
      "\e\C-j" => :rl_vi_editing_mode  ,
      "\e\C-m" => :rl_vi_editing_mode  ,
      "\e\C-r" => :rl_revert_line  ,
      "\e\C-y" => :rl_yank_nth_arg  ,
      "\e\C-[" => :rl_complete  ,
      "\e\C-]" => :rl_backward_char_search  ,
      "\e " => :rl_set_mark  ,
      "\e#" => :rl_insert_comment  ,
      "\e&" => :rl_tilde_expand  ,
      "\e*" => :rl_insert_completions  ,
      "\e-" => :rl_digit_argument  ,
      "\e." => :rl_yank_last_arg ,
      "\e0" => :rl_digit_argument  ,
      "\e1" => :rl_digit_argument  ,
      "\e2" => :rl_digit_argument  ,
      "\e3" => :rl_digit_argument  ,
      "\e4" => :rl_digit_argument  ,
      "\e5" => :rl_digit_argument  ,
      "\e6" => :rl_digit_argument  ,
      "\e7" => :rl_digit_argument  ,
      "\e8" => :rl_digit_argument  ,
      "\e9" => :rl_digit_argument  ,
      "\e<" => :rl_beginning_of_history  ,
      "\e=" => :rl_possible_completions  ,
      "\e>" => :rl_end_of_history  ,
      "\e?" => :rl_possible_completions  ,
      "\eB" => :rl_backward_word  ,
      "\eC" => :rl_capitalize_word  ,
      "\eD" => :rl_kill_word  ,
      "\eF" => :rl_forward_word  ,
      "\eL" => :rl_downcase_word  ,
      "\eN" => :rl_noninc_forward_search  ,
      "\eP" => :rl_noninc_reverse_search  ,
      "\eR" => :rl_revert_line  ,
      "\eT" => :rl_transpose_words  ,
      "\eU" => :rl_upcase_word  ,
      "\eY" => :rl_yank_pop  ,
      "\e\\" => :rl_delete_horizontal_space  ,
      "\e_" => :rl_yank_last_arg  ,
      "\eb" => :rl_backward_word  ,
      "\ec" => :rl_capitalize_word  ,
      "\ed" => :rl_kill_word  ,
      "\ef" => :rl_forward_word  ,
      "\el" => :rl_downcase_word  ,
      "\en" => :rl_noninc_forward_search  ,
      "\ep" => :rl_noninc_reverse_search  ,
      "\er" => :rl_revert_line  ,
      "\et" => :rl_transpose_words  ,
      "\eu" => :rl_upcase_word  ,
      "\ey" => :rl_yank_pop  ,
      "\e~" => :rl_tilde_expand  ,
      "\377" => :rl_backward_kill_word  ,

      "\C-x\C-g" => :rl_abort  ,
      "\C-x\C-r" => :rl_re_read_init_file  ,
      "\C-x\C-u" => :rl_undo_command  ,
      "\C-x\C-x" => :rl_exchange_point_and_mark  ,
      "\C-x(" => :rl_start_kbd_macro  ,
      "\C-x)" => :rl_end_kbd_macro   ,
      "\C-xE" => :rl_call_last_kbd_macro  ,
      "\C-xe" => :rl_call_last_kbd_macro  ,
      "\C-x\x7F" => :rl_backward_kill_line
   }

   @vi_movement_keymap = {
      "\C-d" => :rl_vi_eof_maybe ,
      "\C-e" => :rl_emacs_editing_mode ,
      "\C-g" => :rl_abort ,
      "\C-h" => :rl_backward_char ,
      "\C-j" => :rl_newline ,
      "\C-k" => :rl_kill_line ,
      "\C-l" => :rl_clear_screen ,
      "\C-m" => :rl_newline ,
      "\C-n" => :rl_get_next_history ,
      "\C-p" => :rl_get_previous_history ,
      "\C-q" => :rl_quoted_insert ,
      "\C-r" => :rl_reverse_search_history ,
      "\C-s" => :rl_forward_search_history ,
      "\C-t" => :rl_transpose_chars ,
      "\C-u" => :rl_unix_line_discard ,
      "\C-v" => :rl_quoted_insert ,
      "\C-w" => :rl_unix_word_rubout ,
      "\C-y" => :rl_yank ,
      "\C-_" => :rl_vi_undo ,
      " " => :rl_forward_char ,
      "#" => :rl_insert_comment ,
      "$" => :rl_end_of_line ,
      "%" => :rl_vi_match ,
      "&" => :rl_vi_tilde_expand ,
      "*" => :rl_vi_complete ,
      "+" => :rl_get_next_history ,
      "," => :rl_vi_char_search ,
      "-" => :rl_get_previous_history ,
      "." => :rl_vi_redo ,
      "/" => :rl_vi_search ,
      "0" => :rl_beg_of_line ,
      "1" => :rl_vi_arg_digit ,
      "2" => :rl_vi_arg_digit ,
      "3" => :rl_vi_arg_digit ,
      "4" => :rl_vi_arg_digit ,
      "5" => :rl_vi_arg_digit ,
      "6" => :rl_vi_arg_digit ,
      "7" => :rl_vi_arg_digit ,
      "8" => :rl_vi_arg_digit ,
      "9" => :rl_vi_arg_digit ,
      "" => :rl_vi_char_search ,
      "=" => :rl_vi_complete ,
      "?" => :rl_vi_search ,
      "A" => :rl_vi_append_eol ,
      "B" => :rl_vi_prev_word ,
      "C" => :rl_vi_change_to ,
      "D" => :rl_vi_delete_to ,
      "E" => :rl_vi_end_word ,
      "F" => :rl_vi_char_search ,
      "G" => :rl_vi_fetch_history ,
      "I" => :rl_vi_insert_beg ,
      "N" => :rl_vi_search_again ,
      "P" => :rl_vi_put ,
      "R" => :rl_vi_replace ,
      "S" => :rl_vi_subst ,
      "T" => :rl_vi_char_search ,
      "U" => :rl_revert_line ,
      "W" => :rl_vi_next_word ,
      "X" => :rl_vi_rubout ,
      "Y" => :rl_vi_yank_to ,
      "\\" => :rl_vi_complete ,
      "^" => :rl_vi_first_print ,
      "_" => :rl_vi_yank_arg ,
      "`" => :rl_vi_goto_mark ,
      "a" => :rl_vi_append_mode ,
      "b" => :rl_vi_prev_word ,
      "c" => :rl_vi_change_to ,
      "d" => :rl_vi_delete_to ,
      "e" => :rl_vi_end_word ,
      "f" => :rl_vi_char_search ,
      "h" => :rl_backward_char ,
      "i" => :rl_vi_insertion_mode ,
      "j" => :rl_get_next_history ,
      "k" => :rl_get_previous_history ,
      "l" => :rl_forward_char ,
      "m" => :rl_vi_set_mark ,
      "n" => :rl_vi_search_again ,
      "p" => :rl_vi_put ,
      "r" => :rl_vi_change_char ,
      "s" => :rl_vi_subst ,
      "t" => :rl_vi_char_search ,
      "u" => :rl_vi_undo ,
      "w" => :rl_vi_next_word ,
      "x" => :rl_vi_delete ,
      "y" => :rl_vi_yank_to ,
      "|" => :rl_vi_column ,
      "~" => :rl_vi_change_case
   }

   @vi_insertion_keymap = {
      "\C-a" => :rl_insert  ,
      "\C-b" => :rl_insert  ,
      "\C-c" => :rl_insert  ,
      "\C-d" => :rl_vi_eof_maybe  ,
      "\C-e" => :rl_insert  ,
      "\C-f" => :rl_insert  ,
      "\C-g" => :rl_insert  ,
      "\C-h" => :rl_rubout  ,
      "\C-i" => :rl_complete  ,
      "\C-j" => :rl_newline  ,
      "\C-k" => :rl_insert  ,
      "\C-l" => :rl_insert  ,
      "\C-m" => :rl_newline  ,
      "\C-n" => :rl_insert  ,
      "\C-o" => :rl_insert  ,
      "\C-p" => :rl_insert  ,
      "\C-q" => :rl_insert  ,
      "\C-r" => :rl_reverse_search_history  ,
      "\C-s" => :rl_forward_search_history  ,
      "\C-t" => :rl_transpose_chars  ,
      "\C-u" => :rl_unix_line_discard  ,
      "\C-v" => :rl_quoted_insert  ,
      "\C-w" => :rl_unix_word_rubout  ,
      "\C-x" => :rl_insert  ,
      "\C-y" => :rl_yank  ,
      "\C-z" => :rl_insert  ,
      "\C-[" => :rl_vi_movement_mode  ,
      "\C-\\" => :rl_insert  ,
      "\C-]" => :rl_insert  ,
      "\C-^" => :rl_insert  ,
      "\C-_" => :rl_vi_undo  ,
      "\x7F" => :rl_rubout
   }

   @rl_library_version = RL_LIBRARY_VERSION

   @rl_readline_version = RL_READLINE_VERSION

   @rl_readline_name = "other"

   @rl_getc_function = :rl_getc

   # Non-zero tells rl_delete_text and rl_insert_text to not add to
   #   the undo list.
   @_rl_doing_an_undo = false

   # How many unclosed undo groups we currently have.
   @_rl_undo_group_level = 0

   # The current undo list for THE_LINE.
   @rl_undo_list = nil

   # Application-specific redisplay function.
   @rl_redisplay_function = :rl_redisplay

   # Global variables declared here.
   # What YOU turn on when you have handled all redisplay yourself.
   @rl_display_fixed = false

   @_rl_suppress_redisplay = 0
   @_rl_want_redisplay = false

   # The stuff that gets printed out before the actual text of the line.
   #   This is usually pointing to rl_prompt.
   @rl_display_prompt = nil

   # True if this is `real' readline as opposed to some stub substitute.
   @rl_gnu_readline_p = true

   for i in 32 .. 255
      @emacs_standard_keymap[i.chr] = :rl_insert unless @emacs_standard_keymap[i.chr]
      @vi_insertion_keymap[i.chr] = :rl_insert unless @vi_insertion_keymap[i.chr]
   end
   # A pointer to the keymap that is currently in use.
   #   By default, it is the standard emacs keymap.
   @_rl_keymap = @emacs_standard_keymap


   # The current style of editing.
   @rl_editing_mode = @emacs_mode

   # The current insert mode:  input (the default) or overwrite
   @rl_insert_mode = RL_IM_DEFAULT

   # Non-zero if we called this function from _rl_dispatch().  It's present
   #   so functions can find out whether they were called from a key binding
   #   or directly from an application.
   @rl_dispatching = false

   # Non-zero if the previous command was a kill command.
   @_rl_last_command_was_kill = false

   # The current value of the numeric argument specified by the user.
   @rl_numeric_arg = 1

   # Non-zero if an argument was typed.
   @rl_explicit_arg = false

   # Temporary value used while generating the argument.
   @rl_arg_sign = 1

   # Non-zero means we have been called at least once before.
   @rl_initialized = false

   # Flags word encapsulating the current readline state.
   @rl_readline_state = RL_STATE_NONE

   # The current offset in the current input line.
   @rl_point = 0

   # Mark in the current input line.
   @rl_mark = 0

   # Length of the current input line.
   @rl_end = 0

   # Make this non-zero to return the current input_line.
   @rl_done = false

   # The last function executed by readline.
   @rl_last_func = nil

   # Top level environment for readline_internal ().
   @readline_top_level = nil

   # The streams we interact with.
   @_rl_in_stream = nil
   @_rl_out_stream = nil

   # The names of the streams that we do input and output to.
   @rl_instream = nil
   @rl_outstream = nil

   @pop_index = 0
   @push_index = 0
   @ibuffer = 0.chr * 512
   @ibuffer_len = @ibuffer.length - 1


   # Non-zero means echo characters as they are read.  Defaults to no echo
   #   set to 1 if there is a controlling terminal, we can get its attributes,
   #   and the attributes include `echo'.  Look at rltty.c:prepare_terminal_settings
   #   for the code that sets it.
   @readline_echoing_p = false

   # Current prompt.
   @rl_prompt = nil
   @rl_visible_prompt_length = 0

   # Set to non-zero by calling application if it has already printed rl_prompt
   #   and does not want readline to do it the first time.
   @rl_already_prompted = false

   # The number of characters read in order to type this complete command.
   @rl_key_sequence_length = 0

   # If non-zero, then this is the address of a function to call just
   #   before readline_internal_setup () prints the first prompt.
   @rl_startup_hook = nil

   # If non-zero, this is the address of a function to call just before
   #   readline_internal_setup () returns and readline_internal starts
   #   reading input characters.
   @rl_pre_input_hook = nil

   # The character that can generate an EOF.  Really read from
   #   the terminal driver... just defaulted here.
   @_rl_eof_char = "\cD"

   # Non-zero makes this the next keystroke to read.
   @rl_pending_input = 0

   # Pointer to a useful terminal name.
   @rl_terminal_name = nil

   # Non-zero means to always use horizontal scrolling in line display.
   @_rl_horizontal_scroll_mode = false

   # Non-zero means to display an asterisk at the starts of history lines
   #   which have been modified.
   @_rl_mark_modified_lines = false

   # The style of `bell' notification preferred.  This can be set to NO_BELL,
   #   AUDIBLE_BELL, or VISIBLE_BELL.
   @_rl_bell_preference = AUDIBLE_BELL

   # String inserted into the line by rl_insert_comment ().
   @_rl_comment_begin = nil

   # Keymap holding the function currently being executed.
   @rl_executing_keymap = nil

   # Keymap we're currently using to dispatch.
   @_rl_dispatching_keymap = nil

   # Non-zero means to erase entire line, including prompt, on empty input lines.
   @rl_erase_empty_line = false

   # Non-zero means to read only this many characters rather than up to a
   #   character bound to accept-line.
   @rl_num_chars_to_read = 0

   # Line buffer and maintenence.
   @rl_line_buffer = ""

   # Key sequence `contexts'
   @_rl_kscxt = nil

   # Non-zero means do not parse any lines other than comments and
   #   parser directives.
   @_rl_parsing_conditionalized_out = false

   # Non-zero means to convert characters with the meta bit set to
   #   escape-prefixed characters so we can indirect through
   #   emacs_meta_keymap or vi_escape_keymap.
   @_rl_convert_meta_chars_to_ascii = true

   # Non-zero means to output characters with the meta bit set directly
   #   rather than as a meta-prefixed escape sequence.
   @_rl_output_meta_chars = false

   # Non-zero means to look at the termios special characters and bind
   #   them to equivalent readline functions at startup.
   @_rl_bind_stty_chars = true

   @rl_completion_display_matches_hook = nil

   XOK = 1

   @_rl_term_clreol = nil
   @_rl_term_clrpag = nil
   @_rl_term_cr = nil
   @_rl_term_backspace = nil
   @_rl_term_goto = nil
   @_rl_term_pc = nil

   # Non-zero if we determine that the terminal can do character insertion.
   @_rl_terminal_can_insert = false

   # How to insert characters.
   @_rl_term_im = nil
   @_rl_term_ei = nil
   @_rl_term_ic = nil
   @_rl_term_ip = nil
   @_rl_term_IC = nil

   # How to delete characters.
   @_rl_term_dc = nil
   @_rl_term_DC = nil

   @_rl_term_forward_char = nil

   # How to go up a line.
   @_rl_term_up = nil

   # A visible bell; char if the terminal can be made to flash the screen.
   @_rl_visible_bell = nil

   # Non-zero means the terminal can auto-wrap lines.
   @_rl_term_autowrap = true

   # Non-zero means that this terminal has a meta key.
   @term_has_meta = 0

   # The sequences to write to turn on and off the meta key, if this
   #   terminal has one.
   @_rl_term_mm = nil
   @_rl_term_mo = nil

   # The key sequences output by the arrow keys, if this terminal has any.
   @_rl_term_ku = nil
   @_rl_term_kd = nil
   @_rl_term_kr = nil
   @_rl_term_kl = nil

   # How to initialize and reset the arrow keys, if this terminal has any.
   @_rl_term_ks = nil
   @_rl_term_ke = nil

   # The key sequences sent by the Home and End keys, if any.
   @_rl_term_kh = nil
   @_rl_term_kH = nil
   @_rl_term_at7 = nil

   # Delete key
   @_rl_term_kD = nil

   # Insert key
   @_rl_term_kI = nil

   # Cursor control
   @_rl_term_vs = nil    # very visible
   @_rl_term_ve = nil    # normal

   # Variables that hold the screen dimensions, used by the display code.
   @_rl_screenwidth = @_rl_screenheight = @_rl_screenchars = 0

   # Non-zero means the user wants to enable the keypad.
   @_rl_enable_keypad = false

   # Non-zero means the user wants to enable a meta key.
   @_rl_enable_meta = true

   # ****************************************************************
   #
   #    Completion matching, from readline's point of view.
   #
   # ****************************************************************

   # Variables known only to the readline library.

   # If non-zero, non-unique completions always show the list of matches.
   @_rl_complete_show_all = false

   # If non-zero, non-unique completions show the list of matches, unless it
   #   is not possible to do partial completion and modify the line.
   @_rl_complete_show_unmodified = false

   # If non-zero, completed directory names have a slash appended.
   @_rl_complete_mark_directories = true

   # If non-zero, the symlinked directory completion behavior introduced in
   #   readline-4.2a is disabled, and symlinks that point to directories have
   #   a slash appended (subject to the value of _rl_complete_mark_directories).
   #   This is user-settable via the mark-symlinked-directories variable.
   @_rl_complete_mark_symlink_dirs = false

   # If non-zero, completions are printed horizontally in alphabetical order,
   #   like `ls -x'.
   @_rl_print_completions_horizontally = false

   @_rl_completion_case_fold = false

   # If non-zero, don't match hidden files (filenames beginning with a `.' on
   #   Unix) when doing filename completion.
   @_rl_match_hidden_files = true

   # Global variables available to applications using readline.

   # Non-zero means add an additional character to each filename displayed
   #   during listing completion iff rl_filename_completion_desired which helps
   #   to indicate the type of file being listed.
   @rl_visible_stats = false

   # If non-zero, then this is the address of a function to call when
   #   completing on a directory name.  The function is called with
   #   the address of a string (the current directory name) as an arg.
   @rl_directory_completion_hook = nil

   @rl_directory_rewrite_hook = nil

   # Non-zero means readline completion functions perform tilde expansion.
   @rl_complete_with_tilde_expansion = false

   # Pointer to the generator function for completion_matches ().
   #   NULL means to use rl_filename_completion_function (), the default filename
   #   completer.
   @rl_completion_entry_function = nil

   # Pointer to alternative function to create matches.
   #   Function is called with TEXT, START, and END.
   #   START and END are indices in RL_LINE_BUFFER saying what the boundaries
   #   of TEXT are.
   #   If this function exists and returns NULL then call the value of
   #   rl_completion_entry_function to try to match, otherwise use the
   #   array of strings returned.
   @rl_attempted_completion_function = nil

   # Non-zero means to suppress normal filename completion after the
   #   user-specified completion function has been called.
   @rl_attempted_completion_over = false

   # Set to a character indicating the type of completion being performed
   #   by rl_complete_internal, available for use by application completion
   #   functions.
   @rl_completion_type = 0

   # Up to this many items will be displayed in response to a
   #   possible-completions call.  After that, we ask the user if
   #   she is sure she wants to see them all.  A negative value means
   #   don't ask.
   @rl_completion_query_items = 100

   @_rl_page_completions = 1

   # The basic list of characters that signal a break between words for the
   #   completer routine.  The contents of this variable is what breaks words
   #   in the shell, i.e. " \t\n\"\\'`@$><="
   @rl_basic_word_break_characters = " \t\n\"\\'`@$><=|&{(" # })

   # List of basic quoting characters.
   @rl_basic_quote_characters = "\"'"

   # The list of characters that signal a break between words for
   #   rl_complete_internal.  The default list is the contents of
   #   rl_basic_word_break_characters.
   @rl_completer_word_break_characters = nil

   # Hook function to allow an application to set the completion word
   #   break characters before readline breaks up the line.  Allows
   #   position-dependent word break characters.
   @rl_completion_word_break_hook = nil

   # List of characters which can be used to quote a substring of the line.
   #   Completion occurs on the entire substring, and within the substring
   #   rl_completer_word_break_characters are treated as any other character,
   #   unless they also appear within this list.
   @rl_completer_quote_characters = nil

   # List of characters that should be quoted in filenames by the completer.
   @rl_filename_quote_characters = nil

   # List of characters that are word break characters, but should be left
   #   in TEXT when it is passed to the completion function.  The shell uses
   #   this to help determine what kind of completing to do.
   @rl_special_prefixes = nil

   # If non-zero, then disallow duplicates in the matches.
   @rl_ignore_completion_duplicates = true

   # Non-zero means that the results of the matches are to be treated
   #   as filenames.  This is ALWAYS zero on entry, and can only be changed
   #   within a completion entry finder function.
   @rl_filename_completion_desired = false

   # Non-zero means that the results of the matches are to be quoted using
   #   double quotes (or an application-specific quoting mechanism) if the
   #   filename contains any characters in rl_filename_quote_chars.  This is
   #   ALWAYS non-zero on entry, and can only be changed within a completion
   #   entry finder function.
   @rl_filename_quoting_desired = true

   # This function, if defined, is called by the completer when real
   #   filename completion is done, after all the matching names have been
   #   generated. It is passed a (char**) known as matches in the code below.
   #   It consists of a NULL-terminated array of pointers to potential
   #   matching strings.  The 1st element (matches[0]) is the maximal
   #   substring that is common to all matches. This function can re-arrange
   #   the list of matches as required, but all elements of the array must be
   #   free()'d if they are deleted. The main intent of this function is
   #   to implement FIGNORE a la SunOS csh.
   @rl_ignore_some_completions_function = nil

   # Set to a function to quote a filename in an application-specific fashion.
   #   Called with the text to quote, the type of match found (single or multiple)
   #   and a pointer to the quoting character to be used, which the function can
   #   reset if desired.
   #rl_filename_quoting_function = rl_quote_filename

   # Function to call to remove quoting characters from a filename.  Called
   #   before completion is attempted, so the embedded quotes do not interfere
   #   with matching names in the file system.  Readline doesn't do anything
   #   with this it's set only by applications.
   @rl_filename_dequoting_function = nil

   # Function to call to decide whether or not a word break character is
   #   quoted.  If a character is quoted, it does not break words for the
   #   completer.
   @rl_char_is_quoted_p = nil

   # If non-zero, the completion functions don't append anything except a
   #   possible closing quote.  This is set to 0 by rl_complete_internal and
   #   may be changed by an application-specific completion function.
   @rl_completion_suppress_append = false

   # Character appended to completed words when at the end of the line.  The
   #   default is a space.
   @rl_completion_append_character = ' '

   # If non-zero, the completion functions don't append any closing quote.
   #   This is set to 0 by rl_complete_internal and may be changed by an
   #   application-specific completion function.
   @rl_completion_suppress_quote = false

   # Set to any quote character readline thinks it finds before any application
   #   completion function is called.
   @rl_completion_quote_character = 0

   # Set to a non-zero value if readline found quoting anywhere in the word to
   #   be completed set before any application completion function is called.
   @rl_completion_found_quote = false

   # If non-zero, a slash will be appended to completed filenames that are
   #  symbolic links to directory names, subject to the value of the
   #  mark-directories variable (which is user-settable).  This exists so
   #  that application completion functions can override the user's preference
   #  (set via the mark-symlinked-directories variable) if appropriate.
   #  It's set to the value of _rl_complete_mark_symlink_dirs in
   #  rl_complete_internal before any application-specific completion
   #  function is called, so without that function doing anything, the user's
   #  preferences are honored.
   @rl_completion_mark_symlink_dirs = false

   # If non-zero, inhibit completion (temporarily).
   @rl_inhibit_completion = false

   # Variables local to this file.

   # Local variable states what happened during the last completion attempt.
   @completion_changed_buffer = nil

   # Non-zero means treat 0200 bit in terminal input as Meta bit.
   @_rl_meta_flag = false

   # Stack of previous values of parsing_conditionalized_out.
   @if_stack = []
   @if_stack_depth = 0


   # The last key bindings file read.
   @last_readline_init_file = nil

   # The file we're currently reading key bindings from.
   @current_readline_init_file = nil
   @current_readline_init_include_level = 0
   @current_readline_init_lineno = 0

   ENV["HOME"] ||= ENV["HOMEDRIVE"]+ENV["HOMEPATH"]

   @directory = nil
   @filename = nil
   @dirname = nil
   @users_dirname = nil
   @filename_len = 0

   attr_accessor :rl_attempted_completion_function,:rl_deprep_term_function,
   :rl_event_hook,:rl_attempted_completion_over,:rl_basic_quote_characters,
   :rl_basic_word_break_characters,:rl_completer_quote_characters,
   :rl_completer_word_break_characters,:rl_completion_append_character,
   :rl_filename_quote_characters,:rl_instream,:rl_library_version,:rl_outstream,
   :rl_readline_name,:history_length,:history_base

   module_function
   # Okay, now we write the entry_function for filename completion.  In the
   #   general case.  Note that completion in the shell is a little different
   #   because of all the pathnames that must be followed when looking up the
   #   completion for a command.
   def rl_filename_completion_function(text, state)
      # If we don't have any state, then do some initialization.
      if (state == 0)
         # If we were interrupted before closing the directory or reading
         #all of its contents, close it.
         if(@directory)
            @directory.close
            @directory = nil
         end
         text.delete!(0.chr)
         @filename = text.dup
         if text.length == 0
            text = "."
         end
         @dirname = File.dirname(text)
         # We aren't done yet.  We also support the "~user" syntax.

         # Save the version of the directory that the user typed.
         @users_dirname = @dirname.dup

         if (@dirname[0,1] == '~')
            @dirname = File.expand_path(@dirname)
         end

         # The directory completion hook should perform any necessary
         #   dequoting.
         if (@rl_directory_completion_hook && send(rl_directory_completion_hook,@dirname))
            @users_dirname = @dirname.dup
         elsif (@rl_completion_found_quote && @rl_filename_dequoting_function)
            # delete single and double quotes
            @temp = send(@rl_filename_dequoting_function,@users_dirname, @rl_completion_quote_character)
            @users_dirname = temp
         end

         @directory = Dir.new(@dirname)

         # Now dequote a non-null filename.
         if (@filename && @filename.length>0 && @rl_completion_found_quote && @rl_filename_dequoting_function)
            # delete single and double quotes
            temp = send(@rl_filename_dequoting_function,@filename, @rl_completion_quote_character)
            @filename = temp
         end

         @filename_len = @filename.length
         @rl_filename_completion_desired = true
      end

      # At this point we should entertain the possibility of hacking wildcarded
      #   filenames, like /usr/man/man<WILD>/te<TAB>.  If the directory name
      #   contains globbing characters, then build an array of directories, and
      #   then map over that list while completing.
      # *** UNIMPLEMENTED ***

      # Now that we have some state, we can read the directory.
      entry = nil
      while(@directory && (entry = @directory.read))
         d_name = entry
         # Special case for no filename.  If the user has disabled the
         #   `match-hidden-files' variable, skip filenames beginning with `.'.
         #All other entries except "." and ".." match.
         if (@filename_len == 0)
            next if (!@_rl_match_hidden_files && d_name[0,1] == '.')
            break if (d_name != '.' && d_name != '..')
         else
            # Otherwise, if these match up to the length of filename, then
            #   it is a match.

            if (@_rl_completion_case_fold)
               break if d_name =~ /^#{Regexp.escape(@filename)}/i
            else
               break if d_name =~ /^#{Regexp.escape(@filename)}/
            end
         end
      end

      if entry.nil?
         if @directory
            @directory.close
            @directory = nil
         end
         @dirname = nil
         @filename = nil
         @users_dirname = nil

         return nil
      else
         if (@dirname != '.')
            if (@rl_complete_with_tilde_expansion && @users_dirname[0,1] == "~")
               temp = @dirname
               if(temp[-1,1] != '/')
                  temp += '/'
               end
            else
               temp = @users_dirname
               if(temp[-1,1] != '/')
                  temp += '/'
               end
            end
            temp += entry
         else
            temp = entry.dup
         end
         return (temp)
      end
   end

   # A completion function for usernames.
   #   TEXT contains a partial username preceded by a random
   #   character (usually `~').
   def rl_username_completion_function(text, state)
      return nil if RUBY_PLATFORM =~ /mswin|mingw/

      if (state == 0)
         first_char = text[0,1]
         first_char_loc = (first_char == '~' ? 1 : 0)

         username = text[first_char_loc..-1]
         namelen = username.length
         Etc.setpwent()
      end

      while (entry = Etc.getpwent())
         # Null usernames should result in all users as possible completions.
         break if (namelen == 0 || entry.name =~ /^#{username}/ )
      end

      if entry.nil?
         Etc.endpwent()
         return nil
      else
         value = text.dup
         value[first_char_loc..-1] = entry.name

         if (first_char == '~')
            @rl_filename_completion_desired = true
         end

         return (value)
      end
   end

   #*************************************************************
   #
   #  Application-callable completion match generator functions
   #
   #*************************************************************

   # Return an array of (char *) which is a list of completions for TEXT.
   #   If there are no completions, return a NULL pointer.
   #   The first entry in the returned array is the substitution for TEXT.
   #   The remaining entries are the possible completions.
   #   The array is terminated with a NULL pointer.
   #
   #   ENTRY_FUNCTION is a function of two args, and returns a (char *).
   #     The first argument is TEXT.
   #     The second is a state argument it should be zero on the first call, and
   #     non-zero on subsequent calls.  It returns a NULL pointer to the caller
   #     when there are no more matches.
   #
   def rl_completion_matches(text, entry_function)
      matches = 0
      match_list_size = 10
      match_list = []
      match_list[1] = nil
      while (string = send(entry_function, text, matches))
         match_list[matches+=1] = string
         match_list[matches+1] = nil
      end

      # If there were any matches, then look through them finding out the
      # lowest common denominator.  That then becomes match_list[0].
      if (matches!=0)
         compute_lcd_of_matches(match_list, matches, text)
      else                # There were no matches.
         match_list = nil
      end
      return (match_list)
   end

   def _rl_to_lower(char)
      char.nil? ? nil : char.chr.downcase
   end

   # Find the common prefix of the list of matches, and put it into
   #   matches[0].
   def compute_lcd_of_matches(match_list, matches, text)
      # If only one match, just use that.  Otherwise, compare each
      #   member of the list with the next, finding out where they
      #   stop matching.
      if (matches == 1)
         match_list[0] = match_list[1]
         match_list[1] = nil
         return 1
      end

      i = 1
      low = 100000
      while(i<matches)
         if (@_rl_completion_case_fold)
            si = 0
            while((c1 = _rl_to_lower(match_list[i][si])) &&
               (c2 = _rl_to_lower(match_list[i + 1][si])))
               if !@rl_byte_oriented
                  if(!_rl_compare_chars(match_list[i],si,match_list[i+1],si))
                     break
                  elsif ((v = _rl_get_char_len(match_list[i][si..-1])) > 1)
                     si += v - 1
                  end
               else
                  break if (c1 != c2)
               end
               si += 1
            end
         else
            si = 0
            while((c1 = match_list[i][si]) &&
               (c2 = match_list[i + 1][si]))
               if !@rl_byte_oriented
                  if(!_rl_compare_chars(match_list[i],si,match_list[i+1],si))
                     break
                  elsif ((v = _rl_get_char_len(match_list[i][si..-1])) > 1)
                     si += v - 1
                  end
               else
                  break if (c1 != c2)
               end
               si += 1
            end
         end

         if (low > si)
            low = si
         end
         i += 1
      end

      # If there were multiple matches, but none matched up to even the
      #   first character, and the user typed something, use that as the
      #   value of matches[0].
      if (low == 0 && text && text.length>0 )
         match_list[0] = text.dup
      else
         # XXX - this might need changes in the presence of multibyte chars

         # If we are ignoring case, try to preserve the case of the string
         # the user typed in the face of multiple matches differing in case.
         if (@_rl_completion_case_fold)

            # We're making an assumption here:
            #  IF we're completing filenames AND
            #     the application has defined a filename dequoting function AND
            #     we found a quote character AND
            #     the application has requested filename quoting
            #  THEN
            #     we assume that TEXT was dequoted before checking against
            #     the file system and needs to be dequoted here before we
            #     check against the list of matches
            #  FI
            if (@rl_filename_completion_desired &&
               @rl_filename_dequoting_function &&
               @rl_completion_found_quote &&
               @rl_filename_quoting_desired)

               dtext = send(@rl_filename_dequoting_function,text, @rl_completion_quote_character)
               text = dtext
            end

            # sort the list to get consistent answers.
            match_list = [match_list[0]] + match_list[1..-1].sort

            si = text.length
            if (si <= low)
               for i in 1 .. matches
                  if match_list[i][0,si] == text
                     match_list[0] = match_list[i][0,low]
                     break
                  end
                  # no casematch, use first entry
                  if (i > matches)
                     match_list[0] = match_list[1][0,low]
                  end
               end
            else
               # otherwise, just use the text the user typed.
               match_list[0] = text[0,low]
            end
         else
            match_list[0] = match_list[1][0,low]
         end
      end

      return matches
   end


   def rl_vi_editing_mode(count, key)
      _rl_set_insert_mode(RL_IM_INSERT, 1)    # vi mode ignores insert mode
      @rl_editing_mode = @vi_mode
      rl_vi_insertion_mode(1, key)
      0
   end

   # Switching from one mode to the other really just involves
   #   switching keymaps.
   def rl_vi_insertion_mode(count, key)
      @_rl_keymap = @vi_insertion_keymap
      @_rl_vi_last_key_before_insert = key
      0
   end

   def rl_emacs_editing_mode(count, key)
      @rl_editing_mode = @emacs_mode
      _rl_set_insert_mode(RL_IM_INSERT, 1) # emacs mode default is insert mode
      @_rl_keymap = @emacs_standard_keymap
      0
   end

   # Function for the rest of the library to use to set insert/overwrite mode.
   def _rl_set_insert_mode(im, force)
      @rl_insert_mode = im
   end

   # Toggle overwrite mode.  A positive explicit argument selects overwrite
   #   mode.  A negative or zero explicit argument selects insert mode.
   def rl_overwrite_mode(count, key)
      if (!@rl_explicit_arg)
         _rl_set_insert_mode(@rl_insert_mode ^ 1, 0)
      elsif (count > 0)
         _rl_set_insert_mode(RL_IM_OVERWRITE, 0)
      else
         _rl_set_insert_mode(RL_IM_INSERT, 0)
      end
      0
   end


   # A function for simple tilde expansion.
   def rl_tilde_expand(ignore, key)
      _end = @rl_point
      start = _end - 1

      if (@rl_point == @rl_end && @rl_line_buffer[@rl_point,1] == '~' )
         homedir = File.expand_path("~")
         _rl_replace_text(homedir, start, _end)
         return (0)
      elsif (@rl_line_buffer[start,1] != '~')
         while(!whitespace(@rl_line_buffer[start,1]) && start >= 0)
            start -= 1
         end
         start+=1
      end

      _end = start
      begin
         _end+=1
      end while(!whitespace(@rl_line_buffer[_end,1]) && _end < @rl_end)

      if (whitespace(@rl_line_buffer[_end,1]) || _end >= @rl_end)
         _end-=1
      end

      # If the first character of the current word is a tilde, perform
      #tilde expansion and insert the result.  If not a tilde, do
      #   nothing.
      if (@rl_line_buffer[start,1] == '~')

         len = _end - start + 1
         temp = @rl_line_buffer[start,len]
         homedir = File.expand_path(temp)
         temp = nil

         _rl_replace_text(homedir, start, _end)
      end
      0
   end

   # Clean up the terminal and readline state after catching a signal, before
   #   resending it to the calling application.
   def rl_cleanup_after_signal()
      _rl_clean_up_for_exit()
      if (@rl_deprep_term_function)
         send(@rl_deprep_term_function)
      end
      rl_clear_pending_input()
      rl_clear_signals()
   end

   def _rl_clean_up_for_exit()
      if @readline_echoing_p
         _rl_move_vert(@_rl_vis_botlin)
         @_rl_vis_botlin = 0
         @rl_outstream.flush
         rl_restart_output(1, 0)
      end
   end

   # Move the cursor from _rl_last_c_pos to NEW, which are buffer indices.
   #   (Well, when we don't have multibyte characters, _rl_last_c_pos is a
   #   buffer index.)
   #   DATA is the contents of the screen line of interest; i.e., where
   #   the movement is being done.
   def _rl_move_cursor_relative(new, data, start=0)

      woff = w_offset(@_rl_last_v_pos, @wrap_offset)
      cpos = @_rl_last_c_pos

      if !@rl_byte_oriented
         dpos = _rl_col_width(data, start, start+new)

         if (dpos > @prompt_last_invisible)     # XXX - don't use woff here
            dpos -= woff
            # Since this will be assigned to _rl_last_c_pos at the end (more
            #   precisely, _rl_last_c_pos == dpos when this function returns),
            #   let the caller know.
            @cpos_adjusted = true
         end
      else
         dpos = new
      end
      # If we don't have to do anything, then return.
      if (cpos == dpos)
         return
      end

      if @hConsoleHandle
         csbi = 0.chr * 24
         @GetConsoleScreenBufferInfo.Call(@hConsoleHandle,csbi)
         x,y = csbi[4,4].unpack('SS')
         x = dpos
         @SetConsoleCursorPosition.Call(@hConsoleHandle,y*65536+x)
         @_rl_last_c_pos = dpos
         return
      end

      # It may be faster to output a CR, and then move forwards instead
      #   of moving backwards.
      # i == current physical cursor position.
      if !@rl_byte_oriented
         i = @_rl_last_c_pos
      else
         i = @_rl_last_c_pos - woff
      end

      if (dpos == 0 || cr_faster(dpos, @_rl_last_c_pos) ||
         (@_rl_term_autowrap && i == @_rl_screenwidth))
         @rl_outstream.write(@_rl_term_cr)
         cpos = @_rl_last_c_pos = 0
      end

      if (cpos < dpos)
         # Move the cursor forward.  We do it by printing the command
         # to move the cursor forward if there is one, else print that
         # portion of the output buffer again.  Which is cheaper?

         # The above comment is left here for posterity.  It is faster
         # to print one character (non-control) than to print a control
         # sequence telling the terminal to move forward one character.
         # That kind of control is for people who don't know what the
         # data is underneath the cursor.

         # However, we need a handle on where the current display position is
         # in the buffer for the immediately preceding comment to be true.
         # In multibyte locales, we don't currently have that info available.
         # Without it, we don't know where the data we have to display begins
         # in the buffer and we have to go back to the beginning of the screen
         # line.  In this case, we can use the terminal sequence to move forward
         # if it's available.
         if !@rl_byte_oriented
            if (@_rl_term_forward_char)
               @rl_outstream.write(@_rl_term_forward_char * (dpos-cpos))
            else
               @rl_outstream.write(@_rl_term_cr)
               @rl_outstream.write(data[start,new])
            end
         else
            @rl_outstream.write(data[start+cpos,new-cpos])
         end
      elsif (cpos > dpos)
         _rl_backspace(cpos - dpos)
      end
      @_rl_last_c_pos = dpos
   end


   # PWP: move the cursor up or down.
   def _rl_move_vert(to)
      if (@_rl_last_v_pos == to || to > @_rl_screenheight)
         return
      end

      if ((delta = to - @_rl_last_v_pos) > 0)
         @rl_outstream.write("\n"*delta)
         @rl_outstream.write("\r")
         @_rl_last_c_pos = 0
      else
         if(@_rl_term_up)
            @rl_outstream.write(@_rl_term_up*(-delta))
         end
      end
      @_rl_last_v_pos = to        # Now TO is here
   end

   def rl_setstate(x)
      (@rl_readline_state |= (x))
   end

   def rl_unsetstate(x)
      (@rl_readline_state &= ~(x))
   end

   def rl_isstate(x)
      (@rl_readline_state & (x))!=0
   end

   # Clear any pending input pushed with rl_execute_next()
   def rl_clear_pending_input()
      @rl_pending_input = 0
      rl_unsetstate(RL_STATE_INPUTPENDING)
      0
   end

   def rl_restart_output(count, key)
      0
   end

   def rl_clear_signals()
      if Signal.list['WINCH']
         trap "WINCH",@def_proc
      end
   end

   def rl_set_signals()
      if Signal.list['WINCH']
         @def_proc = trap "WINCH",Proc.new{rl_sigwinch_handler(0)}
      end
   end

   # Current implementation:
   #    \001 (^A) start non-visible characters
   #    \002 (^B) end non-visible characters
   #   all characters except \001 and \002 (following a \001) are copied to
   #   the returned string all characters except those between \001 and
   #   \002 are assumed to be `visible'.
   def expand_prompt(pmt)
      # Short-circuit if we can.
      if (@rl_byte_oriented && pmt[RL_PROMPT_START_IGNORE].nil?)
         r = pmt.dup
         lp = r.length
         lip = 0
         niflp = 0
         vlp = lp
         return [r,lp,lip,niflp,vlp]
      end

      l = pmt.length
      ret = ''
      invfl = 0    # invisible chars in first line of prompt
      invflset = 0    # we only want to set invfl once

      igstart = 0
      rl = 0
      ignoring = false
      last = ninvis = physchars = 0
      for pi in 0 ... pmt.length
         # This code strips the invisible character string markers
         #RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE
         if (!ignoring && pmt[pi,1] == RL_PROMPT_START_IGNORE)        # XXX - check ignoring?
            ignoring = true
            igstart = pi
            next
         elsif (ignoring && pmt[pi,1] == RL_PROMPT_END_IGNORE)
            ignoring = false
            if (pi != (igstart + 1))
               last = ret.length - 1
            end
            next
         else
            if !@rl_byte_oriented
               pind = pi
               ind = _rl_find_next_mbchar(pmt, pind, 1, MB_FIND_NONZERO)
               l = ind - pind
               while (l>0)
                  l-=1
                  ret << pmt[pi]
                  pi += 1
               end
               if (!ignoring)
                  rl += ind - pind
                  physchars += _rl_col_width(pmt, pind, ind)
               else
                  ninvis += ind - pind
               end
               pi-=1       # compensate for later increment
            else
               ret << pmt[pi]
               if (!ignoring)
                  rl+=1            # visible length byte counter
                  physchars+=1
               else
                  ninvis+=1        # invisible chars byte counter
               end

               if (invflset == 0 && rl >= @_rl_screenwidth)
                  invfl = ninvis
                  invflset = 1
               end
            end
         end
      end

      if (rl < @_rl_screenwidth)
         invfl = ninvis
      end
      lp = rl
      lip = last
      niflp = invfl
      vlp = physchars
      return [ret,lp,lip,niflp,vlp]
   end


   #*
   #* Expand the prompt string into the various display components, if
   #* necessary.
   #*
   #* local_prompt = expanded last line of string in rl_display_prompt
   #*          (portion after the final newline)
   #* local_prompt_prefix = portion before last newline of rl_display_prompt,
   #*             expanded via expand_prompt
   #* prompt_visible_length = number of visible characters in local_prompt
   #* prompt_prefix_length = number of visible characters in local_prompt_prefix
   #*
   #* This function is called once per call to readline().  It may also be
   #* called arbitrarily to expand the primary prompt.
   #*
   #* The return value is the number of visible characters on the last line
   #* of the (possibly multi-line) prompt.
   #*
   def rl_expand_prompt(prompt)
      @local_prompt = @local_prompt_prefix = nil
      @local_prompt_len = 0
      @prompt_last_invisible = @prompt_invis_chars_first_line = 0
      @prompt_visible_length = @prompt_physical_chars = 0

      if (prompt.nil? || prompt == '')
         return (0)
      end

      pi = prompt.rindex("\n")
      if pi.nil?
         # The prompt is only one logical line, though it might wrap.
         @local_prompt,@prompt_visible_length,@prompt_last_invisible,@prompt_invis_chars_first_line,@prompt_physical_chars =
         expand_prompt(prompt)
         @local_prompt_prefix = nil
         @local_prompt_len = @local_prompt ? @local_prompt.length : 0
         return (@prompt_visible_length)
      else
         # The prompt spans multiple lines.
         pi += 1 if prompt.length!=pi+1
         t = pi
         @local_prompt,@prompt_visible_length,@prompt_last_invisible,_,@prompt_physical_chars = expand_prompt(prompt[pi..-1])
         c = prompt[t]
         prompt[t] = 0.chr
         # The portion of the prompt string up to and including the
         #final newline is now null-terminated.
         @local_prompt_prefix,@prompt_prefix_length,_,@prompt_invis_chars_first_line, = expand_prompt(prompt)
         prompt[t] = c
         @local_prompt_len = @local_prompt ? @local_prompt.length : 0
         return (@prompt_prefix_length)
      end
   end

   # Set up the prompt and expand it.  Called from readline() and
   #   rl_callback_handler_install ().
   def rl_set_prompt(prompt)
      @rl_prompt = prompt ? prompt.dup : nil
      @rl_display_prompt = @rl_prompt ? @rl_prompt : ""
      @rl_visible_prompt_length = rl_expand_prompt(@rl_prompt)
      0
   end

   def get_term_capabilities(buffer)
      hash = {}
      `infocmp -C`.split(':').select{|x| x =~ /(.*)=(.*)/ and hash[$1]=$2.gsub('\\E',"\e").gsub(/\^(.)/){($1[0].ord ^ ((?a..?z).include?($1[0]) ? 0x60 : 0x40)).chr}}
      @_rl_term_at7          =     hash["@7"]
      @_rl_term_DC           =     hash["DC"]
      @_rl_term_IC           =     hash["IC"]
      @_rl_term_clreol       =     hash["ce"]
      @_rl_term_clrpag       =     hash["cl"]
      @_rl_term_cr           =     hash["cr"]
      @_rl_term_dc           =     hash["dc"]
      @_rl_term_ei           =     hash["ei"]
      @_rl_term_ic           =     hash["ic"]
      @_rl_term_im           =     hash["im"]
      @_rl_term_kD           =     hash["kD"]
      @_rl_term_kH           =     hash["kH"]
      @_rl_term_kI           =     hash["kI"]
      @_rl_term_kd           =     hash["kd"]
      @_rl_term_ke           =     hash["ke"]
      @_rl_term_kh           =     hash["kh"]
      @_rl_term_kl           =     hash["kl"]
      @_rl_term_kr           =     hash["kr"]
      @_rl_term_ks           =     hash["ks"]
      @_rl_term_ku           =     hash["ku"]
      @_rl_term_backspace    =     hash["le"]
      @_rl_term_mm           =     hash["mm"]
      @_rl_term_mo           =     hash["mo"]
      @_rl_term_forward_char =     hash["nd"]
      @_rl_term_pc           =     hash["pc"]
      @_rl_term_up           =     hash["up"]
      @_rl_visible_bell      =     hash["vb"]
      @_rl_term_vs           =     hash["vs"]
      @_rl_term_ve           =     hash["ve"]
      @tcap_initialized = true
   end

   # Set the environment variables LINES and COLUMNS to lines and cols,
   #   respectively.
   def sh_set_lines_and_columns(lines, cols)
      ENV["LINES"] = lines.to_s
      ENV["COLUMNS"] = cols.to_s
   end

   # Get readline's idea of the screen size.  TTY is a file descriptor open
   #   to the terminal.  If IGNORE_ENV is true, we do not pay attention to the
   #   values of $LINES and $COLUMNS.  The tests for TERM_STRING_BUFFER being
   #   non-null serve to check whether or not we have initialized termcap.
   def _rl_get_screen_size(tty, ignore_env)

      if @hConsoleHandle
         csbi = 0.chr * 24
         @GetConsoleScreenBufferInfo.Call(@hConsoleHandle,csbi)
         wc,wr = csbi[0,4].unpack('SS')
         # wr,wc, = `mode con`.scan(/\d+\n/).map{|x| x.to_i}
         @_rl_screenwidth = wc
         @_rl_screenheight = wr
      else
         wr,wc = `stty size`.split(' ').map{|x| x.to_i}
         @_rl_screenwidth = wc
         @_rl_screenheight = wr
         if ignore_env==0 && ENV['LINES']
            @_rl_screenheight = ENV['LINES'].to_i
         end
         if ignore_env==0 && ENV['COLUMNS']
            @_rl_screenwidth = ENV['COLUMNS'].to_i
         end
      end

      # If all else fails, default to 80x24 terminal.
      if @_rl_screenwidth.nil? || @_rl_screenwidth <= 1
         @_rl_screenwidth = 80
      end
      if @_rl_screenheight.nil? || @_rl_screenheight <= 0
         @_rl_screenheight = 24
      end
      # If we're being compiled as part of bash, set the environment
      #   variables $LINES and $COLUMNS to new values.  Otherwise, just
      #   do a pair of putenv () or setenv () calls.
      sh_set_lines_and_columns(@_rl_screenheight, @_rl_screenwidth)

      if !@_rl_term_autowrap
         @_rl_screenwidth-=1
      end
      @_rl_screenchars = @_rl_screenwidth * @_rl_screenheight
   end

   def tgetflag(name)
      `infocmp -C -r`.scan(/\w{2}/).include?(name)
   end

   # Return the function (or macro) definition which would be invoked via
   #   KEYSEQ if executed in MAP.  If MAP is NULL, then the current keymap is
   #   used.  TYPE, if non-NULL, is a pointer to an int which will receive the
   #   type of the object pointed to.  One of ISFUNC (function), ISKMAP (keymap),
   #   or ISMACR (macro).
   def rl_function_of_keyseq(keyseq, map, type)
      map ||= @_rl_keymap
      map[keyseq]
   end

   # Bind the key sequence represented by the string KEYSEQ to
   #   the arbitrary pointer DATA.  TYPE says what kind of data is
   #   pointed to by DATA, right now this can be a function (ISFUNC),
   #   a macro (ISMACR), or a keymap (ISKMAP).  This makes new keymaps
   #   as necessary.  The initial place to do bindings is in MAP.
   def rl_generic_bind(type, keyseq, data, map)
      map[keyseq] = data
      0
   end

   # Bind the key sequence represented by the string KEYSEQ to
   #   FUNCTION.  This makes new keymaps as necessary.  The initial
   #   place to do bindings is in MAP.
   def rl_bind_keyseq_in_map(keyseq, function, map)
      rl_generic_bind(ISFUNC, keyseq, function, map)
   end


   # Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound.  Right
   #   now, this is always used to attempt to bind the arrow keys, hence the
   #   check for rl_vi_movement_mode.
   def rl_bind_keyseq_if_unbound_in_map(keyseq, default_func, kmap)
      if (keyseq)
         func = rl_function_of_keyseq(keyseq, kmap, nil)
         if (func.nil? || func == :rl_vi_movement_mode)
            return (rl_bind_keyseq_in_map(keyseq, default_func, kmap))
         else
            return 1
         end
      end
      0
   end

   def rl_bind_keyseq_if_unbound(keyseq, default_func)
      rl_bind_keyseq_if_unbound_in_map(keyseq, default_func, @_rl_keymap)
   end

   # Bind the arrow key sequences from the termcap description in MAP.
   def bind_termcap_arrow_keys(map)
      xkeymap = @_rl_keymap
      @_rl_keymap = map

      rl_bind_keyseq_if_unbound(@_rl_term_ku, :rl_get_previous_history)
      rl_bind_keyseq_if_unbound(@_rl_term_kd, :rl_get_next_history)
      rl_bind_keyseq_if_unbound(@_rl_term_kr, :rl_forward_char)
      rl_bind_keyseq_if_unbound(@_rl_term_kl, :rl_backward_char)

      rl_bind_keyseq_if_unbound(@_rl_term_kh, :rl_beg_of_line) # Home
      rl_bind_keyseq_if_unbound(@_rl_term_at7, :rl_end_of_line)   # End

      rl_bind_keyseq_if_unbound(@_rl_term_kD, :rl_delete)
      rl_bind_keyseq_if_unbound(@_rl_term_kI, :rl_overwrite_mode)

      @_rl_keymap = xkeymap
   end

   def _rl_init_terminal_io(terminal_name)
      term = terminal_name ? terminal_name : ENV["TERM"]
      @_rl_term_clrpag = @_rl_term_cr = @_rl_term_clreol = nil
      tty = @rl_instream ? @rl_instream.fileno : 0

      if no_terminal?
         term = "dumb"
         @_rl_bind_stty_chars = false
      end

      @term_string_buffer ||= 0.chr * 2032

      @term_buffer ||= 0.chr * 4080

      buffer = @term_string_buffer

      tgetent_ret = (term != "dumb") ? 1 : -1

      if (tgetent_ret <= 0)
         buffer = @term_buffer = @term_string_buffer = nil

         @_rl_term_autowrap = false    # used by _rl_get_screen_size

         # Allow calling application to set default height and width, using
         #rl_set_screen_size
         if (@_rl_screenwidth <= 0 || @_rl_screenheight <= 0)
            _rl_get_screen_size(tty, 0)
         end

         # Defaults.
         if (@_rl_screenwidth <= 0 || @_rl_screenheight <= 0)
            @_rl_screenwidth = 79
            @_rl_screenheight = 24
         end

         # Everything below here is used by the redisplay code (tputs).
         @_rl_screenchars = @_rl_screenwidth * @_rl_screenheight
         @_rl_term_cr = "\r"
         @_rl_term_im = @_rl_term_ei = @_rl_term_ic = @_rl_term_IC = nil
         @_rl_term_up = @_rl_term_dc = @_rl_term_DC = @_rl_visible_bell = nil
         @_rl_term_ku = @_rl_term_kd = @_rl_term_kl = @_rl_term_kr = nil
         @_rl_term_kh = @_rl_term_kH = @_rl_term_kI = @_rl_term_kD = nil
         @_rl_term_ks = @_rl_term_ke = @_rl_term_at7 = nil
         @_rl_term_mm = @_rl_term_mo = nil
         @_rl_term_ve = @_rl_term_vs = nil
         @_rl_term_forward_char = nil
         @_rl_terminal_can_insert = @term_has_meta = false

         # Reasonable defaults for tgoto().  Readline currently only uses
         #   tgoto if _rl_term_IC or _rl_term_DC is defined, but just in case we
         #   change that later...
         @_rl_term_backspace = "\b"

         return 0
      end

      get_term_capabilities(buffer)

      @_rl_term_cr ||= "\r"
      @_rl_term_autowrap = !!(tgetflag("am") && tgetflag("xn"))

      # Allow calling application to set default height and width, using
      #   rl_set_screen_size
      if (@_rl_screenwidth <= 0 || @_rl_screenheight <= 0)
         _rl_get_screen_size(tty, 0)
      end

      # "An application program can assume that the terminal can do
      #    character insertion if *any one of* the capabilities `IC',
      #    `im', `ic' or `ip' is provided."  But we can't do anything if
      #    only `ip' is provided, so...
      @_rl_terminal_can_insert = !!(@_rl_term_IC || @_rl_term_im || @_rl_term_ic)

      # Check to see if this terminal has a meta key and clear the capability
      #   variables if there is none.
      @term_has_meta = !!(tgetflag("km") || tgetflag("MT"))
      if !@term_has_meta
         @_rl_term_mm = @_rl_term_mo = nil
      end

      # Attempt to find and bind the arrow keys.  Do not override already
      #   bound keys in an overzealous attempt, however.

      bind_termcap_arrow_keys(@emacs_standard_keymap)

      bind_termcap_arrow_keys(@vi_movement_keymap)
      bind_termcap_arrow_keys(@vi_insertion_keymap)

      return 0
   end

   # New public way to set the system default editing chars to their readline
   #   equivalents.
   def rl_tty_set_default_bindings(kmap)
      h = Hash[*`stty -a`.scan(/(\w+) = ([^;]+);/).flatten]
      h.each {|k,v| v.gsub!(/\^(.)/){($1[0].ord ^ ((?a..?z).include?($1[0]) ? 0x60 : 0x40)).chr}}
      kmap[h['erase']] = :rl_rubout
      kmap[h['kill']] = :rl_unix_line_discard
      kmap[h['werase']] = :rl_unix_word_rubout
      kmap[h['lnext']] = :rl_quoted_insert
   end

   # If this system allows us to look at the values of the regular
   #   input editing characters, then bind them to their readline
   #   equivalents, iff the characters are not bound to keymaps.
   def readline_default_bindings()
      if @_rl_bind_stty_chars
         rl_tty_set_default_bindings(@_rl_keymap)
      end
   end

   def _rl_init_eightbit()

   end


   # Do key bindings from a file.  If FILENAME is NULL it defaults
   #   to the first non-null filename from this list:
   #     1. the filename used for the previous call
   #     2. the value of the shell variable `INPUTRC'
   #     3. ~/.inputrc
   #     4. /etc/inputrc
   #   If the file existed and could be opened and read, 0 is returned,
   #   otherwise errno is returned.
   def rl_read_init_file(filename)
      # Default the filename.
      filename ||= @last_readline_init_file
      filename ||= ENV["INPUTRC"]
      if (filename.nil? || filename == '')
         filename = DEFAULT_INPUTRC
         # Try to read DEFAULT_INPUTRC; fall back to SYS_INPUTRC on failure
         if (_rl_read_init_file(filename, 0) == 0)
            return 0
         end
         filename = SYS_INPUTRC
      end

      if RUBY_PLATFORM =~ /mswin|mingw/
         return 0 if (_rl_read_init_file(filename, 0) == 0)
         filename = "~/_inputrc"
      end
      return (_rl_read_init_file(filename, 0))
   end

   def _rl_read_init_file(filename, include_level)
      @current_readline_init_file = filename
      @current_readline_init_include_level = include_level

      openname = File.expand_path(filename)
      begin
         buffer = File.open(openname).read
      rescue
         return -1
      end

      if (include_level == 0 && filename != @last_readline_init_file)
         @last_readline_init_file = filename.dup
      end

      @currently_reading_init_file = true

      # Loop over the lines in the file.  Lines that start with `#' are
      #   comments; all other lines are commands for readline initialization.
      @current_readline_init_lineno = 1

      buffer.each_line do |line|
         line.strip!
         next if line =~ /^#/
         next if line == ''
         rl_parse_and_bind(line)
      end

      return 0
   end

   # Push _rl_parsing_conditionalized_out, and set parser state based
   #   on ARGS.
   def parser_if(args)
      # Push parser state.
      @if_stack << @_rl_parsing_conditionalized_out

      # If parsing is turned off, then nothing can turn it back on except
      #   for finding the matching endif.  In that case, return right now.
      if @_rl_parsing_conditionalized_out
         return 0
      end

      args.downcase!
      # Handle "$if term=foo" and "$if mode=emacs" constructs.  If this
      #   isn't term=foo, or mode=emacs, then check to see if the first
      #   word in ARGS is the same as the value stored in rl_readline_name.
      if (@rl_terminal_name && args =~ /^term=/)
         # Terminals like "aaa-60" are equivalent to "aaa".
         tname = @rl_terminal_name.downcase.gsub(/-.*$/,'')

         # Test the `long' and `short' forms of the terminal name so that
         #if someone has a `sun-cmd' and does not want to have bindings
         #that will be executed if the terminal is a `sun', they can put
         #`$if term=sun-cmd' into their .inputrc.
         @_rl_parsing_conditionalized_out = (args[5..-1] != tname && args[5..-1] != @rl_terminal_name.downcase)
      elsif args =~ /^mode=/
         if args[5..-1] == "emacs"
            mode = @emacs_mode
         elsif args[5..-1] == "vi"
            mode = @vi_mode
         else
            mode = @no_mode
         end
         @_rl_parsing_conditionalized_out = (mode != @rl_editing_mode)
         # Check to see if the first word in ARGS is the same as the
         #   value stored in rl_readline_name.
      elsif (args == @rl_readline_name)
         @_rl_parsing_conditionalized_out = false
      else
         @_rl_parsing_conditionalized_out = true
      end
      return 0
   end

   # Invert the current parser state if there is anything on the stack.
   def parser_else(args)
      if @if_stack.empty?
         #_rl_init_file_error ("$else found without matching $if")
         return 0
      end

      # Check the previous (n) levels of the stack to make sure that
      #   we haven't previously turned off parsing.
      return 0 if @if_stack.detect {|x| x }

      # Invert the state of parsing if at top level.
      @_rl_parsing_conditionalized_out = !@_rl_parsing_conditionalized_out
      return 0
   end

   # Terminate a conditional, popping the value of
   #   _rl_parsing_conditionalized_out from the stack.
   def parser_endif(args)
      if (@if_stack.length>0)
         @_rl_parsing_conditionalized_out = @if_stack.pop
      else
         #_rl_init_file_error ("$endif without matching $if")
      end
      0
   end

   def parser_include(args)
      return 0 if (@_rl_parsing_conditionalized_out)

      old_init_file = @current_readline_init_file
      old_line_number = @current_readline_init_lineno
      old_include_level = @current_readline_init_include_level

      r = _rl_read_init_file(args, old_include_level + 1)

      @current_readline_init_file = old_init_file
      @current_readline_init_lineno = old_line_number
      @current_readline_init_include_level = old_include_level

      return r
   end

   # Handle a parser directive.  STATEMENT is the line of the directive
   #   without any leading `$'.
   def handle_parser_directive(statement)

      directive,args = statement.split(' ')

      case directive.downcase
      when "if"
         parser_if(args)
         return 0
      when "endif"
         parser_endif(args)
         return 0
      when "else"
         parser_else(args)
         return 0
      when "include"
         parser_include(args)
         return 0
      end

      #_rl_init_file_error("unknown parser directive")
      return 1
   end


   def rl_variable_bind(name,value)
      case name
      when "bind-tty-special-chars"
         @_rl_bind_stty_chars = value.nil? || value=='1' || value == 'on'
      when "blink-matching-paren"
         @rl_blink_matching_paren = value.nil? || value=='1' || value == 'on'
      when "byte-oriented"
         @rl_byte_oriented = value.nil? || value=='1' || value == 'on'
      when "completion-ignore-case"
         @_rl_completion_case_fold = value.nil? || value=='1' || value == 'on'
      when "convert-meta"
         @_rl_convert_meta_chars_to_ascii = value.nil? || value=='1' || value == 'on'
      when "disable-completion"
         @rl_inhibit_completion = value.nil? || value=='1' || value == 'on'
      when "enable-keypad"
         @_rl_enable_keypad = value.nil? || value=='1' || value == 'on'
      when "expand-tilde"
         @rl_complete_with_tilde_expansion = value.nil? || value=='1' || value == 'on'
      when "history-preserve-point"
         @_rl_history_preserve_point = value.nil? || value=='1' || value == 'on'
      when "horizontal-scroll-mode"
         @_rl_horizontal_scroll_mode = value.nil? || value=='1' || value == 'on'
      when "input-meta"
         @_rl_meta_flag = value.nil? || value=='1' || value == 'on'
      when "mark-directories"
         @_rl_complete_mark_directories = value.nil? || value=='1' || value == 'on'
      when "mark-modified-lines"
         @_rl_mark_modified_lines = value.nil? || value=='1' || value == 'on'
      when "mark-symlinked-directories"
         @_rl_complete_mark_symlink_dirs = value.nil? || value=='1' || value == 'on'
      when "match-hidden-files"
         @_rl_match_hidden_files = value.nil? || value=='1' || value == 'on'
      when "meta-flag"
         @_rl_meta_flag = value.nil? || value=='1' || value == 'on'
      when "output-meta"
         @_rl_output_meta_chars = value.nil? || value=='1' || value == 'on'
      when "page-completions"
         @_rl_page_completions = value.nil? || value=='1' || value == 'on'
      when "prefer-visible-bell"
         @_rl_prefer_visible_bell = value.nil? || value=='1' || value == 'on'
      when "print-completions-horizontally"
         @_rl_print_completions_horizontally = value.nil? || value=='1' || value == 'on'
      when "show-all-if-ambiguous"
         @_rl_complete_show_all = value.nil? || value=='1' || value == 'on'
      when "show-all-if-unmodified"
         @_rl_complete_show_unmodified = value.nil? || value=='1' || value == 'on'
      when "visible-stats"
         @rl_visible_stats = value.nil? || value=='1' || value == 'on'
      when "bell-style"
         case value
         when "none","off"
            @_rl_bell_preference = NO_BELL
         when "audible", "on"
            @_rl_bell_preference = AUDIBLE_BELL
         when "visible"
            @_rl_bell_preference = VISIBLE_BELL
         else
            @_rl_bell_preference = AUDIBLE_BELL
         end
      when "comment-begin"
         @_rl_comment_begin = value.dup
      when "completion-query-items"
         @rl_completion_query_items = value.to_i
      when "editing-mode"
         case value
         when "vi"
            @_rl_keymap = @vi_insertion_keymap
            @rl_editing_mode = @vi_mode
         when "emacs"
            @_rl_keymap = @emacs_standard_keymap
            @rl_editing_mode = @emacs_mode
         end
      when "isearch-terminators"
         @_rl_isearch_terminators = instance_eval(value)
      when "keymap"
         case value
         when "emacs","emacs-standard","emacs-meta","emacs-ctlx"
            @_rl_keymap = @emacs_standard_keymap
         when "vi","vi-move","vi-command"
            @_rl_keymap = @vi_movement_keymap
         when "vi-insert"
            @_rl_keymap = @vi_insertion_keymap
         end
      end
   end

   def rl_named_function(name)
      case name
      when "accept-line"
         return :rl_newline
      when "arrow-key-prefix"
         return :rl_arrow_keys
      when "backward-delete-char"
         return :rl_rubout
      when "character-search"
         return :rl_char_search
      when  "character-search-backward"
         return :rl_backward_char_search
      when "copy-region-as-kill"
         return :rl_copy_region_to_kill
      when "delete-char"
         return :rl_delete
      when "delete-char-or-list"
         return :rl_delete_or_show_completions
      when "forward-backward-delete-char"
         return :rl_rubout_or_delete
      when "kill-whole-line"
         return :rl_kill_full_line
      when "non-incremental-forward-search-history"
         return :rl_noninc_forward_search
      when "non-incremental-reverse-search-history"
         return :rl_noninc_reverse_search
      when "non-incremental-forward-search-history-again"
         return :rl_noninc_forward_search_again
      when "non-incremental-reverse-search-history-again"
         return :rl_noninc_reverse_search_again
      when "redraw-current-line"
         return :rl_refresh_line
      when "self-insert"
         return :rl_insert
      when "undo"
         return :rl_undo_command
      when "beginning-of-line"
         return :rl_beg_of_line
      else
         if name =~ /^[-a-z]+$/
            return ('rl_'+name.gsub('-','_')).to_sym
         end
      end
      nil
   end

   # Bind KEY to FUNCTION.  Returns non-zero if KEY is out of range.
   def rl_bind_key(key, function)
      @_rl_keymap[key] = function
      @rl_binding_keymap = @_rl_keymap
      0
   end

   # Read the binding command from STRING and perform it.
   #   A key binding command looks like: Keyname: function-name\0,
   #   a variable binding command looks like: set variable value.
   #   A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark.
   def rl_parse_and_bind(string)

      # If this is a parser directive, act on it.
      if (string[0,1] == "$")
         handle_parser_directive(string[1..-1])
         return 0
      end

      # If we aren't supposed to be parsing right now, then we're done.
      return 0 if @_rl_parsing_conditionalized_out

      if string =~ /^set/i
         _,var,value = string.downcase.split(' ')
         rl_variable_bind(var, value)
         return 0
      end

      if string =~ /"(.*)"\s*:\s*(.*)$/
        key, funname = $1, $2
        rl_bind_key(key, rl_named_function(funname))
      end

      0
   end


   def _rl_enable_meta_key()
      if(@term_has_meta && @_rl_term_mm)
         @_rl_out_stream.write(@_rl_term_mm)
      end
   end

   def rl_set_keymap_from_edit_mode()
      if (@rl_editing_mode == @emacs_mode)
         @_rl_keymap = @emacs_standard_keymap
      elsif (@rl_editing_mode == @vi_mode)
         @_rl_keymap = @vi_insertion_keymap
      end
   end

   def rl_get_keymap_name_from_edit_mode()
      if (@rl_editing_mode == @emacs_mode)
         "emacs"
      elsif (@rl_editing_mode == @vi_mode)
         "vi"
      else
         "none"
      end
   end

   # Bind some common arrow key sequences in MAP.
   def bind_arrow_keys_internal(map)
      xkeymap = @_rl_keymap
      @_rl_keymap = map

      if RUBY_PLATFORM =~ /mswin|mingw/
         rl_bind_keyseq_if_unbound("\340H", :rl_get_previous_history) # Up
         rl_bind_keyseq_if_unbound("\340P", :rl_get_next_history) # Down
         rl_bind_keyseq_if_unbound("\340M", :rl_forward_char)  # Right
         rl_bind_keyseq_if_unbound("\340K", :rl_backward_char) # Left
         rl_bind_keyseq_if_unbound("\340G", :rl_beg_of_line)   # Home
         rl_bind_keyseq_if_unbound("\340O", :rl_end_of_line)   # End
         rl_bind_keyseq_if_unbound("\340s", :rl_backward_word) # Ctrl-Left
         rl_bind_keyseq_if_unbound("\340t", :rl_forward_word) # Ctrl-Right
         rl_bind_keyseq_if_unbound("\340S", :rl_delete) # Delete
         rl_bind_keyseq_if_unbound("\340R", :rl_overwrite_mode) # Insert
      else
         rl_bind_keyseq_if_unbound("\033[A", :rl_get_previous_history)
         rl_bind_keyseq_if_unbound("\033[B", :rl_get_next_history)
         rl_bind_keyseq_if_unbound("\033[C", :rl_forward_char)
         rl_bind_keyseq_if_unbound("\033[D", :rl_backward_char)
         rl_bind_keyseq_if_unbound("\033[H", :rl_beg_of_line)
         rl_bind_keyseq_if_unbound("\033[F", :rl_end_of_line)

         rl_bind_keyseq_if_unbound("\033OA", :rl_get_previous_history)
         rl_bind_keyseq_if_unbound("\033OB", :rl_get_next_history)
         rl_bind_keyseq_if_unbound("\033OC", :rl_forward_char)
         rl_bind_keyseq_if_unbound("\033OD", :rl_backward_char)
         rl_bind_keyseq_if_unbound("\033OH", :rl_beg_of_line)
         rl_bind_keyseq_if_unbound("\033OF", :rl_end_of_line)
      end

      @_rl_keymap = xkeymap
   end

   # Try and bind the common arrow key prefixes after giving termcap and
   #   the inputrc file a chance to bind them and create `real' keymaps
   #   for the arrow key prefix.
   def bind_arrow_keys()
      bind_arrow_keys_internal(@emacs_standard_keymap)
      bind_arrow_keys_internal(@vi_movement_keymap)
      bind_arrow_keys_internal(@vi_insertion_keymap)
   end

   # Initialize the entire state of the world.
   def readline_initialize_everything()
      # Set up input and output if they are not already set up.
      @rl_instream ||= $stdin

      @rl_outstream ||= $stdout

      # Bind _rl_in_stream and _rl_out_stream immediately.  These values
      #   may change, but they may also be used before readline_internal ()
      #   is called.
      @_rl_in_stream = @rl_instream
      @_rl_out_stream = @rl_outstream

      # Allocate data structures.
      @rl_line_buffer = ""

      # Initialize the terminal interface.
      @rl_terminal_name ||= ENV["TERM"]
      _rl_init_terminal_io(@rl_terminal_name)

      # Bind tty characters to readline functions.
      readline_default_bindings()

      # Decide whether we should automatically go into eight-bit mode.
      _rl_init_eightbit()

      # Read in the init file.
      rl_read_init_file(nil)

      # XXX
      if (@_rl_horizontal_scroll_mode && @_rl_term_autowrap)
         @_rl_screenwidth -= 1
         @_rl_screenchars -= @_rl_screenheight
      end

      # Override the effect of any `set keymap' assignments in the
      #   inputrc file.
      rl_set_keymap_from_edit_mode()

      # Try to bind a common arrow key prefix, if not already bound.
      bind_arrow_keys()

      # Enable the meta key, if this terminal has one.
      if @_rl_enable_meta
         _rl_enable_meta_key()
      end

      # If the completion parser's default word break characters haven't
      #   been set yet, then do so now.
      @rl_completer_word_break_characters ||= @rl_basic_word_break_characters
   end

   def _rl_init_line_state()
      @rl_point = @rl_end = @rl_mark = 0
      @rl_line_buffer = ""
   end

   # Set the history pointer back to the last entry in the history.
   def _rl_start_using_history()
      using_history()
      @_rl_saved_line_for_history = nil
   end


   def cr_faster(new, cur)
      (new + 1) < (cur - new)
   end

   #* _rl_last_c_pos is an absolute cursor position in multibyte locales and a
   #   buffer index in others.  This macro is used when deciding whether the
   #   current cursor position is in the middle of a prompt string containing
   #   invisible characters.
   def prompt_ending_index()
      if !@rl_byte_oriented
         @prompt_physical_chars
      else
         (@prompt_last_invisible+1)
      end
   end

   # Initialize the VISIBLE_LINE and INVISIBLE_LINE arrays, and their associated
   #   arrays of line break markers.  MINSIZE is the minimum size of VISIBLE_LINE
   #   and INVISIBLE_LINE; if it is greater than LINE_SIZE, LINE_SIZE is
   #   increased.  If the lines have already been allocated, this ensures that
   #   they can hold at least MINSIZE characters.
   def init_line_structures(minsize)
      if @invisible_line.nil? # initialize it
         if (@line_size < minsize)
            @line_size = minsize
         end
         @visible_line = 0.chr * @line_size
         @invisible_line = 0.chr * @line_size # 1.chr
      elsif (@line_size < minsize)  # ensure it can hold MINSIZE chars
         @line_size *= 2
         if (@line_size < minsize)
            @line_size = minsize
         end
         @visible_line << 0.chr * (@line_size - @visible_line.length)
         @invisible_line << 1.chr * (@line_size - @invisible_line.length)
      end
      @visible_line[minsize,@line_size-minsize] = 0.chr * (@line_size-minsize)
      @invisible_line[minsize,@line_size-minsize] = 1.chr * (@line_size-minsize)

      if @vis_lbreaks.nil?
         @inv_lbreaks = []
         @vis_lbreaks = []
         @_rl_wrapped_line = []
         @inv_lbreaks[0] = @vis_lbreaks[0] = 0
      end
   end

   # Return the history entry at the current position, as determined by
   #   history_offset.  If there is no entry there, return a NULL pointer.
   def current_history()
      return ((@history_offset == @history_length) || @the_history.nil?) ?
      nil : @the_history[@history_offset]
   end

   def meta_char(c)
      c > "\x7f" && c <= "\xff"
   end

   def ctrl_char(c)
      c < "\x20"
   end

   def isprint(c)
      c >= "\x20" && c < "\x7f"
   end

   def whitespace(c)
      (c == ' ' || c == "\t")
   end

   def w_offset(line, offset)
      ((line) == 0 ? offset : 0)
   end

   def vis_llen(l)
      ((l) > @_rl_vis_botlin ? 0 : (@vis_lbreaks[l+1] - @vis_lbreaks[l]))
   end

   def inv_llen(l)
      (@inv_lbreaks[l+1] - @inv_lbreaks[l])
   end

   def vis_chars(line)
      @visible_line[@vis_lbreaks[line] .. -1]
   end

   def vis_pos(line)
      @vis_lbreaks[line]
   end

   def vis_line(line)
      ((line) > @_rl_vis_botlin) ? "" : vis_chars(line)
   end

   def inv_line(line)
      @invisible_line[@inv_lbreaks[line] .. -1]
   end

   def m_offset(margin, offset)
      ((margin) == 0 ? offset : 0)
   end


   # PWP: update_line() is based on finding the middle difference of each
   #   line on the screen; vis:
   #
   #             /old first difference
   #  /beginning of line   |        /old last same       /old EOL
   #  v          v         v         v
   # old:   eddie> Oh, my little gruntle-buggy is to me, as lurgid as
   # new:   eddie> Oh, my little buggy says to me, as lurgid as
   #  ^          ^   ^           ^
   #  \beginning of line   |  \new last same    \new end of line
   #             \new first difference
   #
   #   All are character pointers for the sake of speed.  Special cases for
   #   no differences, as well as for end of line additions must be handled.
   #
   #   Could be made even smarter, but this works well enough
   def update_line(old, ostart, new, current_line, omax, nmax, inv_botlin)
      # If we're at the right edge of a terminal that supports xn, we're
      #   ready to wrap around, so do so.  This fixes problems with knowing
      #   the exact cursor position and cut-and-paste with certain terminal
      #   emulators.  In this calculation, TEMP is the physical screen
      #   position of the cursor.
      if @encoding == 'X'
         old.force_encoding('ASCII-8BIT')
         new.force_encoding('ASCII-8BIT')
      end

      if !@rl_byte_oriented
         temp = @_rl_last_c_pos
      else
         temp = @_rl_last_c_pos - w_offset(@_rl_last_v_pos, @visible_wrap_offset)
      end
      if (temp == @_rl_screenwidth && @_rl_term_autowrap && !@_rl_horizontal_scroll_mode &&
         @_rl_last_v_pos == current_line - 1)

         if (!@rl_byte_oriented)
            # This fixes only double-column characters, but if the wrapped
            #   character comsumes more than three columns, spaces will be
            #   inserted in the string buffer.
            if (@_rl_wrapped_line[current_line] > 0)
               _rl_clear_to_eol(@_rl_wrapped_line[current_line])
            end

            if new[0,1] != 0.chr
               case @encoding
               when 'E'
                  wc = new.scan(/./me)[0]
                  ret = wc.length
                  tempwidth = wc.length
               when 'S'
                  wc = new.scan(/./ms)[0]
                  ret = wc.length
                  tempwidth = wc.length
               when 'U'
                  wc = new.scan(/./mu)[0]
                  ret = wc.length
                  tempwidth = wc.unpack('U').first >= 0x1000 ? 2 : 1
               when 'X'
                  wc = new[0..-1].force_encoding(@encoding_name)[0]
                  ret = wc.bytesize
                  tempwidth = wc.ord >= 0x1000 ? 2 : 1
               else
                  ret = 1
                  tempwidth = 1
               end
            else
               tempwidth = 0
            end

            if (tempwidth > 0)
               bytes = ret
               @rl_outstream.write(new[0,bytes])
               @_rl_last_c_pos = tempwidth
               @_rl_last_v_pos+=1

               if old[ostart,1] != 0.chr
                  case @encoding
                  when 'E'
                     wc = old[ostart..-1].scan(/./me)[0]
                     ret = wc.length
                  when 'S'
                     wc = old[ostart..-1].scan(/./ms)[0]
                     ret = wc.length
                  when 'U'
                     wc = old[ostart..-1].scan(/./mu)[0]
                     ret = wc.length
                  when 'X'
                     wc = old[ostart..-1].force_encoding(@encoding_name)[0]
                     ret = wc.bytesize
                  end
               else
                  ret = 0
               end
               if (ret != 0 && bytes != 0)
                  if ret != bytes
                     len = old[ostart..-1].index(0.chr,ret)
                     old[ostart+bytes,len-ret] = old[ostart+ret,len-ret]
                  end
                  old[ostart,bytes] = new[0,bytes]
               end
            else
               @rl_outstream.write(' ')
               @_rl_last_c_pos = 1
               @_rl_last_v_pos+=1
               if (old[ostart,1] != 0.chr && new[0,1] != 0.chr)
                  old[ostart,1] = new[0,1]
               end
            end

         else
            if (new[0,1] != 0.chr)
               @rl_outstream.write(new[0,1])
            else
               @rl_outstream.write(' ')
            end
            @_rl_last_c_pos = 1
            @_rl_last_v_pos+=1
            if (old[ostart,1] != 0.chr && new[0,1] != 0.chr)
               old[ostart,1] = new[0,1]
            end
         end
      end

      # Find first difference.
      if (!@rl_byte_oriented)
         # See if the old line is a subset of the new line, so that the
         # only change is adding characters.
         if (index = old.index(0.chr)) && omax+ostart>index
            omax = index - ostart
         end
         if (index = new.index(0.chr)) && nmax>index
            nmax = index
         end

         temp = (omax < nmax) ? omax : nmax
         if old[ostart,temp]==new[0,temp]
            ofd = temp
            nfd = temp
         else
            if (omax == nmax && new[0,omax]==old[ostart,omax])
               ofd = omax
               nfd = nmax
            else
               new_offset = 0
               old_offset = ostart
               ofd = 0
               nfd = 0
               while(ofd < omax && old[ostart+ofd,1] != 0.chr &&
                  _rl_compare_chars(old, old_offset, new, new_offset))

                  old_offset = _rl_find_next_mbchar(old, old_offset, 1, MB_FIND_ANY)
                  new_offset = _rl_find_next_mbchar(new, new_offset, 1, MB_FIND_ANY)
                  ofd = old_offset - ostart
                  nfd = new_offset
               end
            end
         end
      else
         ofd = 0
         nfd = 0
         while(ofd < omax && old[ostart+ofd,1] != 0.chr && old[ostart+ofd,1] == new[nfd,1])
            ofd += 1
            nfd += 1
         end
      end


      # Move to the end of the screen line.  ND and OD are used to keep track
      #   of the distance between ne and new and oe and old, respectively, to
      #   move a subtraction out of each loop.
      oe = old.index(0.chr,ostart+ofd) - ostart
      if oe.nil? || oe>omax
         oe = omax
      end

      ne = new.index(0.chr,nfd)
      if ne.nil? || ne>omax
         ne = nmax
      end

      # If no difference, continue to next line.
      if (ofd == oe && nfd == ne)
         return
      end


      wsatend = true       # flag for trailing whitespace

      if (!@rl_byte_oriented)

         ols = _rl_find_prev_mbchar(old, ostart+oe, MB_FIND_ANY) - ostart
         nls = _rl_find_prev_mbchar(new, ne, MB_FIND_ANY)
         while ((ols > ofd) && (nls > nfd))

            if (!_rl_compare_chars(old, ostart+ols, new, nls))
               break
            end
            if (old[ostart+ols,1] == " ")
               wsatend = false
            end

            ols = _rl_find_prev_mbchar(old, ols+ostart, MB_FIND_ANY) - ostart
            nls = _rl_find_prev_mbchar(new, nls, MB_FIND_ANY)
         end
      else
         ols = oe - 1         # find last same
         nls = ne - 1
         while ((ols > ofd) && (nls > nfd) && old[ostart+ols,1] == new[nls,1])
            if (old[ostart+ols,1] != " ")
               wsatend = false
            end
            ols-=1
            nls-=1
         end
      end

      if (wsatend)
         ols = oe
         nls = ne
      elsif (!_rl_compare_chars(old, ostart+ols, new, nls))
         if (old[ostart+ols,1] != 0.chr)         # don't step past the NUL
            if !@rl_byte_oriented
               ols = _rl_find_next_mbchar(old, ostart+ols, 1, MB_FIND_ANY) - ostart
            else
               ols+=1
            end
         end
         if (new[nls,1] != 0.chr )
            if !@rl_byte_oriented
               nls = _rl_find_next_mbchar(new, nls, 1, MB_FIND_ANY)
            else
               nls+=1
            end
         end
      end

      # count of invisible characters in the current invisible line.
      current_invis_chars = w_offset(current_line, @wrap_offset)
      if (@_rl_last_v_pos != current_line)
         _rl_move_vert(current_line)
         if (@rl_byte_oriented && current_line == 0 && @visible_wrap_offset!=0)
            @_rl_last_c_pos += @visible_wrap_offset
         end
      end

      # If this is the first line and there are invisible characters in the
      #   prompt string, and the prompt string has not changed, and the current
      #   cursor position is before the last invisible character in the prompt,
      #   and the index of the character to move to is past the end of the prompt
      #   string, then redraw the entire prompt string.  We can only do this
      #   reliably if the terminal supports a `cr' capability.

      #  This is not an efficiency hack -- there is a problem with redrawing
      #  portions of the prompt string if they contain terminal escape
      #  sequences (like drawing the `unbold' sequence without a corresponding
      #  `bold') that manifests itself on certain terminals.

      lendiff = @local_prompt_len

      if (current_line == 0 && !@_rl_horizontal_scroll_mode &&
         @_rl_term_cr && lendiff > @prompt_visible_length && @_rl_last_c_pos > 0 &&
         ofd >= lendiff && @_rl_last_c_pos < prompt_ending_index())
         @rl_outstream.write(@_rl_term_cr)
         _rl_output_some_chars(@local_prompt,0,lendiff)
         if !@rl_byte_oriented
            # We take wrap_offset into account here so we can pass correct
            #   information to _rl_move_cursor_relative.
            @_rl_last_c_pos = _rl_col_width(@local_prompt, 0, lendiff) - @wrap_offset
            @cpos_adjusted = true
         else
            @_rl_last_c_pos = lendiff
         end
      end

      # When this function returns, _rl_last_c_pos is correct, and an absolute
      #   cursor postion in multibyte mode, but a buffer index when not in a
      #   multibyte locale.
      _rl_move_cursor_relative(ofd, old, ostart)

      if (current_line == 0 && !@rl_byte_oriented && @_rl_last_c_pos == @prompt_physical_chars)
         @cpos_adjusted = true
      end

      # if (len (new) > len (old))
      #   lendiff == difference in buffer
      #   col_lendiff == difference on screen
      #   When not using multibyte characters, these are equal
      lendiff = (nls - nfd) - (ols - ofd)
      if !@rl_byte_oriented
         col_lendiff = _rl_col_width(new, nfd, nls) - _rl_col_width(old, ostart+ofd, ostart+ols)
      else
         col_lendiff = lendiff
      end

      # If we are changing the number of invisible characters in a line, and
      #   the spot of first difference is before the end of the invisible chars,
      #   lendiff needs to be adjusted.
      if (current_line == 0 && !@_rl_horizontal_scroll_mode &&
         current_invis_chars != @visible_wrap_offset)
         if !@rl_byte_oriented
            lendiff += @visible_wrap_offset - current_invis_chars
            col_lendiff += @visible_wrap_offset - current_invis_chars
         else
            lendiff += @visible_wrap_offset - current_invis_chars
            col_lendiff = lendiff
         end
      end

      # Insert (diff (len (old), len (new)) ch.
      temp = ne - nfd
      if !@rl_byte_oriented
         col_temp = _rl_col_width(new,nfd,ne)
      else
         col_temp = temp
      end
      if (col_lendiff > 0) # XXX - was lendiff

         # Non-zero if we're increasing the number of lines.
         gl = current_line >= @_rl_vis_botlin && inv_botlin > @_rl_vis_botlin
         # Sometimes it is cheaper to print the characters rather than
         # use the terminal's capabilities.  If we're growing the number
         # of lines, make sure we actually cause the new line to wrap
         # around on auto-wrapping terminals.
         if (@_rl_terminal_can_insert && ((2 * col_temp) >= col_lendiff || @_rl_term_IC) && (!@_rl_term_autowrap || !gl))

            # If lendiff > prompt_visible_length and _rl_last_c_pos == 0 and
            #   _rl_horizontal_scroll_mode == 1, inserting the characters with
            #   _rl_term_IC or _rl_term_ic will screw up the screen because of the
            #   invisible characters.  We need to just draw them.
            if (old[ostart+ols,1] != 0.chr && (!@_rl_horizontal_scroll_mode || @_rl_last_c_pos > 0 ||
               lendiff <= @prompt_visible_length || current_invis_chars==0))

               insert_some_chars(new[nfd..-1], lendiff, col_lendiff)
               @_rl_last_c_pos += col_lendiff
            elsif ((@rl_byte_oriented) && old[ostart+ols,1] == 0.chr && lendiff > 0)
               # At the end of a line the characters do not have to
               # be "inserted".  They can just be placed on the screen.
               # However, this screws up the rest of this block, which
               # assumes you've done the insert because you can.
               _rl_output_some_chars(new,nfd, lendiff)
               @_rl_last_c_pos += col_lendiff
            else
               # We have horizontal scrolling and we are not inserting at
               # the end.  We have invisible characters in this line.  This
               # is a dumb update.
               _rl_output_some_chars(new,nfd, temp)
               @_rl_last_c_pos += col_temp
               return
            end
            # Copy (new) chars to screen from first diff to last match.
            temp = nls - nfd
            if ((temp - lendiff) > 0)
               _rl_output_some_chars(new,(nfd + lendiff),temp - lendiff)
               # XXX -- this bears closer inspection.  Fixes a redisplay bug
               # reported against bash-3.0-alpha by Andreas Schwab involving
               # multibyte characters and prompt strings with invisible
               # characters, but was previously disabled.
               @_rl_last_c_pos += _rl_col_width(new,nfd+lendiff, nfd+lendiff+temp-col_lendiff)
            end
         else
            # cannot insert chars, write to EOL
            _rl_output_some_chars(new,nfd, temp)
            @_rl_last_c_pos += col_temp
            # If we're in a multibyte locale and were before the last invisible
            #   char in the current line (which implies we just output some invisible
            #   characters) we need to adjust _rl_last_c_pos, since it represents
            #   a physical character position.
         end
      else           # Delete characters from line.
         # If possible and inexpensive to use terminal deletion, then do so.
         if (@_rl_term_dc && (2 * col_temp) >= -col_lendiff)

            # If all we're doing is erasing the invisible characters in the
            #   prompt string, don't bother.  It screws up the assumptions
            #   about what's on the screen.
            if (@_rl_horizontal_scroll_mode && @_rl_last_c_pos == 0 &&
               -lendiff == @visible_wrap_offset)
               col_lendiff = 0
            end

            if (col_lendiff!=0)
               delete_chars(-col_lendiff) # delete (diff) characters
            end

            # Copy (new) chars to screen from first diff to last match
            temp = nls - nfd
            if (temp > 0)
               _rl_output_some_chars(new,nfd, temp)
               @_rl_last_c_pos += _rl_col_width(new,nfd,nfd+temp)
            end

            # Otherwise, print over the existing material.
         else
            if (temp > 0)
               _rl_output_some_chars(new,nfd, temp)
               @_rl_last_c_pos += col_temp      # XXX
            end

            lendiff = (oe) - (ne)
            if !@rl_byte_oriented
               col_lendiff = _rl_col_width(old, ostart, ostart+oe) - _rl_col_width(new, 0, ne)
            else
               col_lendiff = lendiff
            end

            if (col_lendiff!=0)
               if (@_rl_term_autowrap && current_line < inv_botlin)
                  space_to_eol(col_lendiff)
               else
                  _rl_clear_to_eol(col_lendiff)
               end
            end
         end
      end
   end

   # Basic redisplay algorithm.
   def rl_redisplay()
      return if !@readline_echoing_p

      _rl_wrapped_multicolumn = 0

      @rl_display_prompt ||= ""

      if (@invisible_line.nil? || @vis_lbreaks.nil?)
         init_line_structures(0)
         rl_on_new_line()
      end

      # Draw the line into the buffer.
      @cpos_buffer_position = -1

      line = @invisible_line
      out = inv_botlin = 0

      # Mark the line as modified or not.  We only do this for history
      #   lines.
      modmark = 0
      if (@_rl_mark_modified_lines && current_history() && @rl_undo_list)
         line[out,1] = '*'
         out += 1
         line[out,1] = 0.chr
         modmark = 1
      end

      # If someone thought that the redisplay was handled, but the currently
      #   visible line has a different modification state than the one about
      #   to become visible, then correct the caller's misconception.
      if (@visible_line[0,1] != @invisible_line[0,1])
         @rl_display_fixed = false
      end

      # If the prompt to be displayed is the `primary' readline prompt (the
      #   one passed to readline()), use the values we have already expanded.
      #   If not, use what's already in rl_display_prompt.  WRAP_OFFSET is the
      #   number of non-visible characters in the prompt string.
      if (@rl_display_prompt == @rl_prompt || @local_prompt)

         if (@local_prompt_prefix && @forced_display)
            _rl_output_some_chars(@local_prompt_prefix,0,@local_prompt_prefix.length)
         end
         if (@local_prompt_len > 0)

            temp = @local_prompt_len + out + 2
            if (temp >= @line_size)
               @line_size = (temp + 1024) - (temp % 1024)
               if @visible_line.length >= @line_size
                  @visible_line = @visible_line[0,@line_size]
               else
                  @visible_line += 0.chr * (@line_size-@visible_line.length)
               end

               if @invisible_line.length >= @line_size
                  @invisible_line = @invisible_line[0,@line_size]
               else
                  @invisible_line += 0.chr * (@line_size-@invisible_line.length)
               end
               if @encoding=='X'
                  @visible_line.force_encoding('ASCII-8BIT')
                  @invisible_line.force_encoding('ASCII-8BIT')
               end
               line = @invisible_line
            end
            line[out,@local_prompt_len] = @local_prompt
            out += @local_prompt_len
         end
         line[out,1] = 0.chr
         @wrap_offset = @local_prompt_len - @prompt_visible_length
      else
         prompt_this_line = @rl_display_prompt.rindex("\n")
         if prompt_this_line.nil?
            prompt_this_line = 0
         else
            prompt_this_line+=1

            pmtlen = prompt_this_line # temp var
            if (@forced_display)
               _rl_output_some_chars(@rl_display_prompt,0,pmtlen)
               # Make sure we are at column zero even after a newline,
               #regardless of the state of terminal output processing.
               if (pmtlen < 2 || @rl_display_prompt[prompt_this_line-2,1] != "\r")
                  cr()
               end
            end
         end

         @prompt_physical_chars = pmtlen = @rl_display_prompt.length - prompt_this_line
         temp = pmtlen + out + 2
         if (temp >= @line_size)
            @line_size = (temp + 1024) - (temp % 1024)
            if @visible_line.length >= @line_size
               @visible_line = @visible_line[0,@line_size]
            else
               @visible_line += 0.chr * (@line_size-@visible_line.length)
            end

            if @invisible_line.length >= @line_size
               @invisible_line = @invisible_line[0,@line_size]
            else
               @invisible_line += 0.chr * (@line_size-@invisible_line.length)
            end
            if @encoding=='X'
               @visible_line.force_encoding('ASCII-8BIT')
               @invisible_line.force_encoding('ASCII-8BIT')
            end

            line = @invisible_line
         end
         line[out,pmtlen] = @rl_display_prompt[prompt_this_line,pmtlen]
         out += pmtlen
         line[out,1] = 0.chr
         @wrap_offset = @prompt_invis_chars_first_line = 0
      end
      # inv_lbreaks[i] is where line i starts in the buffer.
      @inv_lbreaks[newlines = 0] = 0
      lpos = @prompt_physical_chars + modmark

      @_rl_wrapped_line = Array.new(@visible_line.length,0)
      num = 0

      # prompt_invis_chars_first_line is the number of invisible characters in
      #   the first physical line of the prompt.
      #  wrap_offset - prompt_invis_chars_first_line is the number of invis
      #   chars on the second line.

      # what if lpos is already >= _rl_screenwidth before we start drawing the
      #   contents of the command line?
      while (lpos >= @_rl_screenwidth)
         # fix from Darin Johnson <darin@acuson.com> for prompt string with
         #   invisible characters that is longer than the screen width.  The
         #   prompt_invis_chars_first_line variable could be made into an array
         #   saying how many invisible characters there are per line, but that's
         #   probably too much work for the benefit gained.  How many people have
         #   prompts that exceed two physical lines?
         #   Additional logic fix from Edward Catmur <ed@catmur.co.uk>
         if (!@rl_byte_oriented)
            n0 = num
            temp = @local_prompt_len
            while (num < temp)
               z = _rl_col_width(@local_prompt, n0, num)
               if (z > @_rl_screenwidth)
                  num = _rl_find_prev_mbchar(@local_prompt, num, MB_FIND_ANY)
                  break
               elsif (z == @_rl_screenwidth)
                  break
               end
               num+=1
            end
            temp = num
         else
            temp = ((newlines + 1) * @_rl_screenwidth)
         end

         # Now account for invisible characters in the current line.
         temp += (@local_prompt_prefix.nil? ? ((newlines == 0) ? @prompt_invis_chars_first_line :
         ((newlines == 1) ? @wrap_offset : 0)) :
         ((newlines == 0) ? @wrap_offset : 0))

         @inv_lbreaks[newlines+=1] = temp
         if !@rl_byte_oriented
            lpos -= _rl_col_width(@local_prompt, n0, num)
         else
            lpos -= @_rl_screenwidth
         end
      end
      @prompt_last_screen_line = newlines

      # Draw the rest of the line (after the prompt) into invisible_line, keeping
      #   track of where the cursor is (cpos_buffer_position), the number of the line containing
      #   the cursor (lb_linenum), the last line number (lb_botlin and inv_botlin).
      #   It maintains an array of line breaks for display (inv_lbreaks).
      #   This handles expanding tabs for display and displaying meta characters.
      lb_linenum = 0
      _in = 0
      if !@rl_byte_oriented && @rl_end>0
         case @encoding
         when 'E'
            wc = @rl_line_buffer[0,@rl_end].scan(/./me)[0]
            wc_bytes = wc ? wc.length : 1
         when 'S'
            wc = @rl_line_buffer[0,@rl_end].scan(/./ms)[0]
            wc_bytes = wc ? wc.length : 1
         when 'U'
            wc = @rl_line_buffer[0,@rl_end].scan(/./mu)[0]
            wc_bytes = wc ? wc.length : 1
         when 'X'
            wc = @rl_line_buffer[0,@rl_end].force_encoding(@encoding_name)[0]
            wc_bytes = wc ? wc.bytesize : 1
         end
      else
         wc_bytes = 1
      end

      while(_in < @rl_end)

         c = @rl_line_buffer[_in,1]
         if(c == 0.chr)
            @rl_end = _in
            break
         end
         if (!@rl_byte_oriented)
            case @encoding
            when 'U'
               wc_width = wc && wc.unpack('U').first >= 0x1000 ? 2 : 1
            when 'X'
               wc_width = wc && wc.ord > 0x1000 ? 2 : 1
            else
               wc_width = wc ? wc.length : 1
            end
         end
         if (out + 8 >= @line_size)    # XXX - 8 for \t
            @line_size *= 2
            if @visible_line.length>=@line_size
               @visible_line = @visible_line[0,@line_size]
            else
               @visible_line += 0.chr * (@line_size-@visible_line.length)
            end
            if @invisible_line.length>=@line_size
               @invisible_line = @invisible_line[0,@line_size]
            else
               @invisible_line += 0.chr * (@line_size-@invisible_line.length)
            end
            line = @invisible_line
         end

         if (_in == @rl_point)
            @cpos_buffer_position = out
            lb_linenum = newlines
         end
         if (false && meta_char(c))
            if (!@_rl_output_meta_chars && false)
               line[out,4] = "\\%03o" % c.ord

               if (lpos + 4 >= @_rl_screenwidth)
                  temp = @_rl_screenwidth - lpos
                  @inv_lbreaks[newlines+=1] = out + temp
                  lpos = 4 - temp
               else
                  lpos += 4
               end
               out += 4
            else
               line[out,1] = c
               out += 1
               lpos+=1
               if (lpos >= @_rl_screenwidth)
                  @inv_lbreaks[newlines+=1] = out
                  @_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
                  lpos = 0
               end
            end

         elsif (c == "\t")

            newout = out + 8 - lpos % 8
            temp = newout - out
            if (lpos + temp >= @_rl_screenwidth)
               temp2 = @_rl_screenwidth - lpos
               @inv_lbreaks[newlines+=1] = out + temp2
               lpos = temp - temp2
               while (out < newout)
                  line[out,1] = ' '
                  out += 1
               end

            else

               while (out < newout)
                  line[out,1] = ' '
                  out += 1
               end
               lpos += temp
            end

         elsif (c == "\n" && !@_rl_horizontal_scroll_mode && @_rl_term_up)
            line[out,1] = 0.chr # XXX - sentinel
            out += 1
            @inv_lbreaks[newlines+=1] = out
            lpos = 0
         elsif (ctrl_char(c) || c == RUBOUT)
            line[out,1] = '^'
            out += 1
            lpos+=1
            if (lpos >= @_rl_screenwidth)
               @inv_lbreaks[newlines+=1] = out
               @_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
               lpos = 0
            end
            # NOTE: c[0].ord works identically on both 1.8 and 1.9
            line[out,1] = ctrl_char(c) ? (c[0].ord|0x40).chr.upcase : '?'
            out += 1
            lpos+=1
            if (lpos >= @_rl_screenwidth)
               @inv_lbreaks[newlines+=1] = out
               @_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
               lpos = 0
            end
         else

            if (!@rl_byte_oriented)
               _rl_wrapped_multicolumn = 0
               if (@_rl_screenwidth < lpos + wc_width)
                  for i in lpos ... @_rl_screenwidth
                     # The space will be removed in update_line()
                     line[out,1] = ' '
                     out += 1
                     _rl_wrapped_multicolumn+=1
                     lpos+=1
                     if (lpos >= @_rl_screenwidth)
                        @inv_lbreaks[newlines+=1] = out
                        @_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
                        lpos = 0
                     end
                  end
               end
               if (_in == @rl_point)
                  @cpos_buffer_position = out
                  lb_linenum = newlines
               end
               line[out,wc_bytes] = @rl_line_buffer[_in,wc_bytes]
               out += wc_bytes
               for i in 0 ... wc_width
                  lpos+=1
                  if (lpos >= @_rl_screenwidth)
                     @inv_lbreaks[newlines+=1] = out
                     @_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
                     lpos = 0
                  end
               end
            else
               line[out,1] = c
               out += 1
               lpos+=1
               if (lpos >= @_rl_screenwidth)
                  @inv_lbreaks[newlines+=1] = out
                  @_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
                  lpos = 0
               end
            end

         end

         if (!@rl_byte_oriented)
            _in += wc_bytes
            case @encoding
            when 'E'
               wc = @rl_line_buffer[_in,@rl_end - _in].scan(/./me)[0]
               wc_bytes = wc ? wc.length : 1
            when 'S'
               wc = @rl_line_buffer[_in,@rl_end - _in].scan(/./ms)[0]
               wc_bytes = wc ? wc.length : 1
            when 'U'
               wc = @rl_line_buffer[_in,@rl_end - _in].scan(/./mu)[0]
               wc_bytes = wc ? wc.length : 1
            when 'X'
               wc = @rl_line_buffer[_in,@rl_end - _in].force_encoding(@encoding_name)[0]
               wc_bytes = wc ? wc.bytesize : 1
            end

         else
            _in+=1
         end
      end

      line[out,1] = 0.chr

      if (@cpos_buffer_position < 0)
         @cpos_buffer_position = out
         lb_linenum = newlines
      end

      inv_botlin = lb_botlin = newlines
      @inv_lbreaks[newlines+1] = out
      cursor_linenum = lb_linenum

      # CPOS_BUFFER_POSITION == position in buffer where cursor should be placed.
      #   CURSOR_LINENUM == line number where the cursor should be placed.

      # PWP: now is when things get a bit hairy.  The visible and invisible
      #   line buffers are really multiple lines, which would wrap every
      #   (screenwidth - 1) characters.  Go through each in turn, finding
      #   the changed region and updating it.  The line order is top to bottom.

      # If we can move the cursor up and down, then use multiple lines,
      #   otherwise, let long lines display in a single terminal line, and
      #   horizontally scroll it.

      if (!@_rl_horizontal_scroll_mode && @_rl_term_up)
         if (!@rl_display_fixed || @forced_display)
            @forced_display = false
            # If we have more than a screenful of material to display, then
            #   only display a screenful.  We should display the last screen,
            #   not the first.
            if (out >= @_rl_screenchars)
               if (!@rl_byte_oriented)
                  out = _rl_find_prev_mbchar(line, @_rl_screenchars, MB_FIND_ANY)
               else
                  out = @_rl_screenchars - 1
               end
            end

            # The first line is at character position 0 in the buffer.  The
            #   second and subsequent lines start at inv_lbreaks[N], offset by
            #   OFFSET (which has already been calculated above).

            # For each line in the buffer, do the updating display.
            for linenum in  0 .. inv_botlin
               # This can lead us astray if we execute a program that changes
               #the locale from a non-multibyte to a multibyte one.
               o_cpos = @_rl_last_c_pos
               @cpos_adjusted = false
               update_line(@visible_line,vis_pos(linenum), inv_line(linenum), linenum,
               vis_llen(linenum), inv_llen(linenum), inv_botlin)

               if (linenum == 0 && !@rl_byte_oriented &&
                  !@cpos_adjusted &&
                  @_rl_last_c_pos != o_cpos &&
                  @_rl_last_c_pos > @wrap_offset &&
                  o_cpos < @prompt_last_invisible)
                  @_rl_last_c_pos -= @wrap_offset
               end

               # If this is the line with the prompt, we might need to
               # compensate for invisible characters in the new line. Do
               # this only if there is not more than one new line (which
               # implies that we completely overwrite the old visible line)
               # and the new line is shorter than the old.  Make sure we are
               # at the end of the new line before clearing.
               if (linenum == 0 &&
                  inv_botlin == 0 && @_rl_last_c_pos == out &&
                  (@wrap_offset > @visible_wrap_offset) &&
                  (@_rl_last_c_pos < @visible_first_line_len))
                  if !@rl_byte_oriented
                     nleft = @_rl_screenwidth - @_rl_last_c_pos
                  else
                     nleft = @_rl_screenwidth + @wrap_offset - @_rl_last_c_pos
                  end
                  if (nleft!=0)
                     _rl_clear_to_eol(nleft)
                  end
               end

               # Since the new first line is now visible, save its length.
               if (linenum == 0)
                  @visible_first_line_len = (inv_botlin > 0) ? @inv_lbreaks[1] : out - @wrap_offset
               end
            end

            # We may have deleted some lines.  If so, clear the left over
            #   blank ones at the bottom out.
            if (@_rl_vis_botlin > inv_botlin)
               while(linenum <= @_rl_vis_botlin)
                  tt = vis_chars(linenum)
                  _rl_move_vert(linenum)
                  _rl_move_cursor_relative(0, tt)
                  _rl_clear_to_eol((linenum == @_rl_vis_botlin) ? tt.length : @_rl_screenwidth)
                  linenum += 1
               end
            end
            @_rl_vis_botlin = inv_botlin

            # CHANGED_SCREEN_LINE is set to 1 if we have moved to a
            #   different screen line during this redisplay.
            changed_screen_line = @_rl_last_v_pos != cursor_linenum
            if (changed_screen_line)
               _rl_move_vert(cursor_linenum)
               # If we moved up to the line with the prompt using _rl_term_up,
               # the physical cursor position on the screen stays the same,
               # but the buffer position needs to be adjusted to account
               # for invisible characters.
               if (@rl_byte_oriented && cursor_linenum == 0 && @wrap_offset!=0)
                  @_rl_last_c_pos += @wrap_offset
               end
            end
            # We have to reprint the prompt if it contains invisible
            #   characters, since it's not generally OK to just reprint
            #   the characters from the current cursor position.  But we
            #   only need to reprint it if the cursor is before the last
            #   invisible character in the prompt string.
            nleft = @prompt_visible_length + @wrap_offset
            if (cursor_linenum == 0 && @wrap_offset > 0 && @_rl_last_c_pos > 0 &&
               @_rl_last_c_pos < prompt_ending_index() && @local_prompt)
               if (@_rl_term_cr)
                  @rl_outstream.write(@_rl_term_cr)
               end
               _rl_output_some_chars(@local_prompt,0,nleft)
               if !@rl_byte_oriented
                  @_rl_last_c_pos = _rl_col_width(@local_prompt, 0, nleft) - @wrap_offset
               else
                  @_rl_last_c_pos = nleft
               end
            end

            # Where on that line?  And where does that line start
            #   in the buffer?
            pos = @inv_lbreaks[cursor_linenum]
            # nleft == number of characters in the line buffer between the
            #   start of the line and the desired cursor position.
            nleft = @cpos_buffer_position - pos

            # NLEFT is now a number of characters in a buffer.  When in a
            #   multibyte locale, however, _rl_last_c_pos is an absolute cursor
            #   position that doesn't take invisible characters in the prompt
            #   into account.  We use a fudge factor to compensate.

            # Since _rl_backspace() doesn't know about invisible characters in the
            #   prompt, and there's no good way to tell it, we compensate for
            #   those characters here and call _rl_backspace() directly.

            if (@wrap_offset!=0 && cursor_linenum == 0 && nleft < @_rl_last_c_pos)
               # TX == new physical cursor position in multibyte locale.
               if !@rl_byte_oriented
                  tx = _rl_col_width(@visible_line, pos, pos+nleft) - @visible_wrap_offset
               else
                  tx = nleft
               end
               if (@_rl_last_c_pos > tx)
                  _rl_backspace(@_rl_last_c_pos - tx) # XXX
                  @_rl_last_c_pos = tx
               end
            end
            # We need to note that in a multibyte locale we are dealing with
            #   _rl_last_c_pos as an absolute cursor position, but moving to a
            #   point specified by a buffer position (NLEFT) that doesn't take
            #   invisible characters into account.
            if !@rl_byte_oriented
               _rl_move_cursor_relative(nleft, @invisible_line,pos)
            elsif (nleft != @_rl_last_c_pos)
               _rl_move_cursor_relative(nleft, @invisible_line,pos)
            end
         end

      else           # Do horizontal scrolling.

         # Always at top line.
         @_rl_last_v_pos = 0

         # Compute where in the buffer the displayed line should start.  This
         # will be LMARGIN.

         # The number of characters that will be displayed before the cursor.
         ndisp = @cpos_buffer_position - @wrap_offset
         nleft  = @prompt_visible_length + @wrap_offset
         # Where the new cursor position will be on the screen.  This can be
         # longer than SCREENWIDTH; if it is, lmargin will be adjusted.
         phys_c_pos = @cpos_buffer_position - (@last_lmargin!=0 ? @last_lmargin : @wrap_offset)
         t = @_rl_screenwidth / 3

         # If the number of characters had already exceeded the screenwidth,
         #last_lmargin will be > 0.

         # If the number of characters to be displayed is more than the screen
         # width, compute the starting offset so that the cursor is about
         # two-thirds of the way across the screen.
         if (phys_c_pos > @_rl_screenwidth - 2)
            lmargin = @cpos_buffer_position - (2 * t)
            if (lmargin < 0)
               lmargin = 0
            end
            # If the left margin would be in the middle of a prompt with
            #   invisible characters, don't display the prompt at all.
            if (@wrap_offset!=0 && lmargin > 0 && lmargin < nleft)
               lmargin = nleft
            end
         elsif (ndisp < @_rl_screenwidth - 2)      # XXX - was -1
            lmargin = 0
         elsif (phys_c_pos < 1)
            # If we are moving back towards the beginning of the line and
            #   the last margin is no longer correct, compute a new one.
            lmargin = ((@cpos_buffer_position - 1) / t) * t # XXX
            if (@wrap_offset!=0 && lmargin > 0 && lmargin < nleft)
               lmargin = nleft
            end
         else
            lmargin = @last_lmargin
         end

         # If the first character on the screen isn't the first character
         #in the display line, indicate this with a special character.
         if (lmargin > 0)
            line[lmargin,1] = '<'
         end

         # If SCREENWIDTH characters starting at LMARGIN do not encompass
         # the whole line, indicate that with a special character at the
         # right edge of the screen.  If LMARGIN is 0, we need to take the
         # wrap offset into account.
         t = lmargin + m_offset(lmargin, @wrap_offset) + @_rl_screenwidth
         if (t < out)
            line[t - 1,1] = '>'
         end
         if (!@rl_display_fixed || @forced_display || lmargin != @last_lmargin)

            @forced_display = false
            update_line(@visible_line,@last_lmargin,@invisible_line[lmargin..-1],
            0,
            @_rl_screenwidth + @visible_wrap_offset,
            @_rl_screenwidth + (lmargin ? 0 : @wrap_offset),
            0)
            # If the visible new line is shorter than the old, but the number
            #   of invisible characters is greater, and we are at the end of
            #   the new line, we need to clear to eol.
            t = @_rl_last_c_pos - m_offset(lmargin, @wrap_offset)
            if ((m_offset(lmargin, @wrap_offset) > @visible_wrap_offset) &&
               (@_rl_last_c_pos == out) &&
               t < @visible_first_line_len)

               nleft = @_rl_screenwidth - t
               _rl_clear_to_eol(nleft)
            end
            @visible_first_line_len = out - lmargin - m_offset(lmargin, @wrap_offset)
            if (@visible_first_line_len > @_rl_screenwidth)
               @visible_first_line_len = @_rl_screenwidth
            end
            _rl_move_cursor_relative(@cpos_buffer_position - lmargin, @invisible_line ,lmargin)
            @last_lmargin = lmargin
         end
      end
      @rl_outstream.flush

      # Swap visible and non-visible lines.
      @visible_line,@invisible_line = @invisible_line,@visible_line
      @vis_lbreaks,@inv_lbreaks = @inv_lbreaks,@vis_lbreaks

      @rl_display_fixed = false
      # If we are displaying on a single line, and last_lmargin is > 0, we
      #   are not displaying any invisible characters, so set visible_wrap_offset
      #   to 0.
      if (@_rl_horizontal_scroll_mode && @last_lmargin!=0)
         @visible_wrap_offset = 0
      else
         @visible_wrap_offset = @wrap_offset
      end
   end

   def rl_line_buffer
      @rl_line_buffer.tr(0.chr, '')
   end

   # Tell the update routines that we have moved onto a new (empty) line.
   def rl_on_new_line()
      if (@visible_line)
         @visible_line[0,1] = 0.chr
      end
      @_rl_last_c_pos = @_rl_last_v_pos = 0
      @_rl_vis_botlin = @last_lmargin = 0
      if (@vis_lbreaks)
         @vis_lbreaks[0] = @vis_lbreaks[1] = 0
      end
      @visible_wrap_offset = 0
      0
   end

   def rl_reset_line_state()
      rl_on_new_line()

      @rl_display_prompt = @rl_prompt ? @rl_prompt : ""
      @forced_display = true
      0
   end

   def _rl_vi_initialize_line
      rl_unsetstate(RL_STATE_VICMDONCE)
   end

   # Initialize readline (and terminal if not already).
   def rl_initialize()
      # If we have never been called before, initialize the
      #   terminal and data structures.
      if (!@rl_initialized)
         rl_setstate(RL_STATE_INITIALIZING)
         readline_initialize_everything()
         rl_unsetstate(RL_STATE_INITIALIZING)
         @rl_initialized = true
         rl_setstate(RL_STATE_INITIALIZED)
      end

      # Initalize the current line information.
      _rl_init_line_state()

      # We aren't done yet.  We haven't even gotten started yet!
      @rl_done = false
      rl_unsetstate(RL_STATE_DONE)

      # Tell the history routines what is going on.
      _rl_start_using_history()

      # Make the display buffer match the state of the line.
      rl_reset_line_state()

      # No such function typed yet.
      @rl_last_func = nil

      # Parsing of key-bindings begins in an enabled state.
      @_rl_parsing_conditionalized_out = 0

      if (@rl_editing_mode == @vi_mode)
         _rl_vi_initialize_line()
      end
      # Each line starts in insert mode (the default).
      _rl_set_insert_mode(RL_IM_DEFAULT, 1)

      return 0
   end

   def _rl_strip_prompt(pmt)
      return expand_prompt(pmt).first
   end

   def _rl_col_width(string,start,_end)
      return 0 if _end <= start

      index = string.index(0.chr)
      str = index ? string[0,index] : string
      width = 0

      case @encoding
      when 'N'
         return (_end - start)
      when 'U'
         str[start ... _end].scan(/./mu).each {|s| width += s.unpack('U').first >= 0x1000 ? 2 : 1 }
      when 'S'
         str[start ... _end].scan(/./ms).each {|s| width += s.length }
      when 'E'
         str[start ... _end].scan(/./me).each {|s| width += s.length }
      when 'X'
         str[start ... _end].force_encoding(@encoding_name).codepoints.each {|s| width += s > 0x1000 ? 2 : 1 }
      end
      width
   end

   # Write COUNT characters from STRING to the output stream.
   def _rl_output_some_chars(string,start,count)
      @_rl_out_stream.write(string[start,count])
   end

   # Tell the update routines that we have moved onto a new line with the
   #   prompt already displayed.  Code originally from the version of readline
   #   distributed with CLISP.  rl_expand_prompt must have already been called
   #   (explicitly or implicitly).  This still doesn't work exactly right.
   def rl_on_new_line_with_prompt()
      # Initialize visible_line and invisible_line to ensure that they can hold
      #   the already-displayed prompt.
      prompt_size = @rl_prompt.length + 1
      init_line_structures(prompt_size)

      # Make sure the line structures hold the already-displayed prompt for
      #   redisplay.
      lprompt = @local_prompt ? @local_prompt : @rl_prompt
      @visible_line[0,lprompt.length] = lprompt
      @invisible_line[0,lprompt.length] = lprompt

      # If the prompt contains newlines, take the last tail.
      prompt_last_line = rl_prompt.rindex("\n")
      if prompt_last_line.nil?
         prompt_last_line = @rl_prompt
      else
         prompt_last_line = @rl_prompt[prompt_last_line..-1]
      end
      l = prompt_last_line.length
      if !@rl_byte_oriented
         @_rl_last_c_pos = _rl_col_width(prompt_last_line, 0, l)
      else
         @_rl_last_c_pos = l
      end

      # Dissect prompt_last_line into screen lines. Note that here we have
      #   to use the real screenwidth. Readline's notion of screenwidth might be
      #   one less, see terminal.c.
      real_screenwidth = @_rl_screenwidth + (@_rl_term_autowrap ? 0 : 1)
      @_rl_last_v_pos = l / real_screenwidth
      # If the prompt length is a multiple of real_screenwidth, we don't know
      #   whether the cursor is at the end of the last line, or already at the
      #   beginning of the next line. Output a newline just to be safe.
      if (l > 0 && (l % real_screenwidth) == 0)
         _rl_output_some_chars("\n",0,1)
      end
      @last_lmargin = 0

      newlines = 0
      i = 0
      while (i <= l)
         @_rl_vis_botlin = newlines
         @vis_lbreaks[newlines] = i
         newlines += 1
         i += real_screenwidth
      end
      @vis_lbreaks[newlines] = l
      @visible_wrap_offset = 0

      @rl_display_prompt = @rl_prompt  # XXX - make sure it's set

      return 0
   end

   def readline_internal_setup()
      @_rl_in_stream = @rl_instream
      @_rl_out_stream = @rl_outstream

      if (@rl_startup_hook)
         send(@rl_startup_hook)
      end

      # If we're not echoing, we still want to at least print a prompt, because
      #   rl_redisplay will not do it for us.  If the calling application has a
      #   custom redisplay function, though, let that function handle it.
      if (!@readline_echoing_p && @rl_redisplay_function == :rl_redisplay)
         if (@rl_prompt && !@rl_already_prompted)
            nprompt = _rl_strip_prompt(@rl_prompt)
            @_rl_out_stream.write(nprompt)
            @_rl_out_stream.flush
         end
      else
         if (@rl_prompt && @rl_already_prompted)
            rl_on_new_line_with_prompt()
         else
            rl_on_new_line()
         end
         send(@rl_redisplay_function)
      end

      if (@rl_editing_mode == @vi_mode)
         rl_vi_insertion_mode(1, 'i')
      end
      if (@rl_pre_input_hook)
         send(@rl_pre_input_hook)
      end
   end

   # Create a default argument.
   def _rl_reset_argument()
      @rl_numeric_arg = @rl_arg_sign = 1
      @rl_explicit_arg = false
      @_rl_argcxt = 0
   end

   # Ring the terminal bell.
   def rl_ding()
      if @MessageBeep
         @MessageBeep.Call(0)
      elsif @readline_echoing_p
         if @_rl_bell_preference == VISIBLE_BELL
            if (@_rl_visible_bell)
               @_rl_out_stream.write(@_rl_visible_bell.chr)
            else
               $stderr.write("\007")
               $stderr.flush
            end
         elsif @_rl_bell_preference == AUDIBLE_BELL
            $stderr.write("\007")
            $stderr.flush
         end
         return 0
      end
      return -1
   end

   def _rl_search_getchar(cxt)
      # Read a key and decide how to proceed.
      rl_setstate(RL_STATE_MOREINPUT)
      c = cxt.lastc = rl_read_key()
      rl_unsetstate(RL_STATE_MOREINPUT)
      if !@rl_byte_oriented
         cxt.mb = ""
         c = cxt.lastc = _rl_read_mbstring(cxt.lastc, cxt.mb, MB_LEN_MAX)
      end
      c
   end

   def endsrch_char(c)
      ((ctrl_char(c) || meta_char(c) || (c) == RUBOUT) && ((c) != "\C-G"))
   end

   def _rl_input_available
      IO.select([ $stdin ], nil, [ $stdin ], @_keyboard_input_timeout)
   end

   # Process just-read character C according to isearch context CXT.  Return
   #   -1 if the caller should just free the context and return, 0 if we should
   #   break out of the loop, and 1 if we should continue to read characters.
   def _rl_isearch_dispatch(cxt, c)
      f = nil

      # Translate the keys we do something with to opcodes.
      if (c && @_rl_keymap[c])
         f = @_rl_keymap[c]
         if (f == :rl_reverse_search_history)
            cxt.lastc = (cxt.sflags & SF_REVERSE)!=0 ? -1 : -2
         elsif (f == :rl_forward_search_history)
            cxt.lastc = (cxt.sflags & SF_REVERSE)!=0 ? -2 : -1
         elsif (f == :rl_rubout)
            cxt.lastc = -3
         elsif (c == "\C-G")
            cxt.lastc = -4
         elsif (c == "\C-W")  # XXX
            cxt.lastc = -5
         elsif (c == "\C-Y")  # XXX
            cxt.lastc = -6
         end
      end

      # The characters in isearch_terminators (set from the user-settable
      #   variable isearch-terminators) are used to terminate the search but
      #   not subsequently execute the character as a command.  The default
      #   value is "\033\012" (ESC and C-J).
      if (cxt.search_terminators.include?(cxt.lastc))
         # ESC still terminates the search, but if there is pending
         #input or if input arrives within 0.1 seconds (on systems
         #with select(2)) it is used as a prefix character
         #with rl_execute_next.  WATCH OUT FOR THIS!  This is intended
         #to allow the arrow keys to be used like ^F and ^B are used
         #to terminate the search and execute the movement command.
         #XXX - since _rl_input_available depends on the application-
         #settable keyboard timeout value, this could alternatively
         #use _rl_input_queued(100000)
         if (cxt.lastc == ESC && _rl_input_available())
            rl_execute_next(ESC)
         end
         return (0)
      end

      if !@rl_byte_oriented
         if (cxt.lastc.class == String && (cxt.mb.length == 1) && endsrch_char(cxt.lastc))
            # This sets rl_pending_input to c; it will be picked up the next
            #   time rl_read_key is called.
            rl_execute_next(cxt.lastc)
            return (0)
         end
      elsif (cxt.lastc.class == String && endsrch_char(cxt.lastc))
         # This sets rl_pending_input to LASTC; it will be picked up the next
         #   time rl_read_key is called.
         rl_execute_next(cxt.lastc)
         return (0)
      end

      # Now dispatch on the character.  `Opcodes' affect the search string or
      #   state.  Other characters are added to the string.
      case (cxt.lastc)

         # search again
      when -1
         if (cxt.search_string_index == 0)
            if (@last_isearch_string)
               cxt.search_string_size = 64 + @last_isearch_string_len
               cxt.search_string = @last_isearch_string.dup
               cxt.search_string_index = @last_isearch_string_len
               rl_display_search(cxt.search_string, (cxt.sflags & SF_REVERSE)!=0, -1)
            else
               return (1)
            end
         elsif (cxt.sflags & SF_REVERSE)!=0
            cxt.sline_index-=1
         elsif (cxt.sline_index != cxt.sline_len)
            cxt.sline_index+=1
         else
            rl_ding()
         end

         # switch directions
      when -2
         cxt.direction = -cxt.direction
         if (cxt.direction < 0)
            cxt.sflags |= SF_REVERSE
         else
            cxt.sflags &= ~SF_REVERSE
         end
         # delete character from search string.
      when -3  # C-H, DEL
         # This is tricky.  To do this right, we need to keep a
         # stack of search positions for the current search, with
         # sentinels marking the beginning and end.  But this will
         # do until we have a real isearch-undo.
         if (cxt.search_string_index == 0)
            rl_ding()
         else
            cxt.search_string_index -= 1
            cxt.search_string.chop!
         end
      when -4  # C-G, abort
         rl_replace_line(cxt.lines[cxt.save_line], false)
         @rl_point = cxt.save_point
         @rl_mark = cxt.save_mark
         rl_restore_prompt()
         rl_clear_message()
         return -1
      when -5  # C-W
         # skip over portion of line we already matched and yank word
         wstart = @rl_point + cxt.search_string_index
         if (wstart >= @rl_end)
            rl_ding()
         else
            # if not in a word, move to one.
            cval = _rl_char_value(@rl_line_buffer, wstart)
            if (!_rl_walphabetic(cval))
               rl_ding()
            else
               if !@rl_byte_oriented
                  n = _rl_find_next_mbchar(@rl_line_buffer, wstart, 1, MB_FIND_NONZERO)
               else
                  n = wstart+1
               end
               while (n < @rl_end)
                  cval = _rl_char_value(@rl_line_buffer, n)
                  break if !_rl_walphabetic(cval)
                  if !@rl_byte_oriented
                     n = _rl_find_next_mbchar(@rl_line_buffer, n, 1, MB_FIND_NONZERO)
                  else
                     n = n+1
                  end
               end
               wlen = n - wstart + 1
               if (cxt.search_string_index + wlen + 1 >= cxt.search_string_size)
                  cxt.search_string_size += wlen + 1
               end
               cxt.search_string[cxt.search_string_index..-1] = @rl_line_buffer[wstart,wlen]
               cxt.search_string_index += wlen
            end
         end

      when -6  # C-Y
         # skip over portion of line we already matched and yank rest
         wstart = @rl_point + cxt.search_string_index
         if (wstart >= @rl_end)
            rl_ding()
         else
            n = @rl_end - wstart + 1
            if (cxt.search_string_index + n + 1 >= cxt.search_string_size)
               cxt.search_string_size += n + 1
            end
            cxt.search_string[cxt.search_string_index..-1] = @rl_line_buffer[wstart,n]
         end

         # Add character to search string and continue search.
      else
         if (cxt.search_string_index + 2 >= cxt.search_string_size)
            cxt.search_string_size += 128
         end
         if !@rl_byte_oriented
            for j in 0 ... cxt.mb.length
               cxt.search_string << cxt.mb[j,1]
               cxt.search_string_index += 1
            end
         else
            cxt.search_string << c
            cxt.search_string_index += 1
         end
      end

      while (cxt.sflags &= ~(SF_FOUND|SF_FAILED))!=0
         limit = cxt.sline_len - cxt.search_string_index + 1
         # Search the current line.
         while ((cxt.sflags & SF_REVERSE)!=0 ? (cxt.sline_index >= 0) : (cxt.sline_index < limit))

            if (cxt.search_string[0,cxt.search_string_index] == cxt.sline[cxt.sline_index,cxt.search_string_index])
               cxt.sflags |= SF_FOUND
               break
            else
               cxt.sline_index += cxt.direction
            end
         end
         break if (cxt.sflags & SF_FOUND)!=0

         # Move to the next line, but skip new copies of the line
         # we just found and lines shorter than the string we're
         # searching for.
         begin
            # Move to the next line.
            cxt.history_pos += cxt.direction

            # At limit for direction?
            if ((cxt.sflags & SF_REVERSE)!=0 ? (cxt.history_pos < 0) : (cxt.history_pos == cxt.hlen))
               cxt.sflags |= SF_FAILED
               break
            end

            # We will need these later.
            cxt.sline = cxt.lines[cxt.history_pos]
            cxt.sline_len = cxt.sline.length
         end while ((cxt.prev_line_found && cxt.prev_line_found == cxt.lines[cxt.history_pos]) ||
         (cxt.search_string_index > cxt.sline_len))

         break if (cxt.sflags & SF_FAILED)!=0

         # Now set up the line for searching...
         cxt.sline_index = (cxt.sflags & SF_REVERSE)!=0 ? cxt.sline_len - cxt.search_string_index : 0
      end

      if (cxt.sflags & SF_FAILED)!=0
         # We cannot find the search string.  Ding the bell.
         rl_ding()
         cxt.history_pos = cxt.last_found_line
         return 1
      end

      # We have found the search string.  Just display it.  But don't
      #   actually move there in the history list until the user accepts
      #   the location.
      if (cxt.sflags & SF_FOUND)!=0
         cxt.prev_line_found = cxt.lines[cxt.history_pos]
         rl_replace_line(cxt.lines[cxt.history_pos], false)
         @rl_point = cxt.sline_index
         cxt.last_found_line = cxt.history_pos
         rl_display_search(cxt.search_string, (cxt.sflags & SF_REVERSE)!=0, (cxt.history_pos == cxt.save_line) ? -1 : cxt.history_pos)
      end
      1
   end

   # How to clear things from the "echo-area".
   def rl_clear_message()
      @rl_display_prompt = @rl_prompt
      if (@msg_saved_prompt)
         rl_restore_prompt()
         @msg_saved_prompt = nil
      end
      send(@rl_redisplay_function)
      0
   end

   def _rl_isearch_fini(cxt)
      # First put back the original state.
      @rl_line_buffer = cxt.lines[cxt.save_line].dup
      rl_restore_prompt()

      # Save the search string for possible later use.
      @last_isearch_string = cxt.search_string
      @last_isearch_string_len = cxt.search_string_index
      cxt.search_string = nil

      if (cxt.last_found_line < cxt.save_line)
         rl_get_previous_history(cxt.save_line - cxt.last_found_line, 0)
      else
         rl_get_next_history(cxt.last_found_line - cxt.save_line, 0)
      end

      # If the string was not found, put point at the end of the last matching
      #   line.  If last_found_line == orig_line, we didn't find any matching
      #   history lines at all, so put point back in its original position.
      if (cxt.sline_index < 0)

         if (cxt.last_found_line == cxt.save_line)
            cxt.sline_index = cxt.save_point
         else
            cxt.sline_index = @rl_line_buffer.delete(0.chr).length
         end
         @rl_mark = cxt.save_mark
      end

      @rl_point = cxt.sline_index
      # Don't worry about where to put the mark here; rl_get_previous_history
      #   and rl_get_next_history take care of it.
      rl_clear_message()
   end


   def _rl_isearch_cleanup(cxt, r)
      if (r >= 0)
         _rl_isearch_fini(cxt)
      end
      ctx = nil
      @_rl_iscxt = nil

      rl_unsetstate(RL_STATE_ISEARCH)

      r != 0
   end

   # Do the command associated with KEY in MAP.
   #   If the associated command is really a keymap, then read
   #   another key, and dispatch into that map.
   def _rl_dispatch(key, map)
      @_rl_dispatching_keymap = map
      return _rl_dispatch_subseq(key, map, false)
   end


   def _rl_dispatch_subseq(key, map, got_subseq)
      func = map[key]
      if (func)
         @rl_executing_keymap = map

         @rl_dispatching = true
         rl_setstate(RL_STATE_DISPATCHING)
         send(map[key],@rl_numeric_arg * @rl_arg_sign, key)
         rl_unsetstate(RL_STATE_DISPATCHING)
         @rl_dispatching = false
         if (@rl_pending_input == 0 && map[key] != :rl_digit_argument)
            @rl_last_func = map[key]
         end
      else
         if(map.keys.detect{|x| x =~ /^#{Regexp.escape(key)}/})
            key += _rl_subseq_getchar(key)
            return _rl_dispatch_subseq(key,map,got_subseq)
         elsif(key.length>1 && key[1].ord < 0x7f)
            _rl_abort_internal()
            return -1
         else
            @rl_dispatching = true
            rl_setstate(RL_STATE_DISPATCHING)
            send(:rl_insert,@rl_numeric_arg * @rl_arg_sign, key)
            rl_unsetstate(RL_STATE_DISPATCHING)
            @rl_dispatching = false
         end
      end
      0
   end

   # Add KEY to the buffer of characters to be read.  Returns 1 if the
   #   character was stuffed correctly; 0 otherwise.
   def rl_stuff_char(key)
      return 0 if (ibuffer_space() == 0)

      if (key == EOF)
         key = NEWLINE
         @rl_pending_input = EOF
         rl_setstate(RL_STATE_INPUTPENDING)
      end
      @ibuffer[@push_index] = key
      @push_index += 1
      if (@push_index >= @ibuffer_len)
         @push_index = 0
      end

      return 1
   end

   begin
      # Cygwin will look like Windows, but we want to treat it like a Posix OS:
      raise LoadError, "Cygwin is a Posix OS." if RUBY_PLATFORM =~ /\bcygwin\b/i
      raise LoadError, "Not Windows" if RUBY_PLATFORM !~ /mswin|mingw/

      if RUBY_VERSION < '1.9.1'
         require 'Win32API'
      else
         require 'dl'
         class Win32API
            DLL = {}
            TYPEMAP = {"0" => DL::TYPE_VOID, "S" => DL::TYPE_VOIDP, "I" => DL::TYPE_LONG}

            def initialize(dllname, func, import, export = "0", calltype = :stdcall)
               @proto = [import].join.tr("VPpNnLlIi", "0SSI").sub(/^(.)0*$/, '\1')
               handle = DLL[dllname] ||= DL.dlopen(dllname)
               @func = DL::CFunc.new(handle[func], TYPEMAP[export.tr("VPpNnLlIi", "0SSI")], func, calltype)
            end

            def call(*args)
               import = @proto.split("")
               args.each_with_index do |x, i|
                  args[i], = [x == 0 ? nil : x].pack("p").unpack("l!*") if import[i] == "S"
                  args[i], = [x].pack("I").unpack("i") if import[i] == "I"
               end
               ret, = @func.call(args)
               return ret || 0
            end

            alias Call call
         end
      end

      STD_OUTPUT_HANDLE = -11
      STD_INPUT_HANDLE  = -10
      KEY_EVENT = 1
      VK_SHIFT = 0x10
      VK_MENU = 0x12
      VK_LMENU = 0xA4
      VK_RMENU = 0xA5

      LEFT_CTRL_PRESSED         = 0x0008
      RIGHT_CTRL_PRESSED        = 0x0004
      LEFT_ALT_PRESSED          = 0x0002
      RIGHT_ALT_PRESSED         = 0x0001

      @getch = Win32API.new("msvcrt", "_getch", [], 'I')
      @kbhit = Win32API.new("msvcrt", "_kbhit", [], 'I')
      @GetStdHandle = Win32API.new("kernel32","GetStdHandle",['L'],'L')
      @SetConsoleCursorPosition = Win32API.new("kernel32","SetConsoleCursorPosition",['L','L'],'L')
      @GetConsoleScreenBufferInfo = Win32API.new("kernel32","GetConsoleScreenBufferInfo",['L','P'],'L')
      @FillConsoleOutputCharacter = Win32API.new("kernel32","FillConsoleOutputCharacter",['L','L','L','L','P'],'L')
      @ReadConsoleInput = Win32API.new( "kernel32", "ReadConsoleInput", ['L', 'P', 'L', 'P'], 'L' )
      @MessageBeep = Win32API.new("user32","MessageBeep",['L'],'L')
      @GetKeyboardState = Win32API.new("user32","GetKeyboardState",['P'],'L')
      @GetKeyState = Win32API.new("user32","GetKeyState",['L'],'L')
      @hConsoleHandle = @GetStdHandle.Call(STD_OUTPUT_HANDLE)
      @hConsoleInput =  @GetStdHandle.Call(STD_INPUT_HANDLE)
      @pending_count = 0
      @pending_key = nil

      begin
        case `chcp`.scan(/\d+$/).first.to_i
        when 936,949,950,51932,51936,50225
          @encoding = "E"
        when 932,50220,50221,20222
          @encoding = "S"
        when 65001
          @encoding = "U"
        else
          @encoding = "N"
        end
      rescue
        @encoding = "N"
      end

      def rl_getc(stream)
         while (@kbhit.Call == 0)
            # If there is no input, yield the processor for other threads
            sleep(@_keyboard_input_timeout)
         end
         c = @getch.Call
         alt = (@GetKeyState.call(VK_LMENU) & 0x80) != 0
         if c==0 || c==0xE0
            while (@kbhit.Call == 0)
               # If there is no input, yield the processor for other threads
               sleep(@_keyboard_input_timeout)
            end
            r = c.chr + @getch.Call.chr
         else
            r = c.chr
         end
         r = "\e"+r if alt
         r
      end

      def rl_gather_tyi()
         chars_avail = @kbhit.Call
         return 0 if(chars_avail<=0)
         k = send(@rl_getc_function,@rl_instream)
         rl_stuff_char(k)
         return 1
      end

   rescue LoadError                  # If we're not on Windows try...

      if ENV["LANG"] =~ /\.UTF-8/
         @encoding = "U"
      elsif ENV["LANG"] =~ /\.EUC/
         @encoding = "E"
      elsif ENV["LANG"] =~ /\.SHIFT/
         @encoding = "S"
      else
         @encoding = "N"
      end

      def rl_getc(stream)
         begin
            c = stream.read(1)
         rescue Errno::EINTR
            c = rl_getc(stream)
         end
         return c ? c : EOF
      end

      def rl_gather_tyi()
         chars_avail = 0
         result = select([@rl_instream],nil,nil,0.1)
         return 0 if result.nil?
         k = send(@rl_getc_function,@rl_instream)
         rl_stuff_char(k)
         return 1
      end
   end

   if (Object.const_defined?('Encoding') and Encoding.respond_to?('default_external'))
      @encoding = "X"      # ruby 1.9.x or greater
      @encoding_name = Encoding.default_external.to_s
   end

   @rl_byte_oriented = @encoding == "N"

   # Read a key, including pending input.
   def rl_read_key()
      @rl_key_sequence_length+=1

      if (@rl_pending_input!=0)
         c = @rl_pending_input
         rl_clear_pending_input()
      else
         # If the user has an event function, then call it periodically.
         if (@rl_event_hook)
            while (@rl_event_hook && (c=rl_get_char()).nil?)

               send(@rl_event_hook)
               if (@rl_done)     # XXX - experimental
                  return ("\n")
               end
               if (rl_gather_tyi() < 0)   # XXX - EIO
                  @rl_done = true
                  return ("\n")
               end
            end

         else

            if (c=rl_get_char()).nil?
               c = send(@rl_getc_function,@rl_instream)
            end
         end
      end

      return (c)
   end


   # Return the amount of space available in the buffer for stuffing
   #   characters.
   def ibuffer_space()
      if (@pop_index > @push_index)
         return (@pop_index - @push_index - 1)
      else
         return (@ibuffer_len - (@push_index - @pop_index))
      end
   end

   # Get a key from the buffer of characters to be read.
   #   Return the key in KEY.
   #   Result is KEY if there was a key, or 0 if there wasn't.
   def rl_get_char()
      if (@push_index == @pop_index)
         return nil
      end
      key = @ibuffer[@pop_index]
      @pop_index += 1

      if (@pop_index >= @ibuffer_len)
         @pop_index = 0
      end

      return key
   end

   # Stuff KEY into the *front* of the input buffer.
   #   Returns non-zero if successful, zero if there is
   #   no space left in the buffer.
   def _rl_unget_char(key)
      if (ibuffer_space()!=0)
         @pop_index-=1
         if (@pop_index < 0)
            @pop_index = @ibuffer_len - 1
         end
         @ibuffer[@pop_index] = key
         return (1)
      end
      return (0)
   end

   def _rl_subseq_getchar(key)
      if (key == ESC)
         rl_setstate(RL_STATE_METANEXT)
      end
      rl_setstate(RL_STATE_MOREINPUT)
      k = rl_read_key()
      rl_unsetstate(RL_STATE_MOREINPUT)
      if (key == ESC)
         rl_unsetstate(RL_STATE_METANEXT)
      end

      return k
   end

   # Clear to the end of the line.  COUNT is the minimum
   #   number of character spaces to clear,
   def _rl_clear_to_eol(count)
      if (@_rl_term_clreol)
         @rl_outstream.write(@_rl_term_clreol)
      elsif (count!=0)
         space_to_eol(count)
      end
   end

   # Clear to the end of the line using spaces.  COUNT is the minimum
   #   number of character spaces to clear,
   def space_to_eol(count)
      if @hConsoleHandle
         csbi = 0.chr * 24
         @GetConsoleScreenBufferInfo.Call(@hConsoleHandle,csbi)
         cursor_pos = csbi[4,4].unpack('L').first
         written = 0.chr * 4
         @FillConsoleOutputCharacter.Call(@hConsoleHandle,0x20,count,cursor_pos,written)
      else
         @rl_outstream.write(' ' * count)
      end
      @_rl_last_c_pos += count
   end

   def _rl_clear_screen()
      if (@_rl_term_clrpag)
         @rl_outstream.write(@_rl_term_clrpag)
      else
         rl_crlf()
      end
   end

   # Move the cursor back.
   def _rl_backspace(count)
      if (@_rl_term_backspace)
         @_rl_out_stream.write(@_rl_term_backspace * count)
      else
         @_rl_out_stream.write("\b"*count)
      end
      0
   end

   # Move to the start of the next line.
   def rl_crlf()
      if (@_rl_term_cr)
         @_rl_out_stream.write(@_rl_term_cr)
      end
      @_rl_out_stream.write("\n")
      return 0
   end

   # Move to the start of the current line.
   def cr()
      if (@_rl_term_cr)
         @_rl_out_stream.write(@_rl_term_cr)
         @_rl_last_c_pos = 0
      end
   end

   def _rl_erase_entire_line()
      cr()
      _rl_clear_to_eol(0)
      cr()
      @rl_outstream.flush
   end

   def _rl_internal_char_cleanup()
      # In vi mode, when you exit insert mode, the cursor moves back
      #   over the previous character.  We explicitly check for that here.
      if (@rl_editing_mode == @vi_mode && @_rl_keymap == @vi_movement_keymap)
         rl_vi_check()
      end

      if (@rl_num_chars_to_read!=0 && @rl_end >= @rl_num_chars_to_read)
         send(@rl_redisplay_function)
         @_rl_want_redisplay = false
         rl_newline(1, "\n")
      end

      if (!@rl_done)
         send(@rl_redisplay_function)
         @_rl_want_redisplay = false
      end

      # If the application writer has told us to erase the entire line if
      #   the only character typed was something bound to rl_newline, do so.
      if (@rl_erase_empty_line && @rl_done && @rl_last_func == :rl_newline &&
         @rl_point == 0 && @rl_end == 0)
         _rl_erase_entire_line()
      end
   end

   def readline_internal_charloop()
      lastc = -1
      eof_found = false

      while (!@rl_done)
         lk = @_rl_last_command_was_kill

         #  send(rl_redisplay_function)
         #  @_rl_want_redisplay = false

         if (@rl_pending_input == 0)
            # Then initialize the argument and number of keys read.
            _rl_reset_argument()
            @rl_key_sequence_length = 0
         end

         rl_setstate(RL_STATE_READCMD)
         c = rl_read_key()
         rl_unsetstate(RL_STATE_READCMD)
         # look at input.c:rl_getc() for the circumstances under which this will
         #be returned; punt immediately on read error without converting it to
         #a newline.
         if (c == READERR)
            eof_found = true
            break
         end

         # EOF typed to a non-blank line is a <NL>.
         if (c == EOF && @rl_end!=0)
            c = NEWLINE
         end

         # The character _rl_eof_char typed to blank line, and not as the
         #previous character is interpreted as EOF.
         if (((c == @_rl_eof_char && lastc != c) || c == EOF) && @rl_end==0)
            eof_found = true
            break
         end
         lastc = c
         if _rl_dispatch(c, @_rl_keymap)== -1
            next
         end

         # If there was no change in _rl_last_command_was_kill, then no kill
         #has taken place.  Note that if input is pending we are reading
         #a prefix command, so nothing has changed yet.
         if (@rl_pending_input == 0 && lk == @_rl_last_command_was_kill)
            @_rl_last_command_was_kill = false
         end
         _rl_internal_char_cleanup()
      end

      eof_found
   end

   # How to abort things.
   def _rl_abort_internal()
      rl_ding()
      rl_clear_message()
      _rl_reset_argument()
      rl_clear_pending_input()

      rl_unsetstate(RL_STATE_MACRODEF)

      @rl_last_func = nil
      #throw :readline_top_level
      send(@rl_redisplay_function)
      @_rl_want_redisplay = false
      0
   end

   def rl_abort(count, key)
      _rl_abort_internal()
   end

   def rl_vi_check()
      if (@rl_point!=0 && @rl_point == @rl_end)
         @rl_point-=1
      end
      0
   end

   def readline_internal_teardown(eof)
      # Restore the original of this history line, iff the line that we
      #   are editing was originally in the history, AND the line has changed.
      entry = current_history()

      if (entry && @rl_undo_list)
         temp = @rl_line_buffer.delete(0.chr).dup
         rl_revert_line(1, 0)
         entry = replace_history_entry(where_history(), @rl_line_buffer, nil)
         entry = nil

         @rl_line_buffer = temp+0.chr
         temp = nil
      end

      # At any rate, it is highly likely that this line has an undo list.  Get
      #   rid of it now.
      if (@rl_undo_list)
         rl_free_undo_list()
      end
      # Restore normal cursor, if available.
      _rl_set_insert_mode(RL_IM_INSERT, 0)

      (eof ? nil : @rl_line_buffer.delete(0.chr))
   end

   # Read a line of input from the global rl_instream, doing output on
   #   the global rl_outstream.
   #   If rl_prompt is non-null, then that is our prompt.
   def readline_internal()
      readline_internal_setup()
      eof = readline_internal_charloop()
      readline_internal_teardown(eof)
   end

   # Read a line of input.  Prompt with PROMPT.  An empty PROMPT means
   #   none.  A return value of NULL means that EOF was encountered.
   def readline(prompt)
      # If we are at EOF return a NULL string.
      if (@rl_pending_input == EOF)
         rl_clear_pending_input()
         return nil
      end

      rl_set_prompt(prompt)

      rl_initialize()
      @readline_echoing_p = true
      if (@rl_prep_term_function)
         send(@rl_prep_term_function,@_rl_meta_flag)
      end
      rl_set_signals()

      value = readline_internal()
      if(@rl_deprep_term_function)
         send(@rl_deprep_term_function)
      end

      rl_clear_signals()

      value
   end

   # Increase the size of RL_LINE_BUFFER until it has enough space to hold
   #   LEN characters.
   def rl_extend_line_buffer(len)
      while (len >= @rl_line_buffer.length)
         @rl_line_buffer << 0.chr * DEFAULT_BUFFER_SIZE
      end
      @the_line = @rl_line_buffer
   end

   # Insert a string of text into the line at point.  This is the only
   #   way that you should do insertion.  _rl_insert_char () calls this
   #   function.  Returns the number of characters inserted.
   def rl_insert_text(string)
      string.delete!(0.chr)
      l = string.length
      return 0 if (l == 0)

      if (@rl_end + l >= @rl_line_buffer.length)
         rl_extend_line_buffer(@rl_end + l)
      end
      @rl_line_buffer[@rl_point,0] = string

      # Remember how to undo this if we aren't undoing something.
      if (!@_rl_doing_an_undo)
         # If possible and desirable, concatenate the undos.
         if ((l == 1) &&
            @rl_undo_list &&
            (@rl_undo_list.what == UNDO_INSERT) &&
            (@rl_undo_list.end == @rl_point) &&
            (@rl_undo_list.end - @rl_undo_list.start < 20))
            @rl_undo_list.end+=1
         else
            rl_add_undo(UNDO_INSERT, @rl_point, @rl_point + l, nil)
         end
      end
      @rl_point += l
      @rl_end += l
      if @rl_line_buffer.length <= @rl_end
         @rl_line_buffer << 0.chr * (@rl_end - @rl_line_buffer.length + 1)
      else
         @rl_line_buffer[@rl_end] = "\0"
      end
      l
   end

   def alloc_undo_entry(what, start, _end, text)
      temp = Struct.new(:what,:start,:end,:text,:next).new
      temp.what = what
      temp.start = start
      temp.end = _end
      temp.text = text
      temp.next = nil
      temp
   end

   #* Remember how to undo something.  Concatenate some undos if that
   #   seems right.
   def rl_add_undo(what, start, _end, text)
      temp = alloc_undo_entry(what, start, _end, text)
      temp.next = @rl_undo_list
      @rl_undo_list = temp
   end


   # Delete the string between FROM and TO.  FROM is inclusive, TO is not.
   #   Returns the number of characters deleted.
   def rl_delete_text(from, to)

      # Fix it if the caller is confused.
      if (from > to)
         from,to = to,from
      end

      # fix boundaries
      if (to > @rl_end)
         to = @rl_end
         if (from > to)
            from = to
         end
      end
      if (from < 0)
         from = 0
      end
      text = rl_copy_text(from, to)
      diff = to - from
      @rl_line_buffer[from...to] = ''
      @rl_line_buffer << 0.chr * diff
      # Remember how to undo this delete.
      if (!@_rl_doing_an_undo)
         rl_add_undo(UNDO_DELETE, from, to, text)
      else
         text = nil
      end
      @rl_end -= diff
      @rl_line_buffer[@rl_end,1] = 0.chr
      return (diff)
   end

   def rl_copy_text(from, to)
      return @rl_line_buffer[from...to]
   end

   # Fix up point so that it is within the line boundaries after killing
   #   text.  If FIX_MARK_TOO is non-zero, the mark is forced within line
   #   boundaries also.

   def __rl_fix_point(x)
      if (x > @rl_end)
         @rl_end
      elsif (x < 0)
         0
      else
         x
      end
   end

   def _rl_fix_point(fix_mark_too)
      @rl_point = __rl_fix_point(@rl_point)
      if (fix_mark_too)
         @rl_mark = __rl_fix_point(@rl_mark)
      end
   end

   # Begin a group.  Subsequent undos are undone as an atomic operation.
   def rl_begin_undo_group()
      rl_add_undo(UNDO_BEGIN, 0, 0, nil)
      @_rl_undo_group_level+=1
      0
   end

   # End an undo group started with rl_begin_undo_group ().
   def rl_end_undo_group()
      rl_add_undo(UNDO_END, 0, 0, nil)
      @_rl_undo_group_level-=1
      0
   end

   def rl_free_undo_list()
      replace_history_data(-1, @rl_undo_list, nil)
      @rl_undo_list = nil
   end

   # Replace the contents of the line buffer between START and END with
   #   TEXT.  The operation is undoable.  To replace the entire line in an
   #   undoable mode, use _rl_replace_text(text, 0, rl_end)
   def _rl_replace_text(text, start, _end)
      rl_begin_undo_group()
      rl_delete_text(start, _end + 1)
      @rl_point = start
      n = rl_insert_text(text)
      rl_end_undo_group()
      n
   end

   # Replace the current line buffer contents with TEXT.  If CLEAR_UNDO is
   #   non-zero, we free the current undo list.
   def rl_replace_line(text, clear_undo)
      len = text.delete(0.chr).length
      @rl_line_buffer = text.dup + 0.chr
      @rl_end = len
      if (clear_undo)
         rl_free_undo_list()
      end
      _rl_fix_point(true)
   end


   # Replace the DATA in the specified history entries, replacing OLD with
   #   NEW.  WHICH says which one(s) to replace:  WHICH == -1 means to replace
   #   all of the history entries where entry->data == OLD; WHICH == -2 means
   #   to replace the `newest' history entry where entry->data == OLD; and
   #   WHICH >= 0 means to replace that particular history entry's data, as
   #   long as it matches OLD.
   def replace_history_data(which,old, new)
      new = new.dup if new
      if (which < -2 || which >= @history_length || @history_length == 0 || @the_history.nil?)
         return
      end
      if (which >= 0)
         entry = @the_history[which]
         if (entry && entry.data == old)
            entry.data = new
         end
         return
      end

      last = -1
      for i in 0 ... @history_length
         entry = @the_history[i]
         if entry.nil?
            next
         end
         if (entry.data == old)
            last = i
            if (which == -1)
               entry.data = new
            end
         end
      end
      if (which == -2 && last >= 0)
         entry = @the_history[last]
         entry.data = new  # XXX - we don't check entry->old
      end
   end

   # Move forward COUNT bytes.
   def rl_forward_byte(count, key)
      if (count < 0)
         return (rl_backward_byte(-count, key))
      end
      if (count > 0)
         _end = @rl_point + count
         lend = @rl_end > 0 ? @rl_end - ((@rl_editing_mode == @vi_mode)?1:0) : @rl_end
         if (_end > lend)
            @rl_point = lend
            rl_ding()
         else
            @rl_point = _end
         end
      end

      if (@rl_end < 0)
         @rl_end = 0
      end
      return 0
   end

   # Move forward COUNT characters.
   def rl_forward_char(count, key)
      if @rl_byte_oriented
         return (rl_forward_byte(count, key))
      end
      if (count < 0)
         return (rl_backward_char(-count, key))
      end
      if (count > 0)
         point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, count, MB_FIND_NONZERO)
         if (@rl_end <= point && @rl_editing_mode == @vi_mode)
            point = _rl_find_prev_mbchar(@rl_line_buffer, @rl_end, MB_FIND_NONZERO)
         end
         if (@rl_point == point)
            rl_ding()
         end
         @rl_point = point
         if (@rl_end < 0)
            @rl_end = 0
         end
      end
      0
   end

   # Backwards compatibility.
   def rl_forward(count, key)
      rl_forward_char(count, key)
   end

   # Move backward COUNT bytes.
   def rl_backward_byte(count, key)
      if (count < 0)
         return (rl_forward_byte(-count, key))
      end
      if (count > 0)
         if (@rl_point < count)
            @rl_point = 0
            rl_ding()
         else
            @rl_point -= count
         end
      end

      if (@rl_point < 0)
         @rl_point = 0
      end
      0
   end

   # Move backward COUNT characters.
   def rl_backward_char(count, key)
      if @rl_byte_oriented
         return (rl_backward_byte(count, key))
      end
      if (count < 0)
         return (rl_forward_char(-count, key))
      end

      if (count > 0)
         point = @rl_point
         while (count > 0 && point > 0)
            point = _rl_find_prev_mbchar(@rl_line_buffer, point, MB_FIND_NONZERO)
            count-=1
         end
         if (count > 0)
            @rl_point = 0
            rl_ding()
         else
            @rl_point = point
         end
      end
      0
   end

   # Backwards compatibility.
   def rl_backward(count, key)
      rl_backward_char(count, key)
   end

   # Move to the beginning of the line.
   def rl_beg_of_line(count, key)
      @rl_point = 0
      0
   end

   # Move to the end of the line.
   def rl_end_of_line(count, key)
      @rl_point = @rl_end
      0
   end

   def _rl_char_value(buf,ind)
      buf[ind,1]
   end

   @_rl_allow_pathname_alphabetic_chars = false
   @pathname_alphabetic_chars = '/-_=~.#$'

   def rl_alphabetic(c)
      if c =~ /\w/
         return true
      end

      return !!(@_rl_allow_pathname_alphabetic_chars &&
      @pathname_alphabetic_chars[c])
   end

   def _rl_walphabetic(c)
      rl_alphabetic(c)
   end

   # Move forward a word.  We do what Emacs does.  Handles multibyte chars.
   def rl_forward_word(count, key)
      if (count < 0)
         return (rl_backward_word(-count, key))
      end

      while (count>0)
         return 0 if (@rl_point == @rl_end)

         # If we are not in a word, move forward until we are in one.
         # Then, move forward until we hit a non-alphabetic character.
         c = _rl_char_value(@rl_line_buffer, @rl_point)

         if (!_rl_walphabetic(c))
            if !@rl_byte_oriented
               @rl_point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
            else
               @rl_point += 1
            end
            while (@rl_point < @rl_end)
               c = _rl_char_value(@rl_line_buffer, @rl_point)
               if (_rl_walphabetic(c))
                  break
               end
               if !@rl_byte_oriented
                  @rl_point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
               else
                  @rl_point += 1
               end
            end
         end

         return 0 if (@rl_point == @rl_end)

         if !@rl_byte_oriented
            @rl_point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
         else
            @rl_point += 1
         end
         while (@rl_point < @rl_end)
            c = _rl_char_value(@rl_line_buffer, @rl_point)
            if (!_rl_walphabetic(c))
               break
            end
            if !@rl_byte_oriented
               @rl_point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
            else
               @rl_point += 1
            end
         end
         count -= 1
      end
      0
   end

   # Move backward a word.  We do what Emacs does.  Handles multibyte chars.
   def rl_backward_word(count, key)
      if (count < 0)
         return (rl_forward_word(-count, key))
      end
      while (count>0)
         return 0 if (@rl_point == 0)

         # Like rl_forward_word (), except that we look at the characters
         # just before point.
         _p =  !@rl_byte_oriented ? _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO):(@rl_point-1)
         c = _rl_char_value(@rl_line_buffer, _p)
         if (!_rl_walphabetic(c))
            @rl_point = _p
            while (@rl_point > 0)
               _p = !@rl_byte_oriented ? _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO):(@rl_point-1)
               c = _rl_char_value(@rl_line_buffer, _p)
               if (_rl_walphabetic(c))
                  break
               end
               @rl_point = _p
            end
         end
         while (@rl_point>0)
            _p = !@rl_byte_oriented ? _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO):(@rl_point-1)
            c = _rl_char_value(@rl_line_buffer, _p)
            if (!_rl_walphabetic(c))
               break
            else
               @rl_point = _p
            end
         end
         count -= 1
      end
      0
   end

   # return the `current display line' of the cursor -- the number of lines to
   #   move up to get to the first screen line of the current readline line.
   def _rl_current_display_line()
      # Find out whether or not there might be invisible characters in the
      #   editing buffer.
      if (@rl_display_prompt == @rl_prompt)
         nleft = @_rl_last_c_pos - @_rl_screenwidth - @rl_visible_prompt_length
      else
         nleft = @_rl_last_c_pos - @_rl_screenwidth
      end

      if (nleft > 0)
         ret = 1 + nleft / @_rl_screenwidth
      else
         ret = 0
      end

      ret
   end

   # Actually update the display, period.
   def rl_forced_update_display()
      if (@visible_line)
         @visible_line.gsub!(/[^\x00]/,0.chr)
      end
      rl_on_new_line()
      @forced_display=true if !@forced_display
      send(@rl_redisplay_function)
      0
   end


   # Clear the current line.  Numeric argument to C-l does this.
   def rl_refresh_line(ignore1, ignore2)
      curr_line = _rl_current_display_line()

      _rl_move_vert(curr_line)
      _rl_move_cursor_relative(0, @rl_line_buffer)   # XXX is this right

      _rl_clear_to_eol(0)     # arg of 0 means to not use spaces

      rl_forced_update_display()
      @rl_display_fixed = true

      0
   end

   # C-l typed to a line without quoting clears the screen, and then reprints
   #   the prompt and the current input line.  Given a numeric arg, redraw only
   #   the current line.
   def rl_clear_screen(count, key)
      if (@rl_explicit_arg)
         rl_refresh_line(count, key)
         return 0
      end

      _rl_clear_screen()      # calls termcap function to clear screen
      rl_forced_update_display()
      @rl_display_fixed = true
      0
   end

   # Restore the _rl_saved_line_for_history if there is one.
   def rl_maybe_unsave_line()
      if (@_rl_saved_line_for_history)
         # Can't call with `1' because rl_undo_list might point to an undo
         # list from a history entry, as in rl_replace_from_history() below.
         rl_replace_line(@_rl_saved_line_for_history.line, false)
         @rl_undo_list = @_rl_saved_line_for_history.data
         @_rl_saved_line_for_history = nil
         @rl_point = @rl_end  # rl_replace_line sets rl_end
      else
         rl_ding()
      end
      0
   end

   # Save the current line in _rl_saved_line_for_history.
   def rl_maybe_save_line()
      if @_rl_saved_line_for_history.nil?
         @_rl_saved_line_for_history = Struct.new(:line,:timestamp,:data).new
         @_rl_saved_line_for_history.line = @rl_line_buffer.dup
         @_rl_saved_line_for_history.timestamp = nil
         @_rl_saved_line_for_history.data = @rl_undo_list
      end
      0
   end

   # Returns the magic number which says what history element we are
   #   looking at now.  In this implementation, it returns history_offset.
   def where_history()
      @history_offset
   end

   # Make the history entry at WHICH have LINE and DATA.  This returns
   #   the old entry so you can dispose of the data.  In the case of an
   #   invalid WHICH, a NULL pointer is returned.
   def replace_history_entry (which, line, data)
      if (which < 0 || which >= @history_length)
         return nil
      end
      temp = Struct.new(:line,:timestamp,:data).new
      old_value = @the_history[which]
      temp.line = line.delete(0.chr)
      temp.data = data
      temp.timestamp = old_value.timestamp.dup
      @the_history[which] = temp
      old_value
   end

   # Perhaps put back the current line if it has changed.
   def rl_maybe_replace_line()
      temp = current_history()
      # If the current line has changed, save the changes.
      if (temp && temp.data != @rl_undo_list)
         temp = replace_history_entry(where_history(), @rl_line_buffer, @rl_undo_list)
      end
      0
   end

   # Back up history_offset to the previous history entry, and return
   #   a pointer to that entry.  If there is no previous entry then return
   #   a NULL pointer.
   def previous_history()
      @history_offset!=0 ? @the_history[@history_offset-=1] : nil
   end

   # Move history_offset forward to the next history entry, and return
   #   a pointer to that entry.  If there is no next entry then return a
   #   NULL pointer.
   def next_history()
      (@history_offset == @history_length) ? nil : @the_history[@history_offset+=1]
   end

   # Get the previous item out of our interactive history, making it the current
   #   line.  If there is no previous history, just ding.
   def rl_get_previous_history(count, key)
      if (count < 0)
         return (rl_get_next_history(-count, key))
      end
      if (count == 0)
         return 0
      end
      # either not saved by rl_newline or at end of line, so set appropriately.
      if (@_rl_history_saved_point == -1 && (@rl_point!=0 || @rl_end!=0))
         @_rl_history_saved_point = (@rl_point == @rl_end) ? -1 : @rl_point
      end

      # If we don't have a line saved, then save this one.
      rl_maybe_save_line()

      # If the current line has changed, save the changes.
      rl_maybe_replace_line()

      temp = old_temp = nil
      while (count>0)
         temp = previous_history()
         if temp.nil?
            break
         end
         old_temp = temp
         count -= 1
      end

      # If there was a large argument, and we moved back to the start of the
      #   history, that is not an error.  So use the last value found.
      if (temp.nil? && old_temp)
         temp = old_temp
      end

      if temp.nil?
         rl_ding()
      else
         rl_replace_from_history(temp, 0)
         _rl_history_set_point()
      end

      0
   end

   def _rl_history_set_point ()
      @rl_point = (@_rl_history_preserve_point && @_rl_history_saved_point != -1) ?
      @_rl_history_saved_point : @rl_end
      if (@rl_point > @rl_end)
         @rl_point = @rl_end
      end
      if (@rl_editing_mode == @vi_mode && @_rl_keymap != @vi_insertion_keymap)
         @rl_point = 0
      end
      if (@rl_editing_mode == @emacs_mode)
         @rl_mark = (@rl_point == @rl_end ? 0 : @rl_end)
      end
   end

   # Move down to the next history line.
   def rl_get_next_history(count, key)
      if (count < 0)
         return (rl_get_previous_history(-count, key))
      end
      if (count == 0)
         return 0
      end
      rl_maybe_replace_line()

      # either not saved by rl_newline or at end of line, so set appropriately.
      if (@_rl_history_saved_point == -1 && (@rl_point!=0 || @rl_end!=0))
         @_rl_history_saved_point = (@rl_point == @rl_end) ? -1 : @rl_point
      end
      temp = nil
      while (count>0)
         temp = next_history()
         if temp.nil?
            break
         end
         count -= 1
      end

      if temp.nil?
         rl_maybe_unsave_line()
      else
         rl_replace_from_history(temp, 0)
         _rl_history_set_point()
      end
      0
   end

   def rl_arrow_keys(count, c)
      rl_setstate(RL_STATE_MOREINPUT)
      ch = rl_read_key()
      rl_unsetstate(RL_STATE_MOREINPUT)

      case (ch.upcase)
      when 'A'
         rl_get_previous_history(count, ch)
      when 'B'
         rl_get_next_history(count, ch)
      when 'C'
         rl_forward_byte(count, ch)
      when 'D'
         rl_backward_byte(count, ch)
      else
         rl_ding()
      end
      0
   end

   def _rl_any_typein()
      return (@push_index != @pop_index)
   end

   def _rl_insert_typein(c)
      string = c

      while ((key = rl_get_char()) &&
         @_rl_keymap[key] == :rl_insert)
         string << key
      end
      if (key)
         _rl_unget_char(key)
      end
      rl_insert_text(string)
   end

   # Insert the character C at the current location, moving point forward.
   #   If C introduces a multibyte sequence, we read the whole sequence and
   #   then insert the multibyte char into the line buffer.
   def _rl_insert_char(count, c)
      return 0 if (count <= 0)

      incoming = ''

      if @rl_byte_oriented
         incoming << c
         incoming_length = 1
      else
         @pending_bytes << c
         if _rl_get_char_len(@pending_bytes) == -2
            return 1
         else
            incoming = @pending_bytes
            @pending_bytes = ''
            incoming_length = incoming.length
         end
      end

      if(count>1)
         string = incoming * count
         rl_insert_text(string)
         string = nil
         return 0
      end

      if @rl_byte_oriented
         # We are inserting a single character.
         #If there is pending input, then make a string of all of the
         #pending characters that are bound to rl_insert, and insert
         #them all.
         if (_rl_any_typein())
            _rl_insert_typein(c)
         else
            rl_insert_text(c)
         end
      else
         rl_insert_text(incoming)
      end

      return 0
   end

   # Overwrite the character at point (or next COUNT characters) with C.
   #   If C introduces a multibyte character sequence, read the entire sequence
   #   before starting the overwrite loop.
   def _rl_overwrite_char(count, c)

      # Read an entire multibyte character sequence to insert COUNT times.
      if (count > 0 && !@rl_byte_oriented)
         mbkey = ''
         k = _rl_read_mbstring(c, mbkey, MB_LEN_MAX)
      end
      rl_begin_undo_group()

      count.times do
         if !@rl_byte_oriented
            rl_insert_text(mbkey)
         else
            _rl_insert_char(1, c)
         end
         if (@rl_point < @rl_end)
            rl_delete(1, c)
         end
      end

      rl_end_undo_group()

      return 0
   end

   def rl_insert(count, c)
      ((@rl_insert_mode == RL_IM_INSERT) ? _rl_insert_char(count, c) :
      _rl_overwrite_char(count, c))
   end


   # Insert the next typed character verbatim.
   def _rl_insert_next(count)
      rl_setstate(RL_STATE_MOREINPUT)
      c = rl_read_key()
      rl_unsetstate(RL_STATE_MOREINPUT)

      _rl_insert_char(count, c)
   end

   def rl_quoted_insert(count, key)
      _rl_insert_next(count)
   end

   # Insert a tab character.
   def rl_tab_insert(count, key)
      _rl_insert_char(count, "\t")
   end

   def _rl_vi_save_insert(up)
      if (up.nil? || up.what != UNDO_INSERT)
         if (@vi_insert_buffer_size >= 1)
            @vi_insert_buffer[0] = 0.chr
         end
         return
      end
      start = up.start
      _end = up.end
      len = _end - start + 1
      @vi_insert_buffer = @rl_line_buffer[start,len-1]
   end

   def _rl_vi_done_inserting()
      if (@_rl_vi_doing_insert)

         # The `C', `s', and `S' commands set this.
         rl_end_undo_group()
         # Now, the text between rl_undo_list->next->start and
         # rl_undo_list->next->end is what was inserted while in insert
         # mode.  It gets copied to VI_INSERT_BUFFER because it depends
         # on absolute indices into the line which may change (though they
         # probably will not).
         @_rl_vi_doing_insert = 0
         _rl_vi_save_insert(@rl_undo_list.next)
         @vi_continued_command = 1
      else
         if ((@_rl_vi_last_key_before_insert == 'i' || @_rl_vi_last_key_before_insert == 'a') && @rl_undo_list)
            _rl_vi_save_insert(@rl_undo_list)

            # XXX - Other keys probably need to be checked.
         elsif (@_rl_vi_last_key_before_insert == 'C')
            rl_end_undo_group()
         end
         while (@_rl_undo_group_level > 0)
            rl_end_undo_group()
         end
         @vi_continued_command = 0
      end
   end

   # Is the command C a VI mode text modification command?
   def _rl_vi_textmod_command(c)
      return @vi_textmod[c]
   end

   def _rl_vi_reset_last()
      @_rl_vi_last_command = 'i'
      @_rl_vi_last_repeat = 1
      @_rl_vi_last_arg_sign = 1
      @_rl_vi_last_motion = 0
   end

   def _rl_update_final()
      full_lines = false
      # If the cursor is the only thing on an otherwise-blank last line,
      #   compensate so we don't print an extra CRLF.
      if (@_rl_vis_botlin && @_rl_last_c_pos == 0 &&
         @visible_line[@vis_lbreaks[@_rl_vis_botlin],1] == 0.chr )
         @_rl_vis_botlin-=1
         full_lines = true
      end
      _rl_move_vert(@_rl_vis_botlin)
      # If we've wrapped lines, remove the final xterm line-wrap flag.
      if (full_lines && @_rl_term_autowrap && (vis_llen(@_rl_vis_botlin) == @_rl_screenwidth))
         last_line = @visible_line[@vis_lbreaks[@_rl_vis_botlin]..-1]
         @cpos_buffer_position = -1 # don't know where we are in buffer
         _rl_move_cursor_relative(@_rl_screenwidth - 1, last_line)   # XXX
         _rl_clear_to_eol(0)
         @rl_outstream.write(last_line[@_rl_screenwidth - 1,1])
      end
      @_rl_vis_botlin = 0
      rl_crlf()
      @rl_outstream.flush
      @rl_display_fixed = true if !@rl_display_fixed
   end


   # What to do when a NEWLINE is pressed.  We accept the whole line.
   #   KEY is the key that invoked this command.  I guess it could have
   #   meaning in the future.
   def rl_newline(count, key)
      @rl_done = true

      if (@_rl_history_preserve_point)
         @_rl_history_saved_point = (@rl_point == @rl_end) ? 1 : @rl_point
      end
      rl_setstate(RL_STATE_DONE)

      if (@rl_editing_mode == @vi_mode)
         _rl_vi_done_inserting()
         if (_rl_vi_textmod_command(@_rl_vi_last_command).nil?)   # XXX
            _rl_vi_reset_last()
         end
      end
      # If we've been asked to erase empty lines, suppress the final update,
      #   since _rl_update_final calls rl_crlf().
      if (@rl_erase_empty_line && @rl_point == 0 && @rl_end == 0)
         return 0
      end
      if @readline_echoing_p
         _rl_update_final()
      end
      0
   end

   # What to do for some uppercase characters, like meta characters,
   #   and some characters appearing in emacs_ctlx_keymap.  This function
   #   is just a stub, you bind keys to it and the code in _rl_dispatch ()
   #   is special cased.
   def rl_do_lowercase_version(ignore1, ignore2)
      0
   end

   def rl_character_len(c, pos)
      if (meta_char(c))
         return ((!@_rl_output_meta_chars) ? 4 : 1)
      end
      if (c == "\t")
         return (((pos | 7) + 1) - pos)
      end
      if (ctrl_char(c) || c == RUBOUT)
         return (2)
      end

      return ((isprint(c)) ? 1 : 2)
   end

   # This is different from what vi does, so the code's not shared.  Emacs
   #   rubout in overwrite mode has one oddity:  it replaces a control
   #   character that's displayed as two characters (^X) with two spaces.
   def _rl_overwrite_rubout(count, key)
      if (@rl_point == 0)
         rl_ding()
         return 1
      end

      opoint = @rl_point

      # L == number of spaces to insert
      l = 0
      count.times do
         rl_backward_char(1, key)
         l += rl_character_len(@rl_line_buffer[@rl_point,1], @rl_point) # not exactly right
      end

      rl_begin_undo_group()

      if (count > 1 || @rl_explicit_arg)
         rl_kill_text(opoint, @rl_point)
      else
         rl_delete_text(opoint, @rl_point)
      end
      # Emacs puts point at the beginning of the sequence of spaces.
      if (@rl_point < @rl_end)
         opoint = @rl_point
         _rl_insert_char(l, ' ')
         @rl_point = opoint
      end

      rl_end_undo_group()

      0
   end

   # Rubout the character behind point.
   def rl_rubout(count, key)
      if (count < 0)
         return (rl_delete(-count, key))
      end
      if (@rl_point==0)
         rl_ding()
         return -1
      end

      if (@rl_insert_mode == RL_IM_OVERWRITE)
         return (_rl_overwrite_rubout(count, key))
      end
      _rl_rubout_char(count, key)
   end


   # Quick redisplay hack when erasing characters at the end of the line.
   def _rl_erase_at_end_of_line(l)
      _rl_backspace(l)
      @rl_outstream.write(' '*l)
      _rl_backspace(l)
      @_rl_last_c_pos -= l
      @visible_line[@_rl_last_c_pos,l] = 0.chr * l
      @rl_display_fixed = true if !@rl_display_fixed
   end

   def _rl_rubout_char(count, key)
      # Duplicated code because this is called from other parts of the library.
      if (count < 0)
         return (rl_delete(-count, key))
      end
      if (@rl_point == 0)
         rl_ding()
         return -1
      end

      orig_point = @rl_point
      if (count > 1 || @rl_explicit_arg)
         rl_backward_char(count, key)
         rl_kill_text(orig_point, @rl_point)
      elsif (@rl_byte_oriented)
         c = @rl_line_buffer[@rl_point-=1,1]
         rl_delete_text(@rl_point, orig_point)
         # The erase-at-end-of-line hack is of questionable merit now.
         if (@rl_point == @rl_end && isprint(c) && @_rl_last_c_pos!=0)
            l = rl_character_len(c, @rl_point)
            _rl_erase_at_end_of_line(l)
         end
      else
         @rl_point = _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO)
         rl_delete_text(@rl_point, orig_point)
      end

      0
   end

   # Delete the character under the cursor.  Given a numeric argument,
   #   kill that many characters instead.
   def rl_delete(count, key)
      if (count < 0)
         return (_rl_rubout_char(-count, key))
      end
      if (@rl_point == @rl_end)
         rl_ding()
         return -1
      end

      if (count > 1 || @rl_explicit_arg)
         xpoint = @rl_point
         rl_forward_byte(count, key)

         rl_kill_text(xpoint, @rl_point)
         @rl_point = xpoint
      else
         if !@rl_byte_oriented
            xpoint =_rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
         else
            xpoint = @rl_point + 1
         end

         rl_delete_text(@rl_point, xpoint)
      end
      0
   end

   # Add TEXT to the kill ring, allocating a new kill ring slot as necessary.
   #   This uses TEXT directly, so the caller must not free it.  If APPEND is
   #   non-zero, and the last command was a kill, the text is appended to the
   #   current kill ring slot, otherwise prepended.
   def _rl_copy_to_kill_ring(text, append)
      # First, find the slot to work with.
      if (!@_rl_last_command_was_kill)
         # Get a new slot.
         if @rl_kill_ring.nil?
            # If we don't have any defined, then make one.
            @rl_kill_ring_length = 1
            @rl_kill_ring = Array.new(@rl_kill_ring_length+1)
            @rl_kill_ring[slot = 0] = nil
         else
            # We have to add a new slot on the end, unless we have
            #   exceeded the max limit for remembering kills.
            slot = @rl_kill_ring_length
            if (slot == @rl_max_kills)
               @rl_kill_ring[0,slot] = @rl_kill_ring[1,slot]
            else
               slot = @rl_kill_ring_length += 1
            end
            @rl_kill_ring[slot-=1] = nil
         end
      else
         slot = @rl_kill_ring_length - 1
      end

      # If the last command was a kill, prepend or append.
      if (@_rl_last_command_was_kill && @rl_editing_mode != @vi_mode)
         old = @rl_kill_ring[slot]

         if (append)
            new = old + text
         else
            new = text + old
         end
         old = nil
         text = nil
         @rl_kill_ring[slot] = new
      else
         @rl_kill_ring[slot] = text
      end

      @rl_kill_index = slot
      0
   end

   # The way to kill something.  This appends or prepends to the last
   #   kill, if the last command was a kill command.  if FROM is less
   #   than TO, then the text is appended, otherwise prepended.  If the
   #   last command was not a kill command, then a new slot is made for
   #   this kill.
   def rl_kill_text(from, to)
      # Is there anything to kill?
      if (from == to)
         @_rl_last_command_was_kill = true if !@_rl_last_command_was_kill
         return 0
      end
      text = rl_copy_text(from, to)

      # Delete the copied text from the line.
      rl_delete_text(from, to)
      _rl_copy_to_kill_ring(text, from < to)
      @_rl_last_command_was_kill = true if !@_rl_last_command_was_kill
      0
   end

   # This does what C-w does in Unix.  We can't prevent people from
   #   using behaviour that they expect.
   def rl_unix_word_rubout(count, key)
      if (@rl_point == 0)
         rl_ding()
      else
         orig_point = @rl_point
         if (count <= 0)
            count = 1
         end

         while (count>0)
            while (@rl_point>0 && whitespace(@rl_line_buffer[@rl_point - 1,1]))
               @rl_point-=1
            end

            while (@rl_point>0 && !whitespace(@rl_line_buffer[@rl_point - 1,1]))
               @rl_point-=1
            end
            count -= 1
         end

         rl_kill_text(orig_point, @rl_point)
         if (@rl_editing_mode == @emacs_mode)
            @rl_mark = @rl_point
         end
      end
      0
   end

   # This deletes one filename component in a Unix pathname.  That is, it
   #   deletes backward to directory separator (`/') or whitespace.
   def rl_unix_filename_rubout(count, key)
      if (@rl_point == 0)
         rl_ding()
      else
         orig_point = @rl_point
         if (count <= 0)
            count = 1
         end

         while (count>0)

            c = @rl_line_buffer[@rl_point - 1,1]
            while (@rl_point>0 && (whitespace(c) || c == '/'))
               @rl_point-=1
               c = @rl_line_buffer[@rl_point - 1,1]
            end

            while (@rl_point>0 && !whitespace(c) && c != '/')
               @rl_point-=1
               c = @rl_line_buffer[@rl_point - 1,1]
            end
            count -= 1
         end

         rl_kill_text(orig_point, @rl_point)
         if (@rl_editing_mode == @emacs_mode)
            @rl_mark = @rl_point
         end
      end
      0
   end

   # Delete the character under the cursor, unless the insertion
   #   point is at the end of the line, in which case the character
   #   behind the cursor is deleted.  COUNT is obeyed and may be used
   #   to delete forward or backward that many characters.
   def rl_rubout_or_delete(count, key)
      if (@rl_end != 0 && @rl_point == @rl_end)
         return (_rl_rubout_char(count, key))
      else
         return (rl_delete(count, key))
      end
   end

   # Delete all spaces and tabs around point.
   def rl_delete_horizontal_space(count, ignore)
      start = @rl_point

      while (@rl_point!=0 && whitespace(@rl_line_buffer[@rl_point - 1]))
         @rl_point-=1
      end
      start = @rl_point
      while (@rl_point < @rl_end && whitespace(@rl_line_buffer[@rl_point]))
         @rl_point+=1
      end
      if (start != @rl_point)
         rl_delete_text(start, @rl_point)
         @rl_point = start
      end
      if (@rl_point < 0)
         @rl_point = 0
      end
      0
   end

   # List the possible completions.  See description of rl_complete ().
   def rl_possible_completions(ignore, invoking_key)
      rl_complete_internal('?')
   end

   # Like the tcsh editing function delete-char-or-list.  The eof character
   #   is caught before this is invoked, so this really does the same thing as
   #   delete-char-or-list-or-eof, as long as it's bound to the eof character.
   def rl_delete_or_show_completions(count, key)
      if (@rl_end != 0 && @rl_point == @rl_end)
         return (rl_possible_completions(count, key))
      else
         return (rl_delete(count, key))
      end
   end

   # Turn the current line into a comment in shell history.
   #   A K*rn shell style function.
   def rl_insert_comment(count, key)
      rl_beg_of_line(1, key)
      @rl_comment_text = @_rl_comment_begin ? @_rl_comment_begin : '#'

      if (!@rl_explicit_arg)
         rl_insert_text(@rl_comment_text)
      else
         @rl_comment_len = @rl_comment_text.length
         if @rl_comment_text[0,@rl_comment_len] == @rl_line_buffer[0,@rl_comment_len]
            rl_delete_text(@rl_point, @rl_point + @rl_comment_len)
         else
            rl_insert_text(@rl_comment_text)
         end
      end

      send(@rl_redisplay_function)
      rl_newline(1, "\n")
      0
   end

   def alloc_history_entry(string, ts)
      temp = Struct.new(:line,:data,:timestamp).new
      temp.line = string ? string.delete(0.chr) : string
      temp.data = nil
      temp.timestamp = ts

      return temp
   end

   def hist_inittime()
      t = Time.now.to_i
      ts = "X%u" % t
      ret = ts.dup
      ret[0,1] = @history_comment_char

      ret
   end

   # Place STRING at the end of the history list.  The data field
   #   is  set to NULL.
   def add_history(string)
      if (@history_stifled && (@history_length == @history_max_entries))
         # If the history is stifled, and history_length is zero,
         # and it equals history_max_entries, we don't save items.
         return if (@history_length == 0)
         @the_history.shift
      else
         if @the_history.nil?
            @the_history = []
            @history_length = 1
         else
            @history_length+=1
         end
      end

      temp = alloc_history_entry(string, hist_inittime())
      @the_history[@history_length] = nil
      @the_history[@history_length - 1] = temp
   end

   def using_history()
      @history_offset = @history_length
   end

   # Set default values for readline word completion.  These are the variables
   #   that application completion functions can change or inspect.
   def set_completion_defaults(what_to_do)
      # Only the completion entry function can change these.
      @rl_filename_completion_desired = false
      @rl_filename_quoting_desired = true
      @rl_completion_type = what_to_do
      @rl_completion_suppress_append = @rl_completion_suppress_quote = false

      # The completion entry function may optionally change this.
      @rl_completion_mark_symlink_dirs = @_rl_complete_mark_symlink_dirs
   end

   def _rl_find_completion_word()
      _end = @rl_point
      found_quote = 0
      delimiter = 0.chr
      quote_char = 0.chr

      brkchars = nil
      if @rl_completion_word_break_hook
         brkchars = send(@rl_completion_word_break_hook)
      end
      if brkchars.nil?
         brkchars = @rl_completer_word_break_characters
      end
      if (@rl_completer_quote_characters)
         # We have a list of characters which can be used in pairs to
         # quote substrings for the completer.  Try to find the start
         # of an unclosed quoted substring.
         # FOUND_QUOTE is set so we know what kind of quotes we found.
         scan = 0
         pass_next = false
         while scan < _end
            if (pass_next)
               pass_next = false
               next
            end

            # Shell-like semantics for single quotes -- don't allow backslash
            #   to quote anything in single quotes, especially not the closing
            #   quote.  If you don't like this, take out the check on the value
            #   of quote_char.
            if (quote_char != "'" && @rl_line_buffer[scan,1] == "\\")
               pass_next = true
               found_quote |= RL_QF_BACKSLASH
               next
            end

            if (quote_char != 0.chr)
               # Ignore everything until the matching close quote char.
               if (@rl_line_buffer[scan,1] == quote_char)
                  # Found matching close.  Abandon this substring.
                  quote_char = 0.chr
                  @rl_point = _end
               end

            elsif (@rl_completer_quote_characters.include?(@rl_line_buffer[scan,1]))

               # Found start of a quoted substring.
               quote_char = @rl_line_buffer[scan,1]
               @rl_point = scan + 1
               # Shell-like quoting conventions.
               if (quote_char == "'")
                  found_quote |= RL_QF_SINGLE_QUOTE
               elsif (quote_char == '"')
                  found_quote |= RL_QF_DOUBLE_QUOTE
               else
                  found_quote |= RL_QF_OTHER_QUOTE
               end
            end
            if !@rl_byte_oriented
               scan = _rl_find_next_mbchar(@rl_line_buffer, scan, 1, MB_FIND_ANY)
            else
               scan += 1
            end
         end
      end

      if (@rl_point == _end && quote_char == 0.chr)

         # We didn't find an unclosed quoted substring upon which to do
         #   completion, so use the word break characters to find the
         #   substring on which to complete.


         while (@rl_point =  !@rl_byte_oriented ?
            _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_ANY):(@rl_point-1))>0

            scan = @rl_line_buffer[@rl_point,1]
            if !brkchars.include?(scan)
               next
            end
            # Call the application-specific function to tell us whether
            #   this word break character is quoted and should be skipped.
            if (@rl_char_is_quoted_p && found_quote!=0 &&
               send(@rl_char_is_quoted_p,@rl_line_buffer, @rl_point))
               next
            end

            # Convoluted code, but it avoids an n^2 algorithm with calls
            #   to char_is_quoted.
            break
         end
      end

      # If we are at an unquoted word break, then advance past it.
      scan = @rl_line_buffer[@rl_point,1]

      # If there is an application-specific function to say whether or not
      #   a character is quoted and we found a quote character, let that
      #   function decide whether or not a character is a word break, even
      #   if it is found in rl_completer_word_break_characters.  Don't bother
      #   if we're at the end of the line, though.
      if (scan != 0.chr)
         if (@rl_char_is_quoted_p)
            isbrk = (found_quote == 0 ||
            !send(@rl_char_is_quoted_p,@rl_line_buffer, @rl_point)) &&
            brkchars.include?(scan)
         else
            isbrk = brkchars.include?(scan)
         end

         if (isbrk)
            # If the character that caused the word break was a quoting
            #   character, then remember it as the delimiter.
            if (@rl_basic_quote_characters &&
               @rl_basic_quote_characters.include?(scan) &&
               (_end - @rl_point) > 1)
               delimiter = scan
            end

            # If the character isn't needed to determine something special
            #   about what kind of completion to perform, then advance past it.
            if (@rl_special_prefixes.nil? || !@rl_special_prefixes.include?(scan) )
               @rl_point+=1
            end
         end
      end

      return [quote_char,found_quote!=0,delimiter]
   end

   def gen_completion_matches(text, start, _end, our_func, found_quote, quote_char)
      @rl_completion_found_quote = found_quote
      @rl_completion_quote_character = quote_char

      # If the user wants to TRY to complete, but then wants to give
      #   up and use the default completion function, they set the
      #   variable rl_attempted_completion_function.
      if (@rl_attempted_completion_function)
         matches = Readline.send(@rl_attempted_completion_function,text, start, _end)
         if (matches || @rl_attempted_completion_over)
            @rl_attempted_completion_over = false
            return (matches)
         end
      end
      # XXX -- filename dequoting moved into rl_filename_completion_function

      matches = rl_completion_matches(text, our_func)
      matches
   end

   # Filter out duplicates in MATCHES.  This frees up the strings in
   #   MATCHES.
   def remove_duplicate_matches(matches)
      # Sort the items.
      # Sort the array without matches[0], since we need it to
      #   stay in place no matter what.
      if matches.length>0
         matches[1..-2] = matches[1..-2].sort.uniq
      end
      matches
   end

   def postprocess_matches(matchesp, matching_filenames)
      matches = matchesp

      return 0 if matches.nil?

      # It seems to me that in all the cases we handle we would like
      #   to ignore duplicate possiblilities.  Scan for the text to
      #   insert being identical to the other completions.
      if (@rl_ignore_completion_duplicates)
         remove_duplicate_matches(matches)
      end

      # If we are matching filenames, then here is our chance to
      #   do clever processing by re-examining the list.  Call the
      #   ignore function with the array as a parameter.  It can
      #   munge the array, deleting matches as it desires.
      if (@rl_ignore_some_completions_function && matching_filenames)
         nmatch = matches.length
         send(@rl_ignore_some_completions_function,matches)
         if (matches.nil? || matches[0].nil?)
            matches = nil
            return 0
         else
            # If we removed some matches, recompute the common prefix.
            i = matches.length
            if (i > 1 && i < nmatch)
               t = matches[0]
               compute_lcd_of_matches(matches, i - 1, t)
            end
         end
      end

      matchesp = matches
      1
   end

   def insert_all_matches(matches, point, qc)
      rl_begin_undo_group()
      # remove any opening quote character; make_quoted_replacement will add
      #   it back.
      if (qc && qc.length>0 && point>0 && @rl_line_buffer[point - 1,1] == qc)
         point-=1
      end
      rl_delete_text(point, @rl_point)
      @rl_point = point
      if (matches[1])
         i = 1
         while(matches[i])
            rp = make_quoted_replacement(matches[i], SINGLE_MATCH, qc)
            rl_insert_text(rp)
            rl_insert_text(" ")
            if (rp != matches[i])
               rp = nil
            end
            i += 1
         end
      else
         rp = make_quoted_replacement(matches[0], SINGLE_MATCH, qc)
         rl_insert_text(rp)
         rl_insert_text(" ")
         if (rp != matches[0])
            rp = nil
         end
      end
      rl_end_undo_group()
   end

   def make_quoted_replacement(match, mtype, qc)
      # If we are doing completion on quoted substrings, and any matches
      #   contain any of the completer_word_break_characters, then auto-
      #   matically prepend the substring with a quote character (just pick
      #   the first one from the list of such) if it does not already begin
      #   with a quote string.  FIXME: Need to remove any such automatically
      #   inserted quote character when it no longer is necessary, such as
      #   if we change the string we are completing on and the new set of
      #   matches don't require a quoted substring.
      replacement = match

      should_quote = match && @rl_completer_quote_characters &&
      @rl_filename_completion_desired &&
      @rl_filename_quoting_desired

      if (should_quote)
         should_quote = should_quote && (qc.nil? || qc == 0.chr ||
         (@rl_completer_quote_characters && @rl_completer_quote_characters.include?(qc)))
      end

      if (should_quote)

         # If there is a single match, see if we need to quote it.
         #   This also checks whether the common prefix of several
         # matches needs to be quoted.
         should_quote = @rl_filename_quote_characters ?
         !!match[@rl_filename_quote_characters] :
         false

         do_replace = should_quote ? mtype : NO_MATCH
         # Quote the replacement, since we found an embedded
         # word break character in a potential match.
         if (do_replace != NO_MATCH && @rl_filename_quoting_function)
            replacement = send(@rl_filename_quoting_function,match, do_replace, qc)
         end
      end
      replacement
   end


   def insert_match(match, start, mtype, qc)
      oqc = qc
      replacement = make_quoted_replacement(match, mtype, qc)

      # Now insert the match.
      if (replacement)
         # Don't double an opening quote character.
         if (qc && qc.length>0 && start!=0 && @rl_line_buffer[start - 1,1] == qc &&
            replacement[0,1] == qc)
            start-=1
            # If make_quoted_replacement changed the quoting character, remove
            # the opening quote and insert the (fully-quoted) replacement.
         elsif (qc && (qc != oqc) && start!=0 && @rl_line_buffer[start - 1,1] == oqc &&
            replacement[0,1] != oqc)
            start-=1
         end
         _rl_replace_text(replacement, start, @rl_point - 1)
         if (replacement != match)
            replacement = nil
         end
      end
   end

   # Return the portion of PATHNAME that should be output when listing
   #   possible completions.  If we are hacking filename completion, we
   #   are only interested in the basename, the portion following the
   #   final slash.  Otherwise, we return what we were passed.  Since
   #   printing empty strings is not very informative, if we're doing
   #   filename completion, and the basename is the empty string, we look
   #   for the previous slash and return the portion following that.  If
   #   there's no previous slash, we just return what we were passed.
   def printable_part(pathname)
      if (!@rl_filename_completion_desired)  # don't need to do anything
         return (pathname)
      end

      temp = pathname.rindex('/')
      return pathname if temp.nil?
      File.basename(pathname)
   end

   def fnprint(to_print)
      printed_len = 0

      case @encoding
      when 'E'
         arr = to_print.scan(/./me)
      when 'S'
         arr = to_print.scan(/./ms)
      when 'U'
         arr = to_print.scan(/./mu)
      when 'X'
         arr = to_print.dup.force_encoding(@encoding_name).chars
      else
         arr = to_print.scan(/./m)
      end

      arr.each do |s|
         if(ctrl_char(s))
            @rl_outstream.write('^'+(s[0].ord|0x40).chr.upcase)
            printed_len += 2
         elsif s == RUBOUT
            @rl_outstream.write('^?')
            printed_len += 2
         else
            @rl_outstream.write(s)
            if @encoding=='U'
               printed_len += s.unpack('U').first >= 0x1000 ? 2 : 1
            elsif @encoding=='X'
               printed_len += s.ord >= 0x1000 ? 2 : 1
            else
               printed_len += s.length
            end
         end

      end

      printed_len
   end

   def _rl_internal_pager(lines)
      @rl_outstream.puts "--More--"
      @rl_outstream.flush
      i = get_y_or_n(1)
      _rl_erase_entire_line()
      if (i == 0)
         return -1
      elsif (i == 2)
         return (lines - 1)
      else
         return 0
      end
   end

   def path_isdir(filename)
      return File.directory?(filename)
   end

   # Return the character which best describes FILENAME.
   #     `@' for symbolic links
   #     `/' for directories
   #     `*' for executables
   #     `=' for sockets
   #     `|' for FIFOs
   #     `%' for character special devices
   #     `#' for block special devices
   def stat_char(filename)
      return nil if !File.exists?(filename)

      return '/' if File.directory?(filename)
      return '%' if File.chardev?(filename)
      return '#' if File.blockdev?(filename)
      return '@' if File.symlink?(filename)
      return '=' if File.socket?(filename)
      return '|' if File.pipe?(filename)
      return '*' if File.executable?(filename)
      nil
   end


   # Output TO_PRINT to rl_outstream.  If VISIBLE_STATS is defined and we
   #   are using it, check for and output a single character for `special'
   #   filenames.  Return the number of characters we output.
   def print_filename(to_print, full_pathname)
      extension_char = 0.chr
      printed_len = fnprint(to_print)

      if (@rl_filename_completion_desired && (@rl_visible_stats || @_rl_complete_mark_directories))

         # If to_print != full_pathname, to_print is the basename of the
         # path passed.  In this case, we try to expand the directory
         # name before checking for the stat character.
         if (to_print != full_pathname)

            if full_pathname.nil? || full_pathname.length==0
               dn = '/'
            else
               dn = File.dirname(full_pathname)
            end
            s = File.expand_path(dn)
            if (@rl_directory_completion_hook)
               send(@rl_directory_completion_hook,s)
            end

            slen = s.length
            tlen = to_print.length
            new_full_pathname = s.dup
            if (s[-1,1] == '/' )
               slen-=1
            else
               new_full_pathname[slen,1] = '/'
            end
            new_full_pathname[slen .. -1] = '/' + to_print

            if (@rl_visible_stats)
               extension_char = stat_char(new_full_pathname)
            else
               if (path_isdir(new_full_pathname))
                  extension_char = '/'
               end
            end

            new_full_pathname = nil

         else

            s = File.expand_path(full_pathname)
            if (@rl_visible_stats)
               extension_char = stat_char(s)
            else
               if (path_isdir(s))
                  extension_char = '/'
               end
            end
         end
         s = nil
         if (extension_char)
            @rl_outstream.write(extension_char)
            printed_len+=1
         end
      end

      printed_len
   end

   # The user must press "y" or "n". Non-zero return means "y" pressed.
   def get_y_or_n(for_pager)
      while(true)

         rl_setstate(RL_STATE_MOREINPUT)
         c = rl_read_key()
         rl_unsetstate(RL_STATE_MOREINPUT)

         if (c == 'y' || c == 'Y' || c == ' ')
            return (1)
         end
         if (c == 'n' || c == 'N' || c == RUBOUT)
            return (0)
         end
         if (c == ABORT_CHAR)
            _rl_abort_internal()
         end
         if (for_pager && (c == NEWLINE || c == RETURN))
            return (2)
         end
         if (for_pager && (c == 'q' || c == 'Q'))
            return (0)
         end
         rl_ding()
      end
   end

   # Compute width of STRING when displayed on screen by print_filename
   def fnwidth(string)
      left = string.length + 1
      width = pos = 0
      while (string[pos] && string[pos,1] != 0.chr)
         if (ctrl_char(string[0,1]) || string[0,1] == RUBOUT)
            width += 2
            pos+=1
         else
            case @encoding
            when 'E'
               wc = string[pos,left-pos].scan(/./me)[0]
               bytes = wc.length
               tempwidth = wc.length
            when 'S'
               wc = string[pos,left-pos].scan(/./ms)[0]
               bytes = wc.length
               tempwidth = wc.length
            when 'U'
               wc = string[pos,left-pos].scan(/./mu)[0]
               bytes = wc.length
               tempwidth = wc.unpack('U').first >= 0x1000 ? 2 : 1
            when 'X'
               wc = string[pos,left-pos].force_encoding(@encoding_name)[0]
               bytes = wc.bytesize
               tempwidth = wc.ord >= 0x1000 ? 2 : 1
            else
               wc = string[pos,left-pos].scan(/./m)[0]
               bytes = wc.length
               tempwidth = wc.length
            end
            clen = bytes
            pos += clen
            w = tempwidth
            width += (w >= 0) ? w : 1
         end
      end
      width
   end

   # Display MATCHES, a list of matching filenames in argv format.  This
   # handles the simple case -- a single match -- first.  If there is more
   # than one match, we compute the number of strings in the list and the
   # length of the longest string, which will be needed by the display
   # function.  If the application wants to handle displaying the list of
   # matches itself, it sets RL_COMPLETION_DISPLAY_MATCHES_HOOK to the
   # address of a function, and we just call it.  If we're handling the
   # display ourselves, we just call rl_display_match_list.  We also check
   # that the list of matches doesn't exceed the user-settable threshold,
   # and ask the user if he wants to see the list if there are more matches
   # than RL_COMPLETION_QUERY_ITEMS.
   def display_matches(matches)
      # Move to the last visible line of a possibly-multiple-line command.
      _rl_move_vert(@_rl_vis_botlin)

      # Handle simple case first.  What if there is only one answer?
      if matches[1].nil?
         temp = printable_part(matches[0])
         rl_crlf()
         print_filename(temp, matches[0])
         rl_crlf()
         rl_forced_update_display()
         @rl_display_fixed = true
         return
      end

      # There is more than one answer.  Find out how many there are,
      #   and find the maximum printed length of a single entry.
      max = 0
      i = 1
      while(matches[i])
         temp = printable_part(matches[i])
         len = fnwidth(temp)

         if (len > max)
            max = len
         end
         i += 1
      end
      len = i - 1

      # If the caller has defined a display hook, then call that now.
      if (@rl_completion_display_matches_hook)
         send(@rl_completion_display_matches_hook,matches, len, max)
         return
      end

      # If there are many items, then ask the user if she really wants to
      #   see them all.
      if (@rl_completion_query_items > 0 && len >= @rl_completion_query_items)

         rl_crlf()
         @rl_outstream.write("Display all #{len} possibilities? (y or n)")
         @rl_outstream.flush
         if (get_y_or_n(false)==0)
            rl_crlf()

            rl_forced_update_display()
            @rl_display_fixed = true

            return
         end
      end

      rl_display_match_list(matches, len, max)

      rl_forced_update_display()
      @rl_display_fixed = true
   end

   # Complete the word at or before point.
   #   WHAT_TO_DO says what to do with the completion.
   #   `?' means list the possible completions.
   #   TAB means do standard completion.
   #   `*' means insert all of the possible completions.
   #   `!' means to do standard completion, and list all possible completions if
   #   there is more than one.
   #   `@' means to do standard completion, and list all possible completions if
   #   there is more than one and partial completion is not possible.
   def rl_complete_internal(what_to_do)
      rl_setstate(RL_STATE_COMPLETING)
      set_completion_defaults(what_to_do)

      saved_line_buffer = @rl_line_buffer ? @rl_line_buffer.delete(0.chr) : nil
      our_func = @rl_completion_entry_function ?
      @rl_completion_entry_function : :rl_filename_completion_function
      # We now look backwards for the start of a filename/variable word.
      _end = @rl_point
      found_quote = false
      delimiter = 0.chr
      quote_char = 0.chr

      if (@rl_point!=0)
         # This (possibly) changes rl_point.  If it returns a non-zero char,
         #   we know we have an open quote.
         quote_char,found_quote,delimiter = _rl_find_completion_word()
      end

      start = @rl_point
      @rl_point = _end

      text = rl_copy_text(start, _end)
      matches = gen_completion_matches(text, start, _end, our_func, found_quote, quote_char)
      # nontrivial_lcd is set if the common prefix adds something to the word
      #   being completed.
      nontrivial_lcd = !!(matches && text != matches[0])
      text = nil
      if matches.nil?
         rl_ding()
         saved_line_buffer = nil
         @completion_changed_buffer = false
         rl_unsetstate(RL_STATE_COMPLETING)
         return 0
      end

      # If we are matching filenames, the attempted completion function will
      #   have set rl_filename_completion_desired to a non-zero value.  The basic
      #   rl_filename_completion_function does this.
      i = @rl_filename_completion_desired
      if (postprocess_matches(matches, i) == 0)
         rl_ding()
         saved_line_buffer = nil
         @completion_changed_buffer = false
         rl_unsetstate(RL_STATE_COMPLETING)
         return 0
      end

      case (what_to_do)

      when TAB,'!','@'
         # Insert the first match with proper quoting.
         if (matches[0])
            insert_match(matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, quote_char)
         end
         # If there are more matches, ring the bell to indicate.
         # If we are in vi mode, Posix.2 says to not ring the bell.
         # If the `show-all-if-ambiguous' variable is set, display
         # all the matches immediately.  Otherwise, if this was the
         # only match, and we are hacking files, check the file to
         # see if it was a directory.  If so, and the `mark-directories'
         # variable is set, add a '/' to the name.  If not, and we
         # are at the end of the line, then add a space.
         if (matches[1])
            if (what_to_do == '!')
               display_matches(matches)
            elsif (what_to_do == '@')
               if (!nontrivial_lcd)
                  display_matches(matches)
               end
            elsif (@rl_editing_mode != @vi_mode)
               rl_ding()   # There are other matches remaining.
            end
         else
            append_to_match(matches[0], delimiter, quote_char, nontrivial_lcd)
         end
      when '*'
         insert_all_matches(matches, start, quote_char)
      when '?'
         display_matches(matches)
      else
         $stderr.write("\r\nreadline: bad value #{what_to_do} for what_to_do in rl_complete\n")
         rl_ding()
         saved_line_buffer = nil
         rl_unsetstate(RL_STATE_COMPLETING)
         return 1
      end

      matches = nil

      # Check to see if the line has changed through all of this manipulation.
      if (saved_line_buffer)
         @completion_changed_buffer = @rl_line_buffer.delete(0.chr) != saved_line_buffer
         saved_line_buffer = nil
      end

      rl_unsetstate(RL_STATE_COMPLETING)
      0
   end

   # Complete the word at or before point.  You have supplied the function
   #   that does the initial simple matching selection algorithm (see
   #   rl_completion_matches ()).  The default is to do filename completion.
   def rl_complete(ignore, invoking_key)
      if (@rl_inhibit_completion)
         return (_rl_insert_char(ignore, invoking_key))
      elsif (@rl_last_func == :rl_complete && !@completion_changed_buffer)
         return (rl_complete_internal('?'))
      elsif (@_rl_complete_show_all)
         return (rl_complete_internal('!'))
      elsif (@_rl_complete_show_unmodified)
         return (rl_complete_internal('@'))
      else
         return (rl_complete_internal(TAB))
      end
   end

   # Return the history entry which is logically at OFFSET in the history array.
   #   OFFSET is relative to history_base.
   def history_get(offset)
      local_index = offset - @history_base
      return (local_index >= @history_length || local_index < 0 || @the_history.nil?) ?
      nil : @the_history[local_index]
   end

   def rl_replace_from_history(entry, flags)
      # Can't call with `1' because rl_undo_list might point to an undo list
      #   from a history entry, just like we're setting up here.
      rl_replace_line(entry.line, false)
      @rl_undo_list = entry.data
      @rl_point = @rl_end
      @rl_mark = 0

      if (@rl_editing_mode == @vi_mode)
         @rl_point = 0
         @rl_mark = @rl_end
      end
   end

   # Remove history element WHICH from the history.  The removed
   #   element is returned to you so you can free the line, data,
   #   and containing structure.
   def remove_history(which)
      if (which < 0 || which >= @history_length || @history_length ==  0 || @the_history.nil?)
         return nil
      end
      return_value = @the_history[which]
      @the_history.delete_at(which)
      @history_length-=1
      return_value
   end

   def block_sigint()
      return if @sigint_blocked
      #@sigint_proc = Signal.trap("INT","IGNORE")
      @sigint_blocked = true
   end

   def release_sigint()
      return if !@sigint_blocked
      #Signal.trap("INT",@sigint_proc)
      @sigint_blocked = false
   end

   def save_tty_chars()
      @_rl_last_tty_chars = @_rl_tty_chars
      h = Hash[*`stty -a`.scan(/(\w+) = ([^;]+);/).flatten]
      h.each {|k,v| v.gsub!(/\^(.)/){($1[0].ord ^ ((?a..?z).include?($1[0]) ? 0x60 : 0x40)).chr}}
      @_rl_tty_chars.t_erase = h['erase']
      @_rl_tty_chars.t_kill = h['kill']
      @_rl_tty_chars.t_intr = h['intr']
      @_rl_tty_chars.t_quit = h['quit']
      @_rl_tty_chars.t_start = h['start']
      @_rl_tty_chars.t_stop = h['stop']
      @_rl_tty_chars.t_eof = h['eof']
      @_rl_tty_chars.t_eol = "\n"
      @_rl_tty_chars.t_eol2 = h['eol2']
      @_rl_tty_chars.t_susp = h['susp']
      @_rl_tty_chars.t_dsusp = h['dsusp']
      @_rl_tty_chars.t_reprint = h['rprnt']
      @_rl_tty_chars.t_flush = h['flush']
      @_rl_tty_chars.t_werase = h['werase']
      @_rl_tty_chars.t_lnext = h['lnext']
      @_rl_tty_chars.t_status = -1
      @otio = `stty -g`
   end

   def _rl_bind_tty_special_chars(kmap)
      kmap[@_rl_tty_chars.t_erase] = :rl_rubout
      kmap[@_rl_tty_chars.t_kill] = :rl_unix_line_discard
      kmap[@_rl_tty_chars.t_werase] = :rl_unix_word_rubout
      kmap[@_rl_tty_chars.t_lnext] = :rl_quoted_insert
   end

   def prepare_terminal_settings(meta_flag)
      @readline_echoing_p = (`stty -a`.scan(/-*echo\b/).first == 'echo')

      # First, the basic settings to put us into character-at-a-time, no-echo
      #   input mode.
      setting = " -echo -icrnl cbreak"

      # If this terminal doesn't care how the 8th bit is used, then we can
      #   use it for the meta-key.  If only one of even or odd parity is
      #  specified, then the terminal is using parity, and we cannot.
      if (`stty -a`.scan(/-parenb\b/).first == '-parenb')
         setting << " pass8"
      end

      setting << " -ixoff"

      rl_bind_key(@_rl_tty_chars.t_start, :rl_restart_output)
      @_rl_eof_char = @_rl_tty_chars.t_eof

      #setting << " -isig"

      `stty #{setting}`
   end

   def _rl_control_keypad(on)
      if on && @_rl_term_ks
         @_rl_out_stream.write(@_rl_term_ks)
      elsif !on && @_rl_term_ke
         @_rl_out_stream.write(@_rl_term_ke)
      end
   end

   # Rebind all of the tty special chars that readline worries about back
   #   to self-insert.  Call this before saving the current terminal special
   #   chars with save_tty_chars().  This only works on POSIX termios or termio
   #   systems.
   def rl_tty_unset_default_bindings(kmap)
      # Don't bother before we've saved the tty special chars at least once.
      return if (!rl_isstate(RL_STATE_TTYCSAVED))

      kmap[@_rl_tty_chars.t_erase] = :rl_insert
      kmap[@_rl_tty_chars.t_kill] = :rl_insert
      kmap[@_rl_tty_chars.t_lnext] = :rl_insert
      kmap[@_rl_tty_chars.t_werase] = :rl_insert
   end

   def rl_prep_terminal(meta_flag)
      if no_terminal?
         @readline_echoing_p = true
         return
      end

      return if (@terminal_prepped)

      # Try to keep this function from being INTerrupted.
      block_sigint()

      if (@_rl_bind_stty_chars)
         # If editing in vi mode, make sure we restore the bindings in the
         # insertion keymap no matter what keymap we ended up in.
         if (@rl_editing_mode == @vi_mode)
            rl_tty_unset_default_bindings(@vi_insertion_keymap)
         else
            rl_tty_unset_default_bindings(@_rl_keymap)
         end
      end

      save_tty_chars()

      rl_setstate(RL_STATE_TTYCSAVED)
      if (@_rl_bind_stty_chars)

         # If editing in vi mode, make sure we set the bindings in the
         # insertion keymap no matter what keymap we ended up in.
         if (@rl_editing_mode == @vi_mode)
            _rl_bind_tty_special_chars(@vi_insertion_keymap)
         else
            _rl_bind_tty_special_chars(@_rl_keymap)
         end
      end

      prepare_terminal_settings(meta_flag)

      if (@_rl_enable_keypad)
         _rl_control_keypad(true)
      end
      @rl_outstream.flush
      @terminal_prepped = true
      rl_setstate(RL_STATE_TERMPREPPED)

      release_sigint()
   end

   # Restore the terminal's normal settings and modes.
   def rl_deprep_terminal()
      return if ENV["TERM"].nil?
      return if (!@terminal_prepped)

      # Try to keep this function from being interrupted.
      block_sigint()

      if (@_rl_enable_keypad)
         _rl_control_keypad(false)
      end

      @rl_outstream.flush

      # restore terminal setting
      `stty #{@otio}`

      @terminal_prepped = false
      rl_unsetstate(RL_STATE_TERMPREPPED)

      release_sigint()
   end

   # Set the mark at POSITION.
   def _rl_set_mark_at_pos(position)
      return -1 if (position > @rl_end)
      @rl_mark = position
      0
   end

   # A bindable command to set the mark.
   def rl_set_mark(count, key)
      _rl_set_mark_at_pos(@rl_explicit_arg ? count : @rl_point)
   end

   # Kill from here to the end of the line.  If DIRECTION is negative, kill
   #   back to the line start instead.
   def rl_kill_line (direction, ignore)
      if (direction < 0)
         return (rl_backward_kill_line(1, ignore))
      else
         orig_point = @rl_point
         rl_end_of_line(1, ignore)
         if (orig_point != @rl_point)
            rl_kill_text(orig_point, @rl_point)
         end
         @rl_point = orig_point
         if (@rl_editing_mode == @emacs_mode)
            @rl_mark = @rl_point
         end
      end
      0
   end

   # Kill backwards to the start of the line.  If DIRECTION is negative, kill
   #   forwards to the line end instead.
   def rl_backward_kill_line(direction, ignore)
      if (direction < 0)
         return (rl_kill_line(1, ignore))
      else
         if (@rl_point==0)
            rl_ding()
         else
            orig_point = @rl_point
            rl_beg_of_line(1, ignore)
            if (@rl_point != orig_point)
               rl_kill_text(orig_point, @rl_point)
            end
            if (@rl_editing_mode == @emacs_mode)
               @rl_mark = @rl_point
            end
         end
      end
      0
   end

   # Kill the whole line, no matter where point is.
   def rl_kill_full_line(count, ignore)
      rl_begin_undo_group()
      @rl_point = 0
      rl_kill_text(@rl_point, @rl_end)
      @rl_mark = 0
      rl_end_undo_group()
      0
   end

   # Search backwards through the history looking for a string which is typed
   #   interactively.  Start with the current line.
   def rl_reverse_search_history(sign, key)
      rl_search_history(-sign, key)
   end

   # Search forwards through the history looking for a string which is typed
   #   interactively.  Start with the current line.
   def rl_forward_search_history(sign, key)
      rl_search_history(sign, key)
   end

   # Search through the history looking for an interactively typed string.
   #   This is analogous to i-search.  We start the search in the current line.
   #   DIRECTION is which direction to search; >= 0 means forward, < 0 means
   #   backwards.
   def rl_search_history(direction, invoking_key)
      rl_setstate(RL_STATE_ISEARCH)
      cxt = _rl_isearch_init(direction)

      rl_display_search(cxt.search_string, (cxt.sflags & SF_REVERSE)!=0, -1)

      # If we are using the callback interface, all we do is set up here and
      #    return.  The key is that we leave RL_STATE_ISEARCH set.
      if (rl_isstate(RL_STATE_CALLBACK))
         return (0)
      end

      r = -1
      while(true)
         c = _rl_search_getchar(cxt)
         # We might want to handle EOF here (c == 0)
         r = _rl_isearch_dispatch(cxt, cxt.lastc)
         break if (r <= 0)
      end

      # The searching is over.  The user may have found the string that she
      #   was looking for, or else she may have exited a failing search.  If
      #   LINE_INDEX is -1, then that shows that the string searched for was
      #   not found.  We use this to determine where to place rl_point.
      _rl_isearch_cleanup(cxt, r)
   end

   def _rl_scxt_alloc(type, flags)
      cxt = Struct.new(:type,:sflags,:search_string,:search_string_index,:search_string_size,:lines,:allocated_line,
      :hlen,:hindex,:save_point,:save_mark,:save_line,:last_found_line,:prev_line_found,:save_undo_list,:history_pos,
      :direction,:lastc,:sline,:sline_len,:sline_index,:search_terminators,:mb).new

      cxt.type = type
      cxt.sflags = flags

      cxt.search_string = nil
      cxt.search_string_size = cxt.search_string_index = 0

      cxt.lines = nil
      cxt.allocated_line = nil
      cxt.hlen = cxt.hindex = 0

      cxt.save_point = @rl_point
      cxt.save_mark = @rl_mark
      cxt.save_line = where_history()
      cxt.last_found_line = cxt.save_line
      cxt.prev_line_found = nil

      cxt.save_undo_list = nil

      cxt.history_pos = 0
      cxt.direction = 0

      cxt.lastc = 0

      cxt.sline = nil
      cxt.sline_len = cxt.sline_index = 0

      cxt.search_terminators = nil

      cxt
   end

   def history_list()
      @the_history
   end

   def _rl_isearch_init(direction)
      cxt = _rl_scxt_alloc(RL_SEARCH_ISEARCH, 0)
      if (direction < 0)
         cxt.sflags |= SF_REVERSE
      end

      cxt.search_terminators = @_rl_isearch_terminators ? @_rl_isearch_terminators :
      @default_isearch_terminators

      # Create an arrary of pointers to the lines that we want to search.
      hlist = history_list()
      rl_maybe_replace_line()
      i = 0
      if (hlist)
         i += 1 while(hlist[i])
      end

      # Allocate space for this many lines, +1 for the current input line,
      #   and remember those lines.
      cxt.hlen = i
      cxt.lines = []
      for i in 0 ... cxt.hlen
         cxt.lines[i] = hlist[i].line
      end

      if (@_rl_saved_line_for_history)
         cxt.lines[i] = @_rl_saved_line_for_history.line.dup
      else
         # Keep track of this so we can free it.
         cxt.allocated_line = @rl_line_buffer.dup
         cxt.lines << cxt.allocated_line
      end

      cxt.hlen+=1

      # The line where we start the search.
      cxt.history_pos = cxt.save_line

      rl_save_prompt()

      # Initialize search parameters.
      cxt.search_string_size = 128
      cxt.search_string_index = 0
      cxt.search_string = ""

      # Normalize DIRECTION into 1 or -1.
      cxt.direction = (direction >= 0) ? 1 : -1

      cxt.sline = @rl_line_buffer
      cxt.sline_len = cxt.sline.delete(0.chr).length
      cxt.sline_index = @rl_point

      @_rl_iscxt = cxt     # save globally
      cxt
   end

   def rl_save_prompt()
      @saved_local_prompt = @local_prompt
      @saved_local_prefix = @local_prompt_prefix
      @saved_prefix_length = @prompt_prefix_length
      @saved_local_length = @local_prompt_len
      @saved_last_invisible = @prompt_last_invisible
      @saved_visible_length = @prompt_visible_length
      @saved_invis_chars_first_line = @prompt_invis_chars_first_line
      @saved_physical_chars = @prompt_physical_chars

      @local_prompt = @local_prompt_prefix = nil
      @local_prompt_len = 0
      @prompt_last_invisible = @prompt_visible_length = @prompt_prefix_length = 0
      @prompt_invis_chars_first_line = @prompt_physical_chars = 0
   end

   def rl_restore_prompt()
      @local_prompt = nil
      @local_prompt_prefix = nil

      @local_prompt = @saved_local_prompt
      @local_prompt_prefix = @saved_local_prefix
      @local_prompt_len = @saved_local_length
      @prompt_prefix_length = @saved_prefix_length
      @prompt_last_invisible = @saved_last_invisible
      @prompt_visible_length = @saved_visible_length
      @prompt_invis_chars_first_line = @saved_invis_chars_first_line
      @prompt_physical_chars = @saved_physical_chars

      # can test saved_local_prompt to see if prompt info has been saved.
      @saved_local_prompt = @saved_local_prefix = nil
      @saved_local_length = 0
      @saved_last_invisible = @saved_visible_length = @saved_prefix_length = 0
      @saved_invis_chars_first_line = @saved_physical_chars = 0
   end

   def rl_message(msg_buf)
      @rl_display_prompt = msg_buf
      if @saved_local_prompt.nil?
         rl_save_prompt()
         @msg_saved_prompt = true
      end
      @local_prompt,@prompt_visible_length,@prompt_last_invisible,@prompt_invis_chars_first_line,@prompt_physical_chars =
      expand_prompt(msg_buf)
      @local_prompt_prefix = nil
      @local_prompt_len = @local_prompt ? @local_prompt.length : 0
      send(@rl_redisplay_function)
      0
   end

   # Display the current state of the search in the echo-area.
   #   SEARCH_STRING contains the string that is being searched for,
   #   DIRECTION is zero for forward, or non-zero for reverse,
   #   WHERE is the history list number of the current line.  If it is
   #   -1, then this line is the starting one.
   def rl_display_search(search_string, reverse_p, where)
      message = '('
      if (reverse_p)
         message << "reverse-"
      end
      message << "i-search)`"

      if (search_string)
         message << search_string
      end
      message << "': "

      rl_message(message)
      message = nil
      send(@rl_redisplay_function)
   end

   # Transpose the characters at point.  If point is at the end of the line,
   #   then transpose the characters before point.
   def rl_transpose_chars(count, key)
      return 0 if (count == 0)

      if (@rl_point==0 || @rl_end < 2)
         rl_ding()
         return -1
      end

      rl_begin_undo_group()

      if (@rl_point == @rl_end)
         if !@rl_byte_oriented
            @rl_point = _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO)
         else
            @rl_point -= 1
         end
         count = 1
      end

      prev_point = @rl_point
      if !@rl_byte_oriented
         @rl_point = _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO)
      else
         @rl_point -= 1
      end

      char_length = prev_point - @rl_point
      dummy = @rl_line_buffer[@rl_point,char_length]

      rl_delete_text(@rl_point, @rl_point + char_length)

      @rl_point += count
      _rl_fix_point(0)
      rl_insert_text(dummy)
      rl_end_undo_group()
      dummy = nil
      0
   end

   # Here is C-u doing what Unix does.  You don't *have* to use these
   # key-bindings.  We have a choice of killing the entire line, or
   # killing from where we are to the start of the line.  We choose the
   # latter, because if you are a Unix weenie, then you haven't backspaced
   # into the line at all, and if you aren't, then you know what you are
   # doing.
   def rl_unix_line_discard(count, key)
      if (@rl_point == 0)
         rl_ding()
      else
         rl_kill_text(@rl_point, 0)
         @rl_point = 0
         if (@rl_editing_mode == @emacs_mode)
            @rl_mark = @rl_point
         end
      end
      0
   end

   # Yank back the last killed text.  This ignores arguments.
   def rl_yank(count, ignore)
      if @rl_kill_ring.nil?
         _rl_abort_internal()
         return -1
      end
      _rl_set_mark_at_pos(@rl_point)
      rl_insert_text(@rl_kill_ring[@rl_kill_index])
      0
   end

   # If the last command was yank, or yank_pop, and the text just
   #   before point is identical to the current kill item, then
   #   delete that text from the line, rotate the index down, and
   #   yank back some other text.
   def rl_yank_pop(count, key)
      if (((@rl_last_func != :rl_yank_pop) && (@rl_last_func != :rl_yank)) ||
         @rl_kill_ring.nil?)
         _rl_abort_internal()
         return -1
      end

      l = @rl_kill_ring[@rl_kill_index].length
      n = @rl_point - l
      if (n >= 0 && @rl_line_buffer[n,l] == @rl_kill_ring[@rl_kill_index][0,l])
         rl_delete_text(n, @rl_point)
         @rl_point = n
         @rl_kill_index-=1
         if (@rl_kill_index < 0)
            @rl_kill_index = @rl_kill_ring_length - 1
         end
         rl_yank(1, 0)
         return 0
      else
         _rl_abort_internal()
         return -1
      end
   end

   # Yank the COUNTh argument from the previous history line, skipping
   #   HISTORY_SKIP lines before looking for the `previous line'.
   def rl_yank_nth_arg_internal(count, ignore, history_skip)
      pos = where_history()
      if (history_skip>0)
         history_skip.times { entry = previous_history() }
      end
      entry = previous_history()
      history_set_pos(pos)
      if entry.nil?
         rl_ding()
         return -1
      end

      arg = history_arg_extract(count, count, entry.line)
      if (arg.nil? || arg=='')
         rl_ding()
         arg = nil
         return -1
      end

      rl_begin_undo_group()

      _rl_set_mark_at_pos(@rl_point)

      # Vi mode always inserts a space before yanking the argument, and it
      #   inserts it right *after* rl_point.
      if (@rl_editing_mode == @vi_mode)
         rl_vi_append_mode(1, ignore)
         rl_insert_text(" ")
      end

      rl_insert_text(arg)
      arg = nil
      rl_end_undo_group()
      return 0
   end

   # Yank the COUNTth argument from the previous history line.
   def rl_yank_nth_arg(count, ignore)
      rl_yank_nth_arg_internal(count, ignore, 0)
   end

   # Yank the last argument from the previous history line.  This `knows'
   #   how rl_yank_nth_arg treats a count of `$'.  With an argument, this
   #   behaves the same as rl_yank_nth_arg.
   @history_skip = 0
   @explicit_arg_p = false
   @count_passed = 1
   @direction = 1
   @undo_needed = false

   def rl_yank_last_arg(count, key)
      if (@rl_last_func != :rl_yank_last_arg)
         @history_skip = 0
         @explicit_arg_p = @rl_explicit_arg
         @count_passed = count
         @direction = 1
      else
         if (@undo_needed)
            rl_do_undo()
         end
         if (count < 1)
            @direction = -@direction
         end
         @history_skip += @direction
         if (@history_skip < 0)
            @history_skip = 0
         end
      end

      if (@explicit_arg_p)
         retval = rl_yank_nth_arg_internal(@count_passed, key, @history_skip)
      else
         retval = rl_yank_nth_arg_internal('$', key, @history_skip)
      end
      @undo_needed = retval == 0
      retval
   end

   def _rl_char_search_internal(count, dir, smbchar, len)
      pos = @rl_point
      inc = (dir < 0) ? -1 : 1
      while (count!=0)
         if ((dir < 0 && pos <= 0) || (dir > 0 && pos >= @rl_end))
            rl_ding()
            return -1
         end
         pos = (inc > 0) ? _rl_find_next_mbchar(@rl_line_buffer, pos, 1, MB_FIND_ANY) :
         _rl_find_prev_mbchar(@rl_line_buffer, pos, MB_FIND_ANY)
         begin
            if (_rl_is_mbchar_matched(@rl_line_buffer, pos, @rl_end, smbchar, len)!=0)
               count-=1
               if (dir < 0)
                  @rl_point = (dir == BTO) ? pos+1 : pos
               else
                  @rl_point = (dir == FTO) ? pos-1 : pos
               end
               break
            end
            prepos = pos
         end while ((dir < 0) ? (pos = _rl_find_prev_mbchar(@rl_line_buffer, pos, MB_FIND_ANY)) != prepos :
         (pos = _rl_find_next_mbchar(@rl_line_buffer, pos, 1, MB_FIND_ANY)) != prepos)
      end
      0
   end

   def _rl_char_search(count, fdir, bdir)
      mbchar = ''
      mb_len = _rl_read_mbchar(mbchar, MB_LEN_MAX)

      if (count < 0)
         return (_rl_char_search_internal(-count, bdir, mbchar, mb_len))
      else
         return (_rl_char_search_internal(count, fdir, mbchar, mb_len))
      end
   end

   def rl_char_search(count, key)
      _rl_char_search(count, FFIND, BFIND)
   end

   # Undo the next thing in the list.  Return 0 if there
   #   is nothing to undo, or non-zero if there was.
   def trans(i)
      ((i) == -1 ? @rl_point : ((i) == -2 ? @rl_end : (i)))
   end

   def rl_do_undo()
      start = _end = waiting_for_begin = 0
      begin
         return 0 if @rl_undo_list.nil?

         @_rl_doing_an_undo = true
         rl_setstate(RL_STATE_UNDOING)

         # To better support vi-mode, a start or end value of -1 means
         # rl_point, and a value of -2 means rl_end.
         if (@rl_undo_list.what == UNDO_DELETE || @rl_undo_list.what == UNDO_INSERT)
            start = trans(@rl_undo_list.start)
            _end = trans(@rl_undo_list.end)
         end

         case (@rl_undo_list.what)
            # Undoing deletes means inserting some text.
         when UNDO_DELETE
            @rl_point = start
            rl_insert_text(@rl_undo_list.text)
            @rl_undo_list.text = nil

            # Undoing inserts means deleting some text.
         when UNDO_INSERT
            rl_delete_text(start, _end)
            @rl_point = start
            # Undoing an END means undoing everything 'til we get to a BEGIN.
         when UNDO_END
            waiting_for_begin+=1
            # Undoing a BEGIN means that we are done with this group.
         when UNDO_BEGIN
            if (waiting_for_begin!=0)
               waiting_for_begin-=1
            else
               rl_ding()
            end
         end

         @_rl_doing_an_undo = false
         rl_unsetstate(RL_STATE_UNDOING)

         release = @rl_undo_list
         @rl_undo_list = @rl_undo_list.next
         replace_history_data(-1, release, @rl_undo_list)
         release = nil
      end while (waiting_for_begin!=0)

      1
   end



   # Do some undoing of things that were done.
   def rl_undo_command(count, key)
      if (count < 0)
         return 0 # Nothing to do.
      end
      while (count>0)
         if (rl_do_undo())
            count-=1
         else
            rl_ding()
            break
         end
      end
      0
   end

   # Delete the word at point, saving the text in the kill ring.
   def rl_kill_word(count, key)
      if (count < 0)
         return (rl_backward_kill_word(-count, key))
      else
         orig_point = @rl_point
         rl_forward_word(count, key)

         if (@rl_point != orig_point)
            rl_kill_text(orig_point, @rl_point)
         end

         @rl_point = orig_point
         if (@rl_editing_mode == @emacs_mode)
            rl_mark = @rl_point
         end
      end
      0
   end

   # Rubout the word before point, placing it on the kill ring.
   def rl_backward_kill_word(count, ignore)
      if (count < 0)
         return (rl_kill_word(-count, ignore))
      else
         orig_point = @rl_point
         rl_backward_word(count, ignore)
         if (@rl_point != orig_point)
            rl_kill_text(orig_point, @rl_point)
         end
         if (@rl_editing_mode == @emacs_mode)
            @rl_mark = @rl_point
         end
      end
      0
   end

   # Revert the current line to its previous state.
   def rl_revert_line(count, key)
      if @rl_undo_list.nil?
         rl_ding()
      else
         while (@rl_undo_list)
            rl_do_undo()
         end
         if (@rl_editing_mode == @vi_mode)
            @rl_point = @rl_mark = 0      # rl_end should be set correctly
         end
      end
      0
   end

   def rl_backward_char_search (count, key)
      _rl_char_search(count, BFIND, FFIND)
   end

   def rl_insert_completions(ignore, invoking_key)
      rl_complete_internal('*')
   end

   def _rl_arg_init()
      rl_save_prompt()
      @_rl_argcxt = 0
      rl_setstate(RL_STATE_NUMERICARG)
   end

   def _rl_arg_getchar()
      rl_message("(arg: #{@rl_arg_sign * @rl_numeric_arg}) ")
      rl_setstate(RL_STATE_MOREINPUT)
      c = rl_read_key()
      rl_unsetstate(RL_STATE_MOREINPUT)
      c
   end

   # Process C as part of the current numeric argument.  Return -1 if the
   #   argument should be aborted, 0 if we should not read any more chars, and
   #   1 if we should continue to read chars.
   def _rl_arg_dispatch(cxt, c)
      key = c

      # If we see a key bound to `universal-argument' after seeing digits,
      #    it ends the argument but is otherwise ignored.
      if (@_rl_keymap[c] == :rl_universal_argument)
         if ((cxt & NUM_SAWDIGITS) == 0)
            @rl_numeric_arg *= 4
            return 1
         elsif (rl_isstate(RL_STATE_CALLBACK))
            @_rl_argcxt |= NUM_READONE
            return 0 # XXX
         else
            rl_setstate(RL_STATE_MOREINPUT)
            key = rl_read_key()
            rl_unsetstate(RL_STATE_MOREINPUT)
            rl_restore_prompt()
            rl_clear_message()
            rl_unsetstate(RL_STATE_NUMERICARG)
            return (_rl_dispatch(key, @_rl_keymap))
         end
      end

      #c = (c[0].ord & ~0x80).chr
      r = c[1,1]
      if (r>='0' && r<='9')
         r = r.to_i
         @rl_numeric_arg = @rl_explicit_arg ? (@rl_numeric_arg * 10) +  r : r
         @rl_explicit_arg = 1
         @_rl_argcxt |= NUM_SAWDIGITS
      elsif (c == '-' && !@rl_explicit_arg)
         @rl_numeric_arg = 1
         @_rl_argcxt |= NUM_SAWMINUS
         @rl_arg_sign = -1
      else
         # Make M-- command equivalent to M--1 command.
         if ((@_rl_argcxt & NUM_SAWMINUS)!=0 && @rl_numeric_arg == 1 && !@rl_explicit_arg)
            @rl_explicit_arg = 1
         end
         rl_restore_prompt()
         rl_clear_message()
         rl_unsetstate(RL_STATE_NUMERICARG)

         r = _rl_dispatch(key, @_rl_keymap)
         if (rl_isstate(RL_STATE_CALLBACK))
            # At worst, this will cause an extra redisplay.  Otherwise,
            #   we have to wait until the next character comes in.
            if (!@rl_done)
               send(@rl_redisplay_function)
            end
            r = 0
         end
         return r
      end
      1
   end

   def _rl_arg_overflow()
      if (@rl_numeric_arg > 1000000)
         @_rl_argcxt = 0
         @rl_explicit_arg = @rl_numeric_arg = 0
         rl_ding()
         rl_restore_prompt()
         rl_clear_message()
         rl_unsetstate(RL_STATE_NUMERICARG)
         return 1
      end
      0
   end

   # Handle C-u style numeric args, as well as M--, and M-digits.
   def rl_digit_loop()
      while (true)
         return 1 if _rl_arg_overflow()!=0
         c = _rl_arg_getchar()
         if (c >= "\xFE")
            _rl_abort_internal()
            return -1
         end
         r = _rl_arg_dispatch(@_rl_argcxt, c)
         break if (r <= 0 || !rl_isstate(RL_STATE_NUMERICARG))
      end

      return r
   end

   # Start a numeric argument with initial value KEY
   def rl_digit_argument(ignore, key)
      _rl_arg_init()
      if (rl_isstate(RL_STATE_CALLBACK))
         _rl_arg_dispatch(@_rl_argcxt, key)
         rl_message("(arg: #{@rl_arg_sign * @rl_numeric_arg}) ")
         return 0
      else
         rl_execute_next(key)
         return (rl_digit_loop())
      end
   end

   # Make C be the next command to be executed.
   def rl_execute_next(c)
      @rl_pending_input = c
      rl_setstate(RL_STATE_INPUTPENDING)
      0
   end

   # Meta-< goes to the start of the history.
   def rl_beginning_of_history(count, key)
      rl_get_previous_history(1 + where_history(), key)
   end

   # Meta-> goes to the end of the history.  (The current line).
   def rl_end_of_history(count, key)
      rl_maybe_replace_line()
      using_history()
      rl_maybe_unsave_line()
      0
   end

   # Uppercase the word at point.
   def rl_upcase_word(count, key)
      rl_change_case(count, UpCase)
   end

   # Lowercase the word at point.
   def rl_downcase_word(count, key)
      rl_change_case(count, DownCase)
   end

   # Upcase the first letter, downcase the rest.
   def rl_capitalize_word(count, key)
      rl_change_case(count, CapCase)
   end

   # Save an undo entry for the text from START to END.
   def rl_modifying(start, _end)
      if (start > _end)
         start,_end = _end,start
      end

      if (start != _end)
         temp = rl_copy_text(start, _end)
         rl_begin_undo_group()
         rl_add_undo(UNDO_DELETE, start, _end, temp)
         rl_add_undo(UNDO_INSERT, start, _end, nil)
         rl_end_undo_group()
      end
      0
   end

   # The meaty function.
   #   Change the case of COUNT words, performing OP on them.
   #   OP is one of UpCase, DownCase, or CapCase.
   #   If a negative argument is given, leave point where it started,
   #   otherwise, leave it where it moves to.
   def rl_change_case(count, op)
      start = @rl_point
      rl_forward_word(count, 0)
      _end = @rl_point

      if (op != UpCase && op != DownCase && op != CapCase)
         rl_ding()
         return -1
      end

      if (count < 0)
         start,_end = _end,start
      end

      # We are going to modify some text, so let's prepare to undo it.
      rl_modifying(start, _end)

      inword = false
      while (start < _end)
         c = _rl_char_value(@rl_line_buffer, start)
         #  This assumes that the upper and lower case versions are the same width.
         if !@rl_byte_oriented
            _next = _rl_find_next_mbchar(@rl_line_buffer, start, 1, MB_FIND_NONZERO)
         else
            _next = start + 1
         end

         if (!_rl_walphabetic(c))
            inword = false
            start = _next
            next
         end

         if (op == CapCase)
            nop = inword ? DownCase : UpCase
            inword = true
         else
            nop = op
         end
         if (isascii(c))
            nc = (nop == UpCase) ? c.upcase : c.downcase
            @rl_line_buffer[start] = nc
         end

         start = _next
      end

      @rl_point = _end
      0
   end

   def isascii(c)
    int_val = c[0].to_i # 1.8 + 1.9 compat.
    return (int_val < 128 && int_val > 0)
   end

   # Search non-interactively through the history list.  DIR < 0 means to
   #   search backwards through the history of previous commands; otherwise
   #   the search is for commands subsequent to the current position in the
   #   history list.  PCHAR is the character to use for prompting when reading
   #   the search string; if not specified (0), it defaults to `:'.
   def noninc_search(dir, pchar)
      cxt = _rl_nsearch_init(dir, pchar)
      if (rl_isstate(RL_STATE_CALLBACK))
         return (0)
      end
      # Read the search string.
      r = 0
      while (true)
         c = _rl_search_getchar(cxt)
         if (c == 0.chr)
            break
         end
         r = _rl_nsearch_dispatch(cxt, c)
         if (r < 0)
            return 1
         elsif (r == 0)
            break
         end
      end

      r = _rl_nsearch_dosearch(cxt)
      (r >= 0) ? _rl_nsearch_cleanup(cxt, r) : (r != 1)
   end

   # Search forward through the history list for a string.  If the vi-mode
   #   code calls this, KEY will be `?'.
   def rl_noninc_forward_search(count, key)
      noninc_search(1, (key == '?') ? '?' : nil)
   end

   # Reverse search the history list for a string.  If the vi-mode code
   #   calls this, KEY will be `/'.
   def rl_noninc_reverse_search(count, key)
      noninc_search(-1, (key == '/') ? '/' : nil)
   end

   # Make the data from the history entry ENTRY be the contents of the
   #   current line.  This doesn't do anything with rl_point; the caller
   #   must set it.
   def make_history_line_current(entry)
      _rl_replace_text(entry.line, 0, @rl_end)
      _rl_fix_point(1)
      if (@rl_editing_mode == @vi_mode)
         # POSIX.2 says that the `U' command doesn't affect the copy of any
         #   command lines to the edit line.  We're going to implement that by
         #   making the undo list start after the matching line is copied to the
         #   current editing buffer.
         rl_free_undo_list()
      end
      if (@_rl_saved_line_for_history)
         @_rl_saved_line_for_history = nil
      end
   end

   # Make the current history item be the one at POS, an absolute index.
   #   Returns zero if POS is out of range, else non-zero.
   def history_set_pos(pos)
      if (pos > @history_length || pos < 0 || @the_history.nil?)
         return (0)
      end
      @history_offset = pos
      1
   end

   # Do an anchored search for string through the history in DIRECTION.
   def history_search_prefix (string, direction)
      history_search_internal(string, direction, ANCHORED_SEARCH)
   end

   # Search for STRING in the history list.  DIR is < 0 for searching
   #   backwards.  POS is an absolute index into the history list at
   #   which point to begin searching.
   def history_search_pos(string, dir, pos)
      old = where_history()
      history_set_pos(pos)
      if (history_search(string, dir) == -1)
         history_set_pos(old)
         return (-1)
      end
      ret = where_history()
      history_set_pos(old)
      ret
   end

   # Search the history list for STRING starting at absolute history position
   #   POS.  If STRING begins with `^', the search must match STRING at the
   #   beginning of a history line, otherwise a full substring match is performed
   #   for STRING.  DIR < 0 means to search backwards through the history list,
   #   DIR >= 0 means to search forward.
   def noninc_search_from_pos(string, pos, dir)
      return 1 if (pos < 0)

      old = where_history()
      return -1 if (history_set_pos(pos) == 0)

      rl_setstate(RL_STATE_SEARCH)
      if (string[0,1] == '^')
         ret = history_search_prefix(string + 1, dir)
      else
         ret = history_search(string, dir)
      end
      rl_unsetstate(RL_STATE_SEARCH)

      if (ret != -1)
         ret = where_history()
      end
      history_set_pos(old)
      ret
   end

   # Search for a line in the history containing STRING.  If DIR is < 0, the
   #   search is backwards through previous entries, else through subsequent
   #   entries.  Returns 1 if the search was successful, 0 otherwise.
   def noninc_dosearch(string, dir)
      if (string.nil? || string == '' || @noninc_history_pos < 0)
         rl_ding()
         return 0
      end

      pos = noninc_search_from_pos(string, @noninc_history_pos + dir, dir)
      if (pos == -1)
         # Search failed, current history position unchanged.
         rl_maybe_unsave_line()
         rl_clear_message()
         @rl_point = 0
         rl_ding()
         return 0
      end

      @noninc_history_pos = pos

      oldpos = where_history()
      history_set_pos(@noninc_history_pos)
      entry = current_history()
      if (@rl_editing_mode != @vi_mode)
         history_set_pos(oldpos)
      end
      make_history_line_current(entry)
      @rl_point = 0
      @rl_mark = @rl_end
      rl_clear_message()
      1
   end

   def _rl_make_prompt_for_search(pchar)
      rl_save_prompt()

      # We've saved the prompt, and can do anything with the various prompt
      #   strings we need before they're restored.  We want the unexpanded
      #   portion of the prompt string after any final newline.
      _p = @rl_prompt ? @rl_prompt.rindex("\n") : nil
      if _p.nil?
         len = (@rl_prompt && @rl_prompt.length>0 ) ? @rl_prompt.length : 0
         if (len>0)
            pmt = @rl_prompt.dup
         else
            pmt = ''
         end
         pmt << pchar
      else
         _p+=1
         pmt = @rl_prompt[_p..-1]
         pmt << pchar
      end

      # will be overwritten by expand_prompt, called from rl_message
      @prompt_physical_chars = @saved_physical_chars + 1
      pmt
   end

   def _rl_nsearch_init(dir, pchar)
      cxt = _rl_scxt_alloc(RL_SEARCH_NSEARCH, 0)
      if (dir < 0)
         cxt.sflags |= SF_REVERSE      # not strictly needed
      end
      cxt.direction = dir
      cxt.history_pos = cxt.save_line
      rl_maybe_save_line()
      # Clear the undo list, since reading the search string should create its
      #   own undo list, and the whole list will end up being freed when we
      #   finish reading the search string.
      @rl_undo_list = nil

      # Use the line buffer to read the search string.
      @rl_line_buffer[0,1] = 0.chr
      @rl_end = @rl_point = 0

      _p = _rl_make_prompt_for_search(pchar ? pchar : ':')
      rl_message(_p)
      _p = nil

      rl_setstate(RL_STATE_NSEARCH)
      @_rl_nscxt = cxt
      cxt
   end

   def _rl_nsearch_cleanup(cxt, r)
      cxt = nil
      @_rl_nscxt = nil
      rl_unsetstate(RL_STATE_NSEARCH)
      r != 1
   end

   def _rl_nsearch_abort(cxt)
      rl_maybe_unsave_line()
      rl_clear_message()
      @rl_point = cxt.save_point
      @rl_mark = cxt.save_mark
      rl_restore_prompt()
      rl_unsetstate(RL_STATE_NSEARCH)
   end

   # Process just-read character C according to search context CXT.  Return -1
   #   if the caller should abort the search, 0 if we should break out of the
   #   loop, and 1 if we should continue to read characters.
   def _rl_nsearch_dispatch(cxt, c)
      case (c)
      when "\C-W"
         rl_unix_word_rubout(1, c)
      when "\C-W"
         rl_unix_line_discard(1, c)
      when RETURN,NEWLINE
         return 0
      when "\C-H",RUBOUT
         if (@rl_point == 0)
            _rl_nsearch_abort(cxt)
            return -1
         end
         _rl_rubout_char(1, c)
      when "\C-C","\C-G"
         rl_ding()
         _rl_nsearch_abort(cxt)
         return -1
      else
         if !@rl_byte_oriented
            rl_insert_text(cxt.mb)
         else
            _rl_insert_char(1, c)
         end
      end

      send(@rl_redisplay_function)
      1
   end

   # Perform one search according to CXT, using NONINC_SEARCH_STRING.  Return
   #   -1 if the search should be aborted, any other value means to clean up
   #   using _rl_nsearch_cleanup ().  Returns 1 if the search was successful,
   #   0 otherwise.
   def _rl_nsearch_dosearch(cxt)
      @rl_mark = cxt.save_mark

      # If rl_point == 0, we want to re-use the previous search string and
      #   start from the saved history position.  If there's no previous search
      #   string, punt.
      if (@rl_point == 0)
         if @noninc_search_string.nil?
            rl_ding()
            rl_restore_prompt()
            rl_unsetstate(RL_STATE_NSEARCH)
            return -1
         end
      else
         # We want to start the search from the current history position.
         @noninc_history_pos = cxt.save_line
         @noninc_search_string = @rl_line_buffer.dup

         # If we don't want the subsequent undo list generated by the search
         #matching a history line to include the contents of the search string,
         #we need to clear rl_line_buffer here.  For now, we just clear the
         #undo list generated by reading the search string.  (If the search
         #fails, the old undo list will be restored by rl_maybe_unsave_line.)
         rl_free_undo_list()
      end

      rl_restore_prompt()
      noninc_dosearch(@noninc_search_string, cxt.direction)
   end

   # Transpose the words at point.  If point is at the end of the line,
   #   transpose the two words before point.
   def rl_transpose_words(count, key)
      orig_point = @rl_point

      return if (count==0)

      # Find the two words.
      rl_forward_word(count, key)
      w2_end = @rl_point
      rl_backward_word(1, key)
      w2_beg = @rl_point
      rl_backward_word(count, key)
      w1_beg = @rl_point
      rl_forward_word(1, key)
      w1_end = @rl_point

      # Do some check to make sure that there really are two words.
      if ((w1_beg == w2_beg) || (w2_beg < w1_end))
         rl_ding()
         @rl_point = orig_point
         return -1
      end

      # Get the text of the words.
      word1 = rl_copy_text(w1_beg, w1_end)
      word2 = rl_copy_text(w2_beg, w2_end)

      # We are about to do many insertions and deletions.  Remember them
      #   as one operation.
      rl_begin_undo_group()

      # Do the stuff at word2 first, so that we don't have to worry
      #   about word1 moving.
      @rl_point = w2_beg
      rl_delete_text(w2_beg, w2_end)
      rl_insert_text(word1)

      @rl_point = w1_beg
      rl_delete_text(w1_beg, w1_end)
      rl_insert_text(word2)

      # This is exactly correct since the text before this point has not
      #   changed in length.
      @rl_point = w2_end

      # I think that does it.
      rl_end_undo_group()
      word1 = nil
      word2 = nil

      0
   end

   # Re-read the current keybindings file.
   def rl_re_read_init_file(count, ignore)
      r = rl_read_init_file(nil)
      rl_set_keymap_from_edit_mode()
      r
   end

   # Exchange the position of mark and point.
   def rl_exchange_point_and_mark(count, key)
      if (@rl_mark > @rl_end)
         @rl_mark = -1
      end
      if (@rl_mark == -1)
         rl_ding()
         return -1
      else
         @rl_point, @rl_mark = @rl_mark, @rl_point
      end
      0
   end

   # A convenience function for displaying a list of strings in
   #   columnar format on readline's output stream.  MATCHES is the list
   #   of strings, in argv format, LEN is the number of strings in MATCHES,
   #   and MAX is the length of the longest string in MATCHES.
   def rl_display_match_list(matches, len, max)
      # How many items of MAX length can we fit in the screen window?
      max += 2
      limit = @_rl_screenwidth / max
      if (limit != 1 && (limit * max == @_rl_screenwidth))
         limit-=1
      end
      # Avoid a possible floating exception.  If max > _rl_screenwidth,
      #   limit will be 0 and a divide-by-zero fault will result.
      if (limit == 0)
         limit = 1
      end
      # How many iterations of the printing loop?
      count = (len + (limit - 1)) / limit

      # Watch out for special case.  If LEN is less than LIMIT, then
      #   just do the inner printing loop.
      #     0 < len <= limit  implies  count = 1.

      # Sort the items if they are not already sorted.
      if (!@rl_ignore_completion_duplicates)
         matches[1,len] = matches[1,len].sort
      end
      rl_crlf()

      lines = 0
      if (!@_rl_print_completions_horizontally)
         # Print the sorted items, up-and-down alphabetically, like ls.
         for i in 1 .. count
            l = i
            for j in 0 ... limit
               if (l > len || matches[l].nil?)
                  break
               else
                  temp = printable_part(matches[l])
                  printed_len = print_filename(temp, matches[l])

                  if (j + 1 < limit)
                     @rl_outstream.write(' '*(max - printed_len))
                  end
               end
               l += count
            end
            rl_crlf()
            lines+=1
            if (@_rl_page_completions && lines >= (@_rl_screenheight - 1) && i < count)
               lines = _rl_internal_pager(lines)
               return if (lines < 0)
            end
         end
      else
         # Print the sorted items, across alphabetically, like ls -x.
         i = 1
         while(matches[i])
            temp = printable_part(matches[i])
            printed_len = print_filename(temp, matches[i])
            # Have we reached the end of this line?
            if (matches[i+1])
               if ((limit > 1) && (i % limit) == 0)
                  rl_crlf()
                  lines+=1
                  if (@_rl_page_completions && lines >= @_rl_screenheight - 1)
                     lines = _rl_internal_pager(lines)
                     return if (lines < 0)
                  end
               else
                  @rl_outstream.write(' '*(max - printed_len))
               end
            end
            i += 1
         end
         rl_crlf()
      end
   end

   # Append any necessary closing quote and a separator character to the
   # just-inserted match.  If the user has specified that directories
   # should be marked by a trailing `/', append one of those instead.  The
   # default trailing character is a space.  Returns the number of characters
   # appended.  If NONTRIVIAL_MATCH is set, we test for a symlink (if the OS
   # has them) and don't add a suffix for a symlink to a directory.  A
   # nontrivial match is one that actually adds to the word being completed.
   # The variable rl_completion_mark_symlink_dirs controls this behavior
   # (it's initially set to the what the user has chosen, indicated by the
   # value of _rl_complete_mark_symlink_dirs, but may be modified by an
   # application's completion function).
   def append_to_match(text, delimiter, quote_char, nontrivial_match)
      temp_string = 0.chr * 4
      temp_string_index = 0
      if (quote_char && @rl_point>0 && !@rl_completion_suppress_quote &&
         @rl_line_buffer[@rl_point - 1,1] != quote_char)
         temp_string[temp_string_index] = quote_char
         temp_string_index += 1
      end
      if (delimiter != 0.chr)
         temp_string[temp_string_index] = delimiter
         temp_string_index += 1
      elsif (!@rl_completion_suppress_append && @rl_completion_append_character)
         temp_string[temp_string_index] = @rl_completion_append_character
         temp_string_index += 1
      end
      temp_string[temp_string_index] = 0.chr
      temp_string_index += 1

      if (@rl_filename_completion_desired)
         filename = File.expand_path(text)
         s = (nontrivial_match && !@rl_completion_mark_symlink_dirs) ?
         File.lstat(filename) : File.stat(filename)
         if s.directory?
            if @_rl_complete_mark_directories
               # This is clumsy.  Avoid putting in a double slash if point
               # is at the end of the line and the previous character is a
               # slash.
               if (@rl_point>0 && @rl_line_buffer[@rl_point,1] == 0.chr && @rl_line_buffer[@rl_point - 1,1] == '/' )

               elsif (@rl_line_buffer[@rl_point,1] != '/')
                  rl_insert_text('/')
               end
            end
            # Don't add anything if the filename is a symlink and resolves to a
            # directory.
         elsif s.symlink? && File.stat(filename).directory?

         else
            if (@rl_point == @rl_end && temp_string_index>0)
               rl_insert_text(temp_string)
            end
         end
         filename = nil
      else
         if (@rl_point == @rl_end && temp_string_index>0)
            rl_insert_text(temp_string)
         end
      end
      temp_string_index
   end

   # Stifle the history list, remembering only MAX number of lines.
   def stifle_history(max)
      max = 0 if (max < 0)

      if (@history_length > max)
         @the_history.slice!(0,(@history_length - max))
         @history_length = max
      end

      @history_stifled = true
      @max_input_history = @history_max_entries = max
   end

   # Stop stifling the history.  This returns the previous maximum
   #   number of history entries.  The value is positive if the history
   #   was stifled,  negative if it wasn't.
   def unstifle_history()
      if (@history_stifled)
         @history_stifled = false
         return (@history_max_entries)
      else
         return (-@history_max_entries)
      end
   end

   def history_is_stifled()
      return (@history_stifled)
   end

   def clear_history()
      @the_history = nil
      @history_offset = @history_length = 0
   end

   # Insert COUNT characters from STRING to the output stream at column COL.
   def insert_some_chars(string, count, col)
      if @hConsoleHandle
         _rl_output_some_chars(string,0,count)
      else
         # DEBUGGING
         if (@rl_byte_oriented)
            if (count != col)
               $stderr.write("readline: debug: insert_some_chars: count (#{count}) != col (#{col})\n");
            end
         end
         # If IC is defined, then we do not have to "enter" insert mode.
         #if (@_rl_term_IC)
         #   buffer = tgoto(@_rl_term_IC, 0, col)
         #   @_rl_out_stream.write(buffer)
         #   _rl_output_some_chars(string,0,count)
         #else
         # If we have to turn on insert-mode, then do so.
         if (@_rl_term_im)
            @_rl_out_stream.write(@_rl_term_im)
         end
         # If there is a special command for inserting characters, then
         # use that first to open up the space.
         if (@_rl_term_ic)
            @_rl_out_stream.write(@_rl_term_ic * count)
         end

         # Print the text.
         _rl_output_some_chars(string,0, count)

         # If there is a string to turn off insert mode, we had best use
         # it now.
         if (@_rl_term_ei)
            @_rl_out_stream.write(@_rl_term_ei)
         end
         #end
      end
   end

   # Delete COUNT characters from the display line.
   def delete_chars(count)
      return if (count > @_rl_screenwidth)   # XXX

      if @hConsoleHandle.nil?
         #if (@_rl_term_DC)
         #   buffer = tgoto(_rl_term_DC, count, count);
         #   @_rl_out_stream.write(buffer * count)
         #else
         if (@_rl_term_dc)
            @_rl_out_stream.write(@_rl_term_dc * count)
         end
         #end
      end
   end

   # adjust pointed byte and find mbstate of the point of string.
   #   adjusted point will be point <= adjusted_point, and returns
   #   differences of the byte(adjusted_point - point).
   #   if point is invalied (point < 0 || more than string length),
   #   it returns -1
   def _rl_adjust_point(string, point)

      length = string.length
      return -1 if (point < 0)
      return -1 if (length < point)

      pos = 0

      case @encoding
      when 'E'
         pos = string.scan(/./me).inject(0){|r,x| r<point ? r += x.length : r }
      when 'S'
         pos = string.scan(/./ms).inject(0){|r,x| r<point ? r += x.length : r }
      when 'U'
         pos = string.scan(/./mu).inject(0){|r,x| r<point ? r += x.length : r }
      when 'X'
         pos = string.dup.force_encoding(@encoding_name).chars.inject(0){|r,x| r<point ? r += x.bytesize : r }
      else
         pos = point
      end
      pos - point
   end

   # Find next `count' characters started byte point of the specified seed.
   #   If flags is MB_FIND_NONZERO, we look for non-zero-width multibyte
   #   characters.
   def _rl_find_next_mbchar(string, seed, count, flags)
      if @encoding == 'N'
         return (seed + count)
      end
      seed = 0 if seed < 0
      return seed if count <= 0

      point = seed + _rl_adjust_point(string,seed)
      if (seed < point)
         count -= 1
      end
      
      str = (flags == MB_FIND_NONZERO) ? string.sub(/\x00+$/,'') : string
      
      case @encoding
      when 'E'
         point += str[point..-1].scan(/./me)[0,count].to_s.length
      when 'S'
         point += str[point..-1].scan(/./ms)[0,count].to_s.length
      when 'U'
         point += str[point..-1].scan(/./mu)[0,count].to_s.length
      when 'X'
         point += str[point..-1].force_encoding(@encoding_name)[0,count].bytesize
      else
         point += count
         point = str.length if point >= str.length
      end
      point
   end

   # Find previous character started byte point of the specified seed.
   #   Returned point will be point <= seed.  If flags is MB_FIND_NONZERO,
   #   we look for non-zero-width multibyte characters.
   def _rl_find_prev_mbchar(string, seed, flags)
      if @encoding == 'N'
         return ((seed == 0) ? seed : seed - 1)
      end

      length = string.length
      if seed < 0
         return 0
      elsif length < seed
         return length
      end

      case @encoding
      when 'E'
         string[0,seed].scan(/./me)[0..-2].to_s.length
      when 'S'
         string[0,seed].scan(/./ms)[0..-2].to_s.length
      when 'U'
         string[0,seed].scan(/./mu)[0..-2].to_s.length
      when 'X'
         string[0,seed].force_encoding(@encoding_name)[0..-2].bytesize
      end
   end

   # compare the specified two characters. If the characters matched,
   #   return true. Otherwise return false.
   def _rl_compare_chars(buf1, pos1, buf2, pos2)
      return false if buf1[pos1].nil? || buf2[pos2].nil?
      case @encoding
      when 'E'
         buf1[pos1..-1].scan(/./me)[0] == buf2[pos2..-1].scan(/./me)[0]
      when 'S'
         buf1[pos1..-1].scan(/./ms)[0] == buf2[pos2..-1].scan(/./ms)[0]
      when 'U'
         buf1[pos1..-1].scan(/./mu)[0] == buf2[pos2..-1].scan(/./mu)[0]
      when 'X'
         buf1[pos1..-1].force_encoding(@encoding_name)[0] == buf2[pos2..-1].force_encoding(@encoding_name)[0]
      else
         buf1[pos1] == buf2[pos2]
      end
   end

   # return the number of bytes parsed from the multibyte sequence starting
   # at src, if a non-L'\0' wide character was recognized. It returns 0,
   # if a L'\0' wide character was recognized. It  returns (size_t)(-1),
   # if an invalid multibyte sequence was encountered. It returns (size_t)(-2)
   # if it couldn't parse a complete  multibyte character.
   def _rl_get_char_len(src)
      return 0 if src[0,1] == 0.chr || src.length==0
      case @encoding
      when 'E'
         len = src.scan(/./me)[0].to_s.length
      when 'S'
         len = src.scan(/./ms)[0].to_s.length
      when 'U'
         len = src.scan(/./mu)[0].to_s.length
      when 'X'
         src = src.dup.force_encoding(@encoding_name)
         len = src.valid_encoding? ? src[0].bytesize : 0
      else
         len = 1
      end
      len==0 ? -2 : len
   end

   # read multibyte char
   def _rl_read_mbchar(mbchar, size)
      mb_len = 0
      while (mb_len < size)
         rl_setstate(RL_STATE_MOREINPUT)
         mbchar << rl_read_key()
         mb_len += 1
         rl_unsetstate(RL_STATE_MOREINPUT)
         case @encoding
         when 'E'
            break unless mbchar.scan(/./me).empty?
         when 'S'
            break unless mbchar.scan(/./ms).empty?
         when 'U'
            break unless mbchar.scan(/./mu).empty?
         when 'X'
            break if mbchar.dup.force_encoding(@encoding_name).valid_encoding?
         end
      end
      mb_len
   end

   # Read a multibyte-character string whose first character is FIRST into
   #   the buffer MB of length MLEN.  Returns the last character read, which
   #   may be FIRST.  Used by the search functions, among others.  Very similar
   #   to _rl_read_mbchar.
   def _rl_read_mbstring(first, mb, mlen)
      c = first
      for i in 0 ... mlen
         mb << c
         if _rl_get_char_len(mb) == -2
            # Read more for multibyte character
            rl_setstate(RL_STATE_MOREINPUT)
            c = rl_read_key()
            rl_unsetstate(RL_STATE_MOREINPUT)
         else
            break
         end
      end
      c
   end

   def _rl_is_mbchar_matched(string, seed, _end, mbchar, length)
      return 0 if ((_end - seed) < length)

      for i in 0 ... length
         if (string[seed + i] != mbchar[i])
            return 0
         end
      end
      1
   end

   # Redraw the last line of a multi-line prompt that may possibly contain
   # terminal escape sequences.  Called with the cursor at column 0 of the
   # line to draw the prompt on.
   def redraw_prompt(t)
      oldp = @rl_display_prompt
      rl_save_prompt()

      @rl_display_prompt = t
      @local_prompt,@prompt_visible_length,@prompt_last_invisible,@prompt_invis_chars_first_line,@prompt_physical_chars =
      expand_prompt(t)
      @local_prompt_prefix = nil
      @local_prompt_len = @local_prompt ? @local_prompt.length : 0

      rl_forced_update_display()

      @rl_display_prompt = oldp
      rl_restore_prompt()
   end

   # Redisplay the current line after a SIGWINCH is received.
   def _rl_redisplay_after_sigwinch()
      # Clear the current line and put the cursor at column 0.  Make sure
      #   the right thing happens if we have wrapped to a new screen line.
      if @_rl_term_cr
         @rl_outstream.write(@_rl_term_cr)
         @_rl_last_c_pos = 0
         if @_rl_term_clreol
            @rl_outstream.write(@_rl_term_clreol)
         else
            space_to_eol(@_rl_screenwidth)
            @rl_outstream.write(@_rl_term_cr)
         end

         if @_rl_last_v_pos > 0
            _rl_move_vert(0)
         end
      else
         rl_crlf()
      end

      # Redraw only the last line of a multi-line prompt.
      t = @rl_display_prompt.index("\n")
      if t
         redraw_prompt(@rl_display_prompt[(t+1)..-1])
      else
         rl_forced_update_display()
      end
   end

   def rl_resize_terminal()
      if @readline_echoing_p
         _rl_get_screen_size(@rl_instream.fileno, 1)
         if @rl_redisplay_function != :rl_redisplay
            rl_forced_update_display()
         else
            _rl_redisplay_after_sigwinch()
         end
      end
   end

   def rl_sigwinch_handler(sig)
      rl_setstate(RL_STATE_SIGHANDLER)
      rl_resize_terminal()
      rl_unsetstate(RL_STATE_SIGHANDLER)
   end



   module_function :rl_attempted_completion_function,:rl_deprep_term_function,
   :rl_event_hook,:rl_attempted_completion_over,:rl_basic_quote_characters,
   :rl_basic_word_break_characters,:rl_completer_quote_characters,
   :rl_completer_word_break_characters,:rl_completion_append_character,
   :rl_filename_quote_characters,:rl_instream,:rl_library_version,:rl_outstream,
   :rl_readline_name,
   :rl_attempted_completion_function=,:rl_deprep_term_function=,
   :rl_event_hook=,:rl_attempted_completion_over=,:rl_basic_quote_characters=,
   :rl_basic_word_break_characters=,:rl_completer_quote_characters=,
   :rl_completer_word_break_characters=,:rl_completion_append_character=,
   :rl_filename_quote_characters=,:rl_instream=,:rl_library_version=,:rl_outstream=,
   :rl_readline_name=,:history_length,:history_base

  def no_terminal?
    term = ENV["TERM"]
    term.nil? || (term == 'dumb') || (RUBY_PLATFORM =~ /mswin|mingw/)
  end
  private :no_terminal?

end
