Skip to content

Commit c670bb7

Browse files
committed
Land rapid7#4337 - Fix prompt coloring on Windows
2 parents 5f2de2e + 5f73027 commit c670bb7

File tree

2 files changed

+102
-15
lines changed

2 files changed

+102
-15
lines changed

lib/rex/ui/text/input/readline.rb

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def pgets()
8383
Thread.current.priority = -20
8484

8585
output.prompting
86-
line = ::Readline.readline(prompt, true)
86+
line = readline_with_output(prompt, true)
8787
::Readline::HISTORY.pop if (line and line.empty?)
8888
ensure
8989
Thread.current.priority = orig || 0
@@ -116,6 +116,37 @@ def intrinsic_shell?
116116
#
117117
attr_accessor :output
118118

119+
private
120+
121+
def readline_with_output(prompt, add_history=false)
122+
# rb-readlines's Readline.readline hardcodes the input and output to $stdin and $stdout, which means setting
123+
# `Readline.input` or `Readline.ouput` has no effect when running `Readline.readline` with rb-readline, so need
124+
# to reimplement []`Readline.readline`](https://github.com/luislavena/rb-readline/blob/ce4908dae45dbcae90a6e42e3710b8c3a1f2cd64/lib/readline.rb#L36-L58)
125+
# for rb-readline to support setting input and output. Output needs to be set so that colorization works for the
126+
# prompt on Windows.
127+
if defined? RbReadline
128+
RbReadline.rl_instream = fd
129+
RbReadline.rl_outstream = output
130+
131+
begin
132+
line = RbReadline.readline(prompt)
133+
rescue ::Exception => exception
134+
RbReadline.rl_cleanup_after_signal()
135+
RbReadline.rl_deprep_terminal()
136+
137+
raise exception
138+
end
139+
140+
if add_history && line
141+
RbReadline.add_history(line)
142+
end
143+
144+
line.try(:dup)
145+
else
146+
::Readline.readline(prompt, true)
147+
end
148+
end
149+
119150
end
120151
rescue LoadError
121152
end

lib/rex/ui/text/output/stdio.rb

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,76 @@ module Text
1616
#
1717
###
1818
class Output::Stdio < Rex::Ui::Text::Output
19+
#
20+
# Attributes
21+
#
22+
23+
# @!attribute io
24+
# The raw `IO` backing this Text output. Defaults to `$stdout`
25+
#
26+
# @return [#flush, #puts, #write]
27+
attr_writer :io
28+
29+
#
30+
# Constructor
31+
#
32+
33+
# @param options [Hash{Symbol => IO}]
34+
# @option options [IO]
35+
def initialize(options={})
36+
options.assert_valid_keys(:io)
37+
38+
super()
39+
40+
self.io = options[:io]
41+
end
42+
43+
#
44+
# Methods
45+
#
46+
47+
def flush
48+
io.flush
49+
end
50+
51+
# IO to write to.
52+
#
53+
# @return [IO] Default to `$stdout`
54+
def io
55+
@io ||= $stdout
56+
end
57+
58+
#
59+
# Prints the supplied message to standard output.
60+
#
61+
def print_raw(msg = '')
62+
if (Rex::Compat.is_windows and supports_color?)
63+
WindowsConsoleColorSupport.new(io).write(msg)
64+
else
65+
io.print(msg)
66+
end
67+
68+
io.flush
69+
70+
msg
71+
end
72+
alias_method :write, :print_raw
73+
74+
def puts(*args)
75+
args.each do |argument|
76+
line = argument.to_s
77+
write(line)
78+
79+
unless line.ends_with? "\n"
80+
# yes, this is output, but `IO#puts` uses `rb_default_rs`, which is
81+
# [`$/`](https://github.com/ruby/ruby/blob/3af8e150aded9d162bfd41426aaaae0279e5a653/io.c#L12168-L12172),
82+
# which is [`$INPUT_RECORD_SEPARATOR`](https://github.com/ruby/ruby/blob/3af8e150aded9d162bfd41426aaaae0279e5a653/lib/English.rb#L83)
83+
write($INPUT_RECORD_SEPARATOR)
84+
end
85+
end
86+
87+
nil
88+
end
1989

2090
def supports_color?
2191
case config[:color]
@@ -31,20 +101,6 @@ def supports_color?
31101
return (term and term.match(/(?:vt10[03]|xterm(?:-color)?|linux|screen|rxvt)/i) != nil)
32102
end
33103
end
34-
35-
#
36-
# Prints the supplied message to standard output.
37-
#
38-
def print_raw(msg = '')
39-
if (Rex::Compat.is_windows and supports_color?)
40-
WindowsConsoleColorSupport.new($stdout).write(msg)
41-
else
42-
$stdout.print(msg)
43-
end
44-
$stdout.flush
45-
46-
msg
47-
end
48104
end
49105

50106
end

0 commit comments

Comments
 (0)