CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/ui/text/input/readline.rb
Views: 11704
1
# -*- coding: binary -*-
2
3
module Rex
4
module Ui
5
module Text
6
7
begin
8
9
###
10
#
11
# This class implements standard input using readline against
12
# standard input. It supports tab completion.
13
#
14
###
15
class Input::Readline < Rex::Ui::Text::Input
16
17
#
18
# Initializes the readline-aware Input instance for text.
19
#
20
def initialize(tab_complete_proc = nil)
21
if(not Object.const_defined?('Readline'))
22
require 'readline'
23
end
24
25
self.extend(::Readline)
26
27
if tab_complete_proc
28
::Readline.basic_word_break_characters = ""
29
@rl_saved_proc = with_error_handling(tab_complete_proc)
30
::Readline.completion_proc = @rl_saved_proc
31
end
32
end
33
34
#
35
# Reattach the original completion proc
36
#
37
def reset_tab_completion(tab_complete_proc = nil)
38
::Readline.basic_word_break_characters = "\x00"
39
::Readline.completion_proc = tab_complete_proc ? with_error_handling(tab_complete_proc) : @rl_saved_proc
40
end
41
42
43
#
44
# Retrieve the line buffer
45
#
46
def line_buffer
47
if defined? RbReadline
48
RbReadline.rl_line_buffer
49
else
50
::Readline.line_buffer
51
end
52
end
53
54
attr_accessor :prompt
55
56
#
57
# Whether or not the input medium supports readline.
58
#
59
def supports_readline
60
true
61
end
62
63
#
64
# Calls sysread on the standard input handle.
65
#
66
def sysread(len = 1)
67
begin
68
self.fd.sysread(len)
69
rescue ::Errno::EINTR
70
retry
71
end
72
end
73
74
#
75
# Read a line from stdin
76
#
77
def gets()
78
begin
79
self.fd.gets()
80
rescue ::Errno::EINTR
81
retry
82
end
83
end
84
85
#
86
# Stick readline into a low-priority thread so that the scheduler doesn't slow
87
# down other background threads. This is important when there are many active
88
# background jobs, such as when the user is running Karmetasploit
89
#
90
def pgets
91
92
line = nil
93
orig = Thread.current.priority
94
95
begin
96
Thread.current.priority = -20
97
98
output.prompting
99
line = readline_with_output(prompt, true)
100
::Readline::HISTORY.pop if (line and line.empty?)
101
ensure
102
Thread.current.priority = orig || 0
103
end
104
105
line
106
end
107
108
#
109
# Returns the output pipe handle
110
#
111
def fd
112
$stdin
113
end
114
115
#
116
# Indicates that this input medium as a shell builtin, no need
117
# to extend.
118
#
119
def intrinsic_shell?
120
true
121
end
122
123
#
124
# The prompt that is to be displayed.
125
#
126
attr_accessor :prompt
127
#
128
# The output handle to use when displaying the prompt.
129
#
130
attr_accessor :output
131
132
private
133
134
def readline_with_output(prompt, add_history=false)
135
# rb-readlines's Readline.readline hardcodes the input and output to
136
# $stdin and $stdout, which means setting `Readline.input` or
137
# `Readline.ouput` has no effect when running `Readline.readline` with
138
# rb-readline, so need to reimplement
139
# []`Readline.readline`](https://github.com/luislavena/rb-readline/blob/ce4908dae45dbcae90a6e42e3710b8c3a1f2cd64/lib/readline.rb#L36-L58)
140
# for rb-readline to support setting input and output. Output needs to
141
# be set so that colorization works for the prompt on Windows.
142
self.prompt = prompt
143
144
# TODO: there are unhandled quirks in async output buffering that
145
# we have not solved yet, for instance when loading meterpreter
146
# extensions, supporting Windows, printing output from commands, etc.
147
# Remove this guard when issues are resolved.
148
=begin
149
reset_sequence = "\n\001\r\033[K\002"
150
if (/mingw/ =~ RUBY_PLATFORM)
151
reset_sequence = ""
152
end
153
=end
154
reset_sequence = ""
155
156
if defined? RbReadline
157
RbReadline.rl_instream = fd
158
RbReadline.rl_outstream = output
159
160
begin
161
line = RbReadline.readline(reset_sequence + prompt)
162
rescue ::Exception => exception
163
RbReadline.rl_cleanup_after_signal()
164
RbReadline.rl_deprep_terminal()
165
166
raise exception
167
end
168
169
if add_history && line && !line.start_with?(' ')
170
# Don't add duplicate lines to history
171
if ::Readline::HISTORY.empty? || line.strip != ::Readline::HISTORY[-1]
172
RbReadline.add_history(line.strip)
173
end
174
end
175
176
line.try(:dup)
177
else
178
# The line that's read is immediately added to history
179
line = ::Readline.readline(reset_sequence + prompt, true)
180
181
# Don't add duplicate lines to history
182
if ::Readline::HISTORY.length > 1 && line == ::Readline::HISTORY[-2]
183
::Readline::HISTORY.pop
184
end
185
186
line
187
end
188
end
189
190
private
191
192
def with_error_handling(proc)
193
proc do |*args|
194
proc.call(*args)
195
rescue StandardError => e
196
elog("tab_complete_proc has failed with args #{args}", error: e)
197
[]
198
end
199
end
200
201
end
202
rescue LoadError
203
end
204
205
end
206
end
207
end
208
209