metasploit-framework/lib/rex/ui/text/input/readline.rb

192 lines
3.5 KiB
Ruby

require 'rex/ui'
module Rex
module Ui
module Text
begin
require 'readline'
###
#
# This class implements standard input using readline against
# standard input. It supports tab completion.
#
###
class Input::Readline < Rex::Ui::Text::Input
include ::Readline
@@rl_thread = nil
@@rl_pipes = nil
@@rl_prompt = ''
@@rl_history = true
#
# Enable readline mode by setting rl_usestdio to false
# Address the following items before switching:
#
# XXX: Find a way to emulate ^Z when in readline mode
# XXX: Create histories/tab completion for shells
# XXX: Capture remote shell prompt and use for readline
#
@@rl_usestdio = true
#
# Initializes the readline-aware Input instance for text.
#
def initialize(tab_complete_proc = nil)
if (tab_complete_proc)
::Readline.basic_word_break_characters = "\x00"
::Readline.completion_proc = tab_complete_proc
end
end
#
# Whether or not the input medium supports readline.
#
def supports_readline
true
end
#
# Start the readline thread
#
def readline_start
return if @@rl_usestdio
return if @@rl_thread
@@rl_pipes = Rex::Compat.pipe
@@rl_thread = ::Thread.new do
begin
while (line = ::Readline.readline(@@rl_prompt, @@rl_history))
@@rl_pipes[1].write(line+"\n")
@@rl_pipes[1].flush
end
rescue ::Exception => e
$stderr.puts "ERROR: readline thread: #{e.to_s} #{e.backtrace.to_s}"
end
end
end
#
# Stop the readline thread
#
def readline_stop
# Stop the reader thread
if (@@rl_thread)
begin
@@rl_thread.kill
rescue ::Exception
end
@@rl_thread = nil
end
# Close the pipes
if (@@rl_pipes)
begin
@rl_pipes[0].close
@rl_pipes[1].close
rescue ::Exception
end
@@rl_pipes = nil
end
end
#
# Status of the readline thread
#
def readline_status
@@rl_thread ? true : false
end
#
# Calls sysread on the standard input handle.
#
def sysread(len = 1)
return $stdin.sysread(len) if @@rl_usestdio
if (! readline_status)
$stderr.puts "ERROR: sysread() called outside of thread mode: " + caller(1).to_s
return ''
end
@@rl_pipes[0].sysread(len)
end
#
# Fake gets using readline
#
def gets()
return $stdin.gets() if @@rl_usestdio
if (! readline_status)
$stderr.puts "ERROR: gets() called outside of thread mode: " + caller(1).to_s
return ''
end
@@rl_pipes[0].gets
end
#
# Print a prompt and flush standard output.
#
def prompt(prompt)
_print_prompt(prompt)
return gets()
end
#
# Prompt-based getline using readline.
#
def pgets
if (readline_status)
$stderr.puts "ERROR: pgets called inside of thread mode: " + caller(1).to_s
return ''
end
if ((line = ::Readline.readline(prompt, true)))
HISTORY.pop if (line.empty?)
return line + "\n"
else
eof = true
return line
end
end
#
# Returns the output pipe handle
#
def fd
return $stdin if @@rl_usestdio
if (! readline_status)
$stderr.puts "fd called outside of thread mode: " + caller(1).to_s
return ''
end
@@rl_pipes[0]
end
#
# Indicates that this input medium as a shell builtin, no need
# to extend.
#
def intrinsic_shell?
true
end
#
# The prompt that is to be displayed.
#
attr_accessor :prompt
#
# The output handle to use when displaying the prompt.
#
attr_accessor :output
end
rescue LoadError
end
end
end
end