Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/ui/text/input/readline.rb
Views: 11704
# -*- coding: binary -*-12module Rex3module Ui4module Text56begin78###9#10# This class implements standard input using readline against11# standard input. It supports tab completion.12#13###14class Input::Readline < Rex::Ui::Text::Input1516#17# Initializes the readline-aware Input instance for text.18#19def initialize(tab_complete_proc = nil)20if(not Object.const_defined?('Readline'))21require 'readline'22end2324self.extend(::Readline)2526if tab_complete_proc27::Readline.basic_word_break_characters = ""28@rl_saved_proc = with_error_handling(tab_complete_proc)29::Readline.completion_proc = @rl_saved_proc30end31end3233#34# Reattach the original completion proc35#36def reset_tab_completion(tab_complete_proc = nil)37::Readline.basic_word_break_characters = "\x00"38::Readline.completion_proc = tab_complete_proc ? with_error_handling(tab_complete_proc) : @rl_saved_proc39end404142#43# Retrieve the line buffer44#45def line_buffer46if defined? RbReadline47RbReadline.rl_line_buffer48else49::Readline.line_buffer50end51end5253attr_accessor :prompt5455#56# Whether or not the input medium supports readline.57#58def supports_readline59true60end6162#63# Calls sysread on the standard input handle.64#65def sysread(len = 1)66begin67self.fd.sysread(len)68rescue ::Errno::EINTR69retry70end71end7273#74# Read a line from stdin75#76def gets()77begin78self.fd.gets()79rescue ::Errno::EINTR80retry81end82end8384#85# Stick readline into a low-priority thread so that the scheduler doesn't slow86# down other background threads. This is important when there are many active87# background jobs, such as when the user is running Karmetasploit88#89def pgets9091line = nil92orig = Thread.current.priority9394begin95Thread.current.priority = -209697output.prompting98line = readline_with_output(prompt, true)99::Readline::HISTORY.pop if (line and line.empty?)100ensure101Thread.current.priority = orig || 0102end103104line105end106107#108# Returns the output pipe handle109#110def fd111$stdin112end113114#115# Indicates that this input medium as a shell builtin, no need116# to extend.117#118def intrinsic_shell?119true120end121122#123# The prompt that is to be displayed.124#125attr_accessor :prompt126#127# The output handle to use when displaying the prompt.128#129attr_accessor :output130131private132133def readline_with_output(prompt, add_history=false)134# rb-readlines's Readline.readline hardcodes the input and output to135# $stdin and $stdout, which means setting `Readline.input` or136# `Readline.ouput` has no effect when running `Readline.readline` with137# rb-readline, so need to reimplement138# []`Readline.readline`](https://github.com/luislavena/rb-readline/blob/ce4908dae45dbcae90a6e42e3710b8c3a1f2cd64/lib/readline.rb#L36-L58)139# for rb-readline to support setting input and output. Output needs to140# be set so that colorization works for the prompt on Windows.141self.prompt = prompt142143# TODO: there are unhandled quirks in async output buffering that144# we have not solved yet, for instance when loading meterpreter145# extensions, supporting Windows, printing output from commands, etc.146# Remove this guard when issues are resolved.147=begin148reset_sequence = "\n\001\r\033[K\002"149if (/mingw/ =~ RUBY_PLATFORM)150reset_sequence = ""151end152=end153reset_sequence = ""154155if defined? RbReadline156RbReadline.rl_instream = fd157RbReadline.rl_outstream = output158159begin160line = RbReadline.readline(reset_sequence + prompt)161rescue ::Exception => exception162RbReadline.rl_cleanup_after_signal()163RbReadline.rl_deprep_terminal()164165raise exception166end167168if add_history && line && !line.start_with?(' ')169# Don't add duplicate lines to history170if ::Readline::HISTORY.empty? || line.strip != ::Readline::HISTORY[-1]171RbReadline.add_history(line.strip)172end173end174175line.try(:dup)176else177# The line that's read is immediately added to history178line = ::Readline.readline(reset_sequence + prompt, true)179180# Don't add duplicate lines to history181if ::Readline::HISTORY.length > 1 && line == ::Readline::HISTORY[-2]182::Readline::HISTORY.pop183end184185line186end187end188189private190191def with_error_handling(proc)192proc do |*args|193proc.call(*args)194rescue StandardError => e195elog("tab_complete_proc has failed with args #{args}", error: e)196[]197end198end199200end201rescue LoadError202end203204end205end206end207208209