ruby-debug 0.10.3 → 0.10.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +15 -0
- data/ChangeLog +461 -104
- data/Rakefile +69 -13
- data/VERSION +3 -0
- data/bin/rdebug +2 -1
- data/cli/ruby-debug.rb +8 -5
- data/cli/ruby-debug/command.rb +43 -4
- data/cli/ruby-debug/commands/breakpoints.rb +3 -1
- data/cli/ruby-debug/commands/catchpoint.rb +5 -3
- data/cli/ruby-debug/commands/{continue.RB.save → continue.RB} +0 -0
- data/cli/ruby-debug/commands/eval.rb +4 -1
- data/cli/ruby-debug/commands/frame.rb +11 -10
- data/cli/ruby-debug/commands/help.rb +5 -0
- data/cli/ruby-debug/commands/info.rb +6 -3
- data/cli/ruby-debug/commands/irb.rb +115 -12
- data/cli/ruby-debug/commands/kill.rb +50 -0
- data/cli/ruby-debug/commands/list.rb +3 -3
- data/cli/ruby-debug/commands/quit.rb +12 -6
- data/cli/ruby-debug/commands/raise.RB +41 -0
- data/cli/ruby-debug/commands/show.rb +26 -28
- data/cli/ruby-debug/helper.rb +5 -0
- data/cli/ruby-debug/interface.rb +53 -25
- data/cli/ruby-debug/processor.RB +484 -0
- data/cli/ruby-debug/processor.rb +140 -56
- data/rdbg.rb +0 -0
- data/runner.sh +7 -0
- data/test/base/base.rb +0 -0
- data/test/base/binding.rb +0 -0
- data/test/base/catchpoint.rb +0 -0
- data/test/base/reload_bug.rb +8 -0
- data/test/brkpt-class-bug.rb +8 -0
- data/test/cli/commands/unit/regexp.rb +11 -0
- data/test/config.yaml +8 -0
- data/test/data/annotate.cmd +1 -1
- data/test/data/annotate.right +3 -3
- data/test/data/break_bad.cmd +1 -1
- data/test/data/break_bad.right +1 -1
- data/test/data/break_loop_bug.right +1 -1
- data/test/data/breakpoints.cmd +1 -1
- data/test/data/breakpoints.right +3 -3
- data/test/data/brkpt-class-bug.cmd +9 -0
- data/test/data/brkpt-class-bug.right +18 -0
- data/test/data/catch.right +2 -0
- data/test/data/condition.cmd +1 -0
- data/test/data/condition.right +5 -3
- data/test/data/ctrl.right +2 -2
- data/test/data/display.right +1 -1
- data/test/data/emacs_basic.right +2 -2
- data/test/data/except-bug1.cmd +7 -0
- data/test/data/except-bug1.right +13 -0
- data/test/data/file-with-space.cmd +7 -0
- data/test/data/file-with-space.right +9 -0
- data/test/data/finish.right +1 -1
- data/test/data/frame.cmd +7 -1
- data/test/data/frame.right +12 -1
- data/test/data/info-var.right +1 -1
- data/test/data/info.cmd +1 -0
- data/test/data/info.right +21 -2
- data/test/data/list.right +1 -1
- data/test/data/method.right +1 -1
- data/test/data/post-mortem.right +5 -4
- data/test/data/save.right +1 -1
- data/test/data/setshow.cmd +0 -10
- data/test/data/setshow.right +0 -17
- data/test/dollar-0.rb +0 -0
- data/test/except-bug1.rb +4 -0
- data/test/file with space.rb +1 -0
- data/test/gcd-dbg.rb +0 -0
- data/test/helper.rb +7 -1
- data/test/pm-base.rb +0 -0
- data/test/pm.rb +1 -1
- data/test/raise.rb +0 -0
- data/test/rdebug-save.1 +7 -0
- data/test/tdebug.rb +2 -2
- data/test/test-annotate.rb +0 -0
- data/test/test-break-bad.rb +0 -0
- data/test/test-breakpoints.rb +0 -0
- data/test/test-brkpt-class-bug.rb +26 -0
- data/test/test-catch.rb +1 -1
- data/test/test-condition.rb +1 -1
- data/test/test-ctrl.rb +1 -0
- data/test/test-display.rb +0 -0
- data/test/test-dollar-0.rb +0 -0
- data/test/test-edit.rb +0 -0
- data/test/test-emacs-basic.rb +0 -0
- data/test/test-enable.rb +0 -0
- data/test/test-except-bug1.rb +31 -0
- data/test/test-file-with-space.rb +30 -0
- data/test/test-finish.rb +0 -0
- data/test/test-frame.rb +0 -0
- data/test/test-help.rb +0 -0
- data/test/test-hist.rb +0 -0
- data/test/test-info-thread.rb +0 -0
- data/test/test-info-var.rb +0 -0
- data/test/test-info.rb +0 -0
- data/test/test-init.rb +7 -1
- data/test/test-list.rb +0 -0
- data/test/test-method.rb +0 -0
- data/test/test-output.rb +0 -0
- data/test/test-pm.rb +0 -0
- data/test/test-quit.rb +0 -0
- data/test/test-raise.rb +1 -1
- data/test/test-save.rb +7 -1
- data/test/test-setshow.rb +0 -0
- data/test/test-source.rb +0 -0
- data/test/test-stepping.rb +0 -0
- data/test/test-trace.rb +0 -0
- metadata +211 -180
@@ -4,10 +4,13 @@ module Debugger
|
|
4
4
|
class HelpCommand < Command
|
5
5
|
self.allow_in_control = true
|
6
6
|
|
7
|
+
# An input line is matched against this regular expression. If we have
|
8
|
+
# a match, run this command.
|
7
9
|
def regexp
|
8
10
|
/^\s* h(?:elp)? (?:\s+(.+))? $/x
|
9
11
|
end
|
10
12
|
|
13
|
+
# The code that implements this command.
|
11
14
|
def execute
|
12
15
|
if @match[1]
|
13
16
|
args = @match[1].split
|
@@ -41,10 +44,12 @@ module Debugger
|
|
41
44
|
end
|
42
45
|
|
43
46
|
class << self
|
47
|
+
# The command name listed via 'help'
|
44
48
|
def help_command
|
45
49
|
'help'
|
46
50
|
end
|
47
51
|
|
52
|
+
# Returns a String given the help description of this command
|
48
53
|
def help(cmd)
|
49
54
|
%{
|
50
55
|
h[elp]\t\tprint this help
|
@@ -91,7 +91,7 @@ item. If \'verbose\' is given then the entire stack frame is shown.'],
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def execute
|
94
|
-
if @match[1].empty?
|
94
|
+
if !@match[1] || @match[1].empty?
|
95
95
|
errmsg "\"info\" must be followed by the name of an info command:\n"
|
96
96
|
print "List of info subcommands:\n\n"
|
97
97
|
for subcmd in Subcommands do
|
@@ -142,12 +142,15 @@ item. If \'verbose\' is given then the entire stack frame is shown.'],
|
|
142
142
|
end
|
143
143
|
print "Num Enb What\n"
|
144
144
|
brkpts.each do |b|
|
145
|
+
fname = Command.settings[:basename] ?
|
146
|
+
File.basename(b.source) : b.source
|
147
|
+
|
145
148
|
if b.expr.nil?
|
146
149
|
print "%3d %s at %s:%s\n",
|
147
|
-
b.id, (b.enabled? ? 'y' : 'n'),
|
150
|
+
b.id, (b.enabled? ? 'y' : 'n'), fname, b.pos
|
148
151
|
else
|
149
152
|
print "%3d %s at %s:%s if %s\n",
|
150
|
-
b.id, (b.enabled? ? 'y' : 'n'),
|
153
|
+
b.id, (b.enabled? ? 'y' : 'n'), fname, b.pos, b.expr
|
151
154
|
end
|
152
155
|
hits = b.hit_count
|
153
156
|
if hits > 0
|
@@ -2,20 +2,78 @@ require 'irb'
|
|
2
2
|
|
3
3
|
module IRB # :nodoc:
|
4
4
|
module ExtendCommand # :nodoc:
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
|
6
|
+
# FIXME: should we read these out of a directory to
|
7
|
+
# make this more user-customizable?
|
8
|
+
# A base command class that resume execution
|
9
|
+
class DebuggerResumeCommand
|
10
|
+
def self.execute(conf, *opts)
|
11
|
+
name =
|
12
|
+
if self.name =~ /IRB::ExtendCommand::(\S+)/
|
13
|
+
$1.downcase
|
14
|
+
else
|
15
|
+
'unknown'
|
16
|
+
end
|
17
|
+
$rdebug_args = opts
|
18
|
+
$rdebug_command =
|
19
|
+
if $rdebug_irb_statements
|
20
|
+
$rdebug_irb_statements
|
21
|
+
else
|
22
|
+
([name] + opts).join(' ')
|
23
|
+
end
|
24
|
+
|
25
|
+
throw :IRB_EXIT, name.to_sym
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Continue < DebuggerResumeCommand ; end
|
30
|
+
class Next < DebuggerResumeCommand ; end
|
31
|
+
class Quit < DebuggerResumeCommand ; end
|
32
|
+
class Step < DebuggerResumeCommand ; end
|
33
|
+
|
34
|
+
# Issues a comamnd to the debugger without continuing
|
35
|
+
# execution.
|
36
|
+
class Dbgr
|
37
|
+
def self.execute(conf, *opts)
|
38
|
+
command =
|
39
|
+
if opts.size == 1 && opts[0].is_a?(String)
|
40
|
+
args = opts[0]
|
41
|
+
else
|
42
|
+
opts.join(' ')
|
43
|
+
end
|
44
|
+
if $rdebug_state && $rdebug_state.processor
|
45
|
+
processor = $rdebug_state.processor
|
46
|
+
processor.one_cmd($rdebug_state.processor.commands,
|
47
|
+
$rdebug_state.context,
|
48
|
+
command)
|
49
|
+
end
|
8
50
|
end
|
9
51
|
end
|
52
|
+
|
53
|
+
end
|
54
|
+
if defined?(ExtendCommandBundle)
|
55
|
+
[['cont', :Continue],
|
56
|
+
['dbgr', :Dbgr],
|
57
|
+
['n', :Next],
|
58
|
+
['step', :Step],
|
59
|
+
['q', :Quit]].each do |command, sym|
|
60
|
+
ExtendCommandBundle.def_extend_command command, sym
|
61
|
+
end
|
10
62
|
end
|
11
|
-
ExtendCommandBundle.def_extend_command "cont", :Continue
|
12
63
|
|
13
64
|
def self.start_session(binding)
|
14
65
|
unless @__initialized
|
15
|
-
args = ARGV
|
16
|
-
ARGV.replace(
|
66
|
+
args = ARGV.dup
|
67
|
+
ARGV.replace([])
|
17
68
|
IRB.setup(nil)
|
18
69
|
ARGV.replace(args)
|
70
|
+
|
71
|
+
# If the user has a IRB profile, run that now.
|
72
|
+
if ENV['RDEBUG_IRB']
|
73
|
+
ENV['IRBRC'] = ENV['RDEBUG_IRB']
|
74
|
+
@CONF[:RC_NAME_GENERATOR]=nil
|
75
|
+
IRB.run_config
|
76
|
+
end
|
19
77
|
@__initialized = true
|
20
78
|
end
|
21
79
|
|
@@ -32,6 +90,18 @@ module IRB # :nodoc:
|
|
32
90
|
end
|
33
91
|
end
|
34
92
|
|
93
|
+
# Monkeypatch to save the current IRB statement to be run and make the instruction sequence
|
94
|
+
# "filename" unique. Possibly not needed.
|
95
|
+
class IRB::Context
|
96
|
+
def evaluate(line, line_no)
|
97
|
+
$rdebug_irb_statements = line
|
98
|
+
@line_no = line_no
|
99
|
+
set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
|
100
|
+
# @workspace.evaluate("_ = IRB.conf[:MAIN_CONTEXT]._")
|
101
|
+
# @_ = @workspace.evaluate(line, irb_path, line_no)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
35
105
|
module Debugger
|
36
106
|
|
37
107
|
# Implements debugger "irb" command.
|
@@ -45,7 +115,9 @@ module Debugger
|
|
45
115
|
end
|
46
116
|
|
47
117
|
def regexp
|
48
|
-
|
118
|
+
/^\s* irb
|
119
|
+
(?:\s+(-d))?
|
120
|
+
\s*$/x
|
49
121
|
end
|
50
122
|
|
51
123
|
def execute
|
@@ -55,13 +127,30 @@ module Debugger
|
|
55
127
|
end
|
56
128
|
|
57
129
|
save_trap = trap("SIGINT") do
|
58
|
-
throw :IRB_EXIT, :cont if $
|
130
|
+
throw :IRB_EXIT, :cont if $rdebug_in_irb
|
59
131
|
end
|
60
132
|
|
61
|
-
|
133
|
+
# add_debugging = @match.is_a?(Array) && '-d' == @match[1]
|
134
|
+
$rdebug_state = @state
|
135
|
+
$rdebug_in_irb = true
|
62
136
|
cont = IRB.start_session(get_binding)
|
63
|
-
|
137
|
+
case cont
|
138
|
+
when :cont
|
139
|
+
@state.proceed
|
140
|
+
when :step
|
141
|
+
force = Command.settings[:force_stepping]
|
142
|
+
@state.context.step(1, force)
|
64
143
|
@state.proceed
|
144
|
+
when :next
|
145
|
+
force = Command.settings[:force_stepping]
|
146
|
+
@state.context.step_over(1, @state.frame_pos, force)
|
147
|
+
@state.proceed
|
148
|
+
when :quit
|
149
|
+
# FIXME: DRY with code from file/command quit.
|
150
|
+
if confirm("Really quit? (y/n) ")
|
151
|
+
@state.interface.finalize
|
152
|
+
exit! # exit -> exit!: No graceful way to stop threads...
|
153
|
+
end
|
65
154
|
else
|
66
155
|
file = @state.context.frame_file(0)
|
67
156
|
line = @state.context.frame_line(0)
|
@@ -70,7 +159,8 @@ module Debugger
|
|
70
159
|
end
|
71
160
|
|
72
161
|
ensure
|
73
|
-
$
|
162
|
+
$rdebug_in_irb = nil
|
163
|
+
$rdebug_state = nil
|
74
164
|
trap("SIGINT", save_trap) if save_trap
|
75
165
|
end
|
76
166
|
|
@@ -81,7 +171,20 @@ module Debugger
|
|
81
171
|
|
82
172
|
def help(cmd)
|
83
173
|
%{
|
84
|
-
irb\tstarts an Interactive Ruby (IRB) session.
|
174
|
+
irb [-d]\tstarts an Interactive Ruby (IRB) session.
|
175
|
+
|
176
|
+
If -d is added you can get access to debugger state via the global variable
|
177
|
+
$rdebug_state.
|
178
|
+
|
179
|
+
irb is extended with methods "cont", "n", "step" and "q" which run the
|
180
|
+
corresponding debugger commands. In contrast to the real debugger
|
181
|
+
commands these commands do not allow command arguments.
|
182
|
+
|
183
|
+
However to run any arbitrary rdebug command which does not involve
|
184
|
+
execution of the debugged program (like the above "step" "cont", etc.)
|
185
|
+
use method "dbgr" and give an array of string parameters. For example:
|
186
|
+
|
187
|
+
dbgr ['list', '10'] # same as "list 10" inside debugger
|
85
188
|
}
|
86
189
|
end
|
87
190
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
# Implements debugger "kill" command
|
4
|
+
class KillCommand < Command
|
5
|
+
self.allow_in_control = true
|
6
|
+
|
7
|
+
def regexp
|
8
|
+
/ ^\s*
|
9
|
+
(?:kill) \s*
|
10
|
+
(?:\s+(\S+))?\s*
|
11
|
+
$
|
12
|
+
/ix
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute
|
16
|
+
if @match[1]
|
17
|
+
signame = @match[1]
|
18
|
+
unless Signal.list.member?(signame)
|
19
|
+
errmsg("signal name #{signame} is not a signal I know about\n")
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
if 'KILL' == signame
|
23
|
+
@state.interface.finalize
|
24
|
+
end
|
25
|
+
else
|
26
|
+
if not confirm("Really kill? (y/n) ")
|
27
|
+
return
|
28
|
+
else
|
29
|
+
signame = 'KILL'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
Process.kill(signame, Process.pid)
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
def help_command
|
37
|
+
%w[kill]
|
38
|
+
end
|
39
|
+
|
40
|
+
def help(cmd)
|
41
|
+
%{
|
42
|
+
kill [SIGNAL]
|
43
|
+
|
44
|
+
Send [signal] to Process.pid
|
45
|
+
Equivalent of Process.kill(Process.pid)
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -69,12 +69,12 @@ module Debugger
|
|
69
69
|
# If we can show from B to E then we return B, otherwise we return the
|
70
70
|
# previous line @state.previous_line.
|
71
71
|
def display_list(b, e, file, current)
|
72
|
-
print "[%d, %d] in %s\n", b, e, file
|
73
72
|
lines = LineCache::getlines(file,
|
74
73
|
Command.settings[:reload_source_on_change])
|
75
74
|
if lines
|
76
|
-
|
75
|
+
b = lines.size - (e - b) if b >= lines.size
|
77
76
|
e = lines.size if lines.size < e
|
77
|
+
print "[%d, %d] in %s\n", b, e, file
|
78
78
|
[b, 1].max.upto(e) do |n|
|
79
79
|
if n > 0 && lines[n-1]
|
80
80
|
if n == current
|
@@ -88,7 +88,7 @@ module Debugger
|
|
88
88
|
errmsg "No sourcefile available for %s\n", file
|
89
89
|
return @state.previous_line
|
90
90
|
end
|
91
|
-
return
|
91
|
+
return b
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
@@ -4,14 +4,17 @@ module Debugger
|
|
4
4
|
class QuitCommand < Command
|
5
5
|
self.allow_in_control = true
|
6
6
|
|
7
|
+
# An input line is matched against this regular expression. If we have
|
8
|
+
# a match, run this command.
|
7
9
|
def regexp
|
8
10
|
/ ^\s*
|
9
11
|
(?:q(?:uit)?|exit) \s*
|
10
|
-
(
|
12
|
+
(!|\s+unconditionally)? \s*
|
11
13
|
$
|
12
14
|
/ix
|
13
15
|
end
|
14
16
|
|
17
|
+
# The code that implements this command.
|
15
18
|
def execute
|
16
19
|
if @match[1] or confirm("Really quit? (y/n) ")
|
17
20
|
@state.interface.finalize
|
@@ -20,19 +23,22 @@ module Debugger
|
|
20
23
|
end
|
21
24
|
|
22
25
|
class << self
|
26
|
+
# The command name listed via 'help'
|
23
27
|
def help_command
|
24
28
|
%w[quit exit]
|
25
29
|
end
|
26
30
|
|
31
|
+
# Returns a String given the help description of this command
|
27
32
|
def help(cmd)
|
28
33
|
%{
|
29
|
-
q[uit] [unconditionally]\texit from debugger.
|
30
|
-
exit\talias to quit
|
34
|
+
q[uit] [!|unconditionally]\texit from debugger.
|
35
|
+
exit[!]\talias to quit
|
31
36
|
|
32
37
|
Normally we prompt before exiting. However if the parameter
|
33
|
-
"unconditionally" is given
|
34
|
-
|
35
|
-
|
38
|
+
"unconditionally" or is given or suffixed with !, we stop
|
39
|
+
without asking further questions.
|
40
|
+
}
|
41
|
+
end
|
36
42
|
end
|
37
43
|
end
|
38
44
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Debugger
|
2
|
+
class RaiseCommand < Command # :nodoc:
|
3
|
+
self.allow_in_control = false
|
4
|
+
|
5
|
+
def regexp
|
6
|
+
/^\s* raise
|
7
|
+
(?:\s+ (\S+))? \s* $/ix
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
excn = @match[1]
|
12
|
+
exception =
|
13
|
+
if not excn
|
14
|
+
# No args given.
|
15
|
+
RuntimeError
|
16
|
+
else
|
17
|
+
unless debug_eval("#{excn}.is_a?(Class)", binding)
|
18
|
+
errmsg "#{excn} is not known to be a Class\n"
|
19
|
+
return
|
20
|
+
end
|
21
|
+
debug_eval(excn, binding)
|
22
|
+
end
|
23
|
+
@state.exception = exception
|
24
|
+
@state.proceed
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def help_command
|
29
|
+
'raise'
|
30
|
+
end
|
31
|
+
|
32
|
+
def help(cmd)
|
33
|
+
%{
|
34
|
+
raise EXCEPTION
|
35
|
+
|
36
|
+
Raise an exception in the debugged program."
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -38,37 +38,34 @@ module Debugger
|
|
38
38
|
style = Command.settings[:callstyle]
|
39
39
|
return "Frame call-display style is #{style}."
|
40
40
|
when /^commands(:?\s+(\d+))?$/
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
if last_line > Readline::HISTORY.length
|
53
|
-
last_line = Readline::HISTORY.length
|
54
|
-
end
|
55
|
-
i = first_line
|
56
|
-
commands = Readline::HISTORY.to_a[first_line..last_line]
|
57
|
-
else
|
58
|
-
if Readline::HISTORY.length > 10
|
59
|
-
commands = Readline::HISTORY.to_a[-10..-1]
|
60
|
-
i = Readline::HISTORY.length - 10
|
61
|
-
else
|
62
|
-
commands = Readline::HISTORY.to_a
|
63
|
-
i = 1
|
64
|
-
end
|
41
|
+
return 'No readline support.' unless @state.interface.readline_support?
|
42
|
+
s = '';
|
43
|
+
args = @match[1].split
|
44
|
+
if args[1]
|
45
|
+
first_line = args[1].to_i - 4
|
46
|
+
last_line = first_line + 10 - 1
|
47
|
+
if first_line > Readline::HISTORY.length
|
48
|
+
first_line = last_line = Readline::HISTORY.length
|
49
|
+
elsif first_line <= 0
|
50
|
+
first_line = 1
|
65
51
|
end
|
66
|
-
|
67
|
-
|
68
|
-
i += 1
|
52
|
+
if last_line > Readline::HISTORY.length
|
53
|
+
last_line = Readline::HISTORY.length
|
69
54
|
end
|
55
|
+
i = first_line
|
56
|
+
commands = Readline::HISTORY.to_a[first_line..last_line]
|
70
57
|
else
|
71
|
-
|
58
|
+
if Readline::HISTORY.length > 10
|
59
|
+
commands = Readline::HISTORY.to_a[-10..-1]
|
60
|
+
i = Readline::HISTORY.length - 10
|
61
|
+
else
|
62
|
+
commands = Readline::HISTORY.to_a
|
63
|
+
i = 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
commands.each do |cmd|
|
67
|
+
s += ("%5d %s\n" % [i, cmd])
|
68
|
+
i += 1
|
72
69
|
end
|
73
70
|
return s
|
74
71
|
when /^debuggertesting$/
|
@@ -81,6 +78,7 @@ module Debugger
|
|
81
78
|
on_off = Command.settings[:full_path]
|
82
79
|
return "Displaying frame's full file names is #{show_onoff(on_off)}."
|
83
80
|
when /^history(:?\s+(filename|save|size))?$/
|
81
|
+
return 'No readline support.' unless @state.interface.readline_support?
|
84
82
|
args = @match[1].split
|
85
83
|
interface = @state.interface
|
86
84
|
if args[1]
|