ruby-debug19 0.11.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/AUTHORS +9 -0
  2. data/LICENSE +23 -0
  3. data/bin/rdebug +415 -0
  4. data/cli/ruby-debug.rb +176 -0
  5. data/cli/ruby-debug/command.rb +228 -0
  6. data/cli/ruby-debug/commands/breakpoints.rb +153 -0
  7. data/cli/ruby-debug/commands/catchpoint.rb +55 -0
  8. data/cli/ruby-debug/commands/condition.rb +49 -0
  9. data/cli/ruby-debug/commands/continue.rb +38 -0
  10. data/cli/ruby-debug/commands/control.rb +107 -0
  11. data/cli/ruby-debug/commands/display.rb +120 -0
  12. data/cli/ruby-debug/commands/edit.rb +48 -0
  13. data/cli/ruby-debug/commands/enable.rb +202 -0
  14. data/cli/ruby-debug/commands/eval.rb +176 -0
  15. data/cli/ruby-debug/commands/finish.rb +42 -0
  16. data/cli/ruby-debug/commands/frame.rb +301 -0
  17. data/cli/ruby-debug/commands/help.rb +56 -0
  18. data/cli/ruby-debug/commands/info.rb +469 -0
  19. data/cli/ruby-debug/commands/irb.rb +123 -0
  20. data/cli/ruby-debug/commands/kill.rb +51 -0
  21. data/cli/ruby-debug/commands/list.rb +94 -0
  22. data/cli/ruby-debug/commands/method.rb +84 -0
  23. data/cli/ruby-debug/commands/quit.rb +39 -0
  24. data/cli/ruby-debug/commands/reload.rb +40 -0
  25. data/cli/ruby-debug/commands/save.rb +90 -0
  26. data/cli/ruby-debug/commands/set.rb +237 -0
  27. data/cli/ruby-debug/commands/show.rb +253 -0
  28. data/cli/ruby-debug/commands/source.rb +36 -0
  29. data/cli/ruby-debug/commands/stepping.rb +81 -0
  30. data/cli/ruby-debug/commands/threads.rb +189 -0
  31. data/cli/ruby-debug/commands/tmate.rb +36 -0
  32. data/cli/ruby-debug/commands/trace.rb +57 -0
  33. data/cli/ruby-debug/commands/variables.rb +199 -0
  34. data/cli/ruby-debug/debugger.rb +5 -0
  35. data/cli/ruby-debug/helper.rb +69 -0
  36. data/cli/ruby-debug/interface.rb +232 -0
  37. data/cli/ruby-debug/processor.rb +474 -0
  38. data/rdbg.rb +33 -0
  39. metadata +122 -0
@@ -0,0 +1,5 @@
1
+ # Module/Package to do the most-common thing: get into the debugger with
2
+ # minimal fuss. Compare with: require "debug"
3
+ require "ruby-debug"
4
+ Debugger.start
5
+ debugger
@@ -0,0 +1,69 @@
1
+ module Debugger
2
+
3
+ module ParseFunctions
4
+ Position_regexp = '(?:(\d+)|(.+?)[:.#]([^.:\s]+))'
5
+
6
+ # Parse 'str' of command 'cmd' as an integer between
7
+ # min and max. If either min or max is nil, that
8
+ # value has no bound.
9
+ def get_int(str, cmd, min=nil, max=nil, default=1)
10
+ return default unless str
11
+ begin
12
+ int = Integer(str)
13
+ if min and int < min
14
+ print "%s argument '%s' needs to at least %s.\n" % [cmd, str, min]
15
+ return nil
16
+ elsif max and int > max
17
+ print "%s argument '%s' needs to at most %s.\n" % [cmd, str, max]
18
+ return nil
19
+ end
20
+ return int
21
+ rescue
22
+ print "%s argument '%s' needs to be a number.\n" % [cmd, str]
23
+ return nil
24
+ end
25
+ end
26
+
27
+ # Return true if arg is 'on' or 1 and false arg is 'off' or 0.
28
+ # Any other value raises RuntimeError.
29
+ def get_onoff(arg, default=nil, print_error=true)
30
+ if arg.nil? or arg == ''
31
+ if default.nil?
32
+ if print_error
33
+ print "Expecting 'on', 1, 'off', or 0. Got nothing.\n"
34
+ raise RuntimeError
35
+ end
36
+ return default
37
+ end
38
+ end
39
+ case arg.downcase
40
+ when '1', 'on'
41
+ return true
42
+ when '0', 'off'
43
+ return false
44
+ else
45
+ if print_error
46
+ print "Expecting 'on', 1, 'off', or 0. Got: %s.\n" % arg.to_s
47
+ raise RuntimeError
48
+ end
49
+ end
50
+ end
51
+
52
+ # Return 'on' or 'off' for supplied parameter. The parmeter should
53
+ # be true, false or nil.
54
+ def show_onoff(bool)
55
+ if not [TrueClass, FalseClass, NilClass].member?(bool.class)
56
+ return "??"
57
+ end
58
+ return bool ? 'on' : 'off'
59
+ end
60
+
61
+ # Return true if code is syntactically correct for Ruby.
62
+ def syntax_valid?(code)
63
+ eval("BEGIN {return true}\n#{code}", nil, "", 0)
64
+ rescue Exception
65
+ false
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,232 @@
1
+ module Debugger
2
+ class Interface # :nodoc:
3
+ attr_writer :have_readline # true if Readline is available
4
+
5
+ def initialize
6
+ @have_readline = false
7
+ end
8
+
9
+ # Common routine for reporting debugger error messages.
10
+ # Derived classed may want to override this to capture output.
11
+ def errmsg(*args)
12
+ if Debugger.annotate.to_i > 2
13
+ aprint 'error-begin'
14
+ print(*args)
15
+ aprint ''
16
+ else
17
+ print '*** '
18
+ print(*args)
19
+ end
20
+ end
21
+
22
+ # Format msg with gdb-style annotation header
23
+ def afmt(msg, newline="\n")
24
+ "\032\032#{msg}#{newline}"
25
+ end
26
+
27
+ def aprint(msg)
28
+ print afmt(msg)
29
+ end
30
+
31
+ end
32
+
33
+ class LocalInterface < Interface # :nodoc:
34
+ attr_accessor :command_queue
35
+ attr_accessor :histfile
36
+ attr_accessor :history_save
37
+ attr_accessor :history_length
38
+ attr_accessor :restart_file
39
+
40
+ unless defined?(FILE_HISTORY)
41
+ FILE_HISTORY = ".rdebug_hist"
42
+ end
43
+ def initialize()
44
+ super
45
+ @command_queue = []
46
+ @have_readline = false
47
+ @history_save = true
48
+ # take gdb's default
49
+ @history_length = ENV["HISTSIZE"] ? ENV["HISTSIZE"].to_i : 256
50
+ @histfile = File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
51
+ FILE_HISTORY)
52
+ open(@histfile, 'r') do |file|
53
+ file.each do |line|
54
+ line.chomp!
55
+ Readline::HISTORY << line
56
+ end
57
+ end if File.exist?(@histfile)
58
+ @restart_file = nil
59
+ end
60
+
61
+ def read_command(prompt)
62
+ readline(prompt, true)
63
+ end
64
+
65
+ def confirm(prompt)
66
+ readline(prompt, false)
67
+ end
68
+
69
+ def print(*args)
70
+ STDOUT.printf(*args)
71
+ end
72
+
73
+ def close
74
+ end
75
+
76
+ # Things to do before quitting
77
+ def finalize
78
+ if Debugger.method_defined?("annotate") and Debugger.annotate.to_i > 2
79
+ print "\032\032exited\n\n"
80
+ end
81
+ if Debugger.respond_to?(:save_history)
82
+ Debugger.save_history
83
+ end
84
+ end
85
+
86
+ def readline_support?
87
+ @have_readline
88
+ end
89
+
90
+ private
91
+ begin
92
+ require 'readline'
93
+ class << Debugger
94
+ @have_readline = true
95
+ define_method(:save_history) do
96
+ iface = self.handler.interface
97
+ iface.histfile ||= File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
98
+ FILE_HISTORY)
99
+ open(iface.histfile, 'w') do |file|
100
+ Readline::HISTORY.to_a.last(iface.history_length).each do |line|
101
+ file.puts line unless line.strip.empty?
102
+ end if defined?(iface.history_save) and iface.history_save
103
+ end rescue nil
104
+ end
105
+ public :save_history
106
+ end
107
+ Debugger.debug_at_exit do
108
+ finalize if respond_to?(:finalize)
109
+ end
110
+
111
+ def readline(prompt, hist)
112
+ Readline::readline(prompt, hist)
113
+ end
114
+ rescue LoadError
115
+ def readline(prompt, hist)
116
+ @histfile = ''
117
+ @hist_save = false
118
+ STDOUT.print prompt
119
+ STDOUT.flush
120
+ line = STDIN.gets
121
+ exit unless line
122
+ line.chomp!
123
+ line
124
+ end
125
+ end
126
+ end
127
+
128
+ class RemoteInterface < Interface # :nodoc:
129
+ attr_accessor :command_queue
130
+ attr_accessor :histfile
131
+ attr_accessor :history_save
132
+ attr_accessor :history_length
133
+ attr_accessor :restart_file
134
+
135
+ def initialize(socket)
136
+ @command_queue = []
137
+ @socket = socket
138
+ @history_save = false
139
+ @history_length = 256
140
+ @histfile = ''
141
+ # Do we read the histfile?
142
+ # open(@histfile, 'r') do |file|
143
+ # file.each do |line|
144
+ # line.chomp!
145
+ # Readline::HISTORY << line
146
+ # end
147
+ # end if File.exist?(@histfile)
148
+ @restart_file = nil
149
+ end
150
+
151
+ def close
152
+ @socket.close
153
+ rescue Exception
154
+ end
155
+
156
+ def confirm(prompt)
157
+ send_command "CONFIRM #{prompt}"
158
+ end
159
+
160
+ def finalize
161
+ end
162
+
163
+ def read_command(prompt)
164
+ send_command "PROMPT #{prompt}"
165
+ end
166
+
167
+ def readline_support?
168
+ false
169
+ end
170
+
171
+ def print(*args)
172
+ @socket.printf(*args)
173
+ end
174
+
175
+ private
176
+
177
+ def send_command(msg)
178
+ @socket.puts msg
179
+ result = @socket.gets
180
+ raise IOError unless result
181
+ result.chomp
182
+ end
183
+ end
184
+
185
+ class ScriptInterface < Interface # :nodoc:
186
+ attr_accessor :command_queue
187
+ attr_accessor :histfile
188
+ attr_accessor :history_save
189
+ attr_accessor :history_length
190
+ attr_accessor :restart_file
191
+ def initialize(file, out, verbose=false)
192
+ super()
193
+ @command_queue = []
194
+ @file = file.respond_to?(:gets) ? file : open(file)
195
+ @out = out
196
+ @verbose = verbose
197
+ @history_save = false
198
+ @history_length = 256 # take gdb default
199
+ @histfile = ''
200
+ end
201
+
202
+ def finalize
203
+ end
204
+
205
+ def read_command(prompt)
206
+ while result = @file.gets
207
+ puts "# #{result}" if @verbose
208
+ next if result =~ /^\s*#/
209
+ next if result.strip.empty?
210
+ break
211
+ end
212
+ raise IOError unless result
213
+ result.chomp!
214
+ end
215
+
216
+ def readline_support?
217
+ false
218
+ end
219
+
220
+ def confirm(prompt)
221
+ 'y'
222
+ end
223
+
224
+ def print(*args)
225
+ @out.printf(*args)
226
+ end
227
+
228
+ def close
229
+ @file.close
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,474 @@
1
+ require_relative 'interface'
2
+ require_relative 'command'
3
+
4
+ module Debugger
5
+
6
+ # Should this be a mixin?
7
+ class Processor # :nodoc
8
+ attr_accessor :interface
9
+
10
+ # Format msg with gdb-style annotation header
11
+ def afmt(msg, newline="\n")
12
+ "\032\032#{msg}#{newline}"
13
+ end
14
+
15
+ def aprint(msg)
16
+ print afmt(msg) if Debugger.annotate.to_i > 2
17
+ end
18
+
19
+ # FIXME: use delegate?
20
+ def errmsg(*args)
21
+ @interface.errmsg(*args)
22
+ end
23
+
24
+ # Callers of this routine should make sure to use comma to
25
+ # separate format argments rather than %. Otherwise it seems that
26
+ # if the string you want to print has format specifier, which
27
+ # could happen if you are trying to show say a source-code line
28
+ # with "puts" or "print" in it, this print routine will give an
29
+ # error saying it is looking for more arguments.
30
+ def print(*args)
31
+ @interface.print(*args)
32
+ end
33
+
34
+ end
35
+
36
+ class CommandProcessor < Processor # :nodoc:
37
+ attr_reader :display
38
+
39
+ # FIXME: get from Command regexp method.
40
+ @@Show_breakpoints_postcmd = [
41
+ /^\s*b(?:reak)?/,
42
+ /^\s* cond(?:ition)? (?:\s+(\d+)\s*(.*))?$/ix,
43
+ /^\s*del(?:ete)?(?:\s+(.*))?$/ix,
44
+ /^\s* dis(?:able)? (?:\s+(.*))?$/ix,
45
+ /^\s* en(?:able)? (?:\s+(.*))?$/ix,
46
+ # "tbreak", "clear",
47
+ ]
48
+ @@Show_annotations_run = [
49
+ /^\s*c(?:ont(?:inue)?)?(?:\s+(.*))?$/,
50
+ /^\s*fin(?:ish)?$/,
51
+ /^\s*n(?:ext)?([+-])?(?:\s+(.*))?$/,
52
+ /^\s*s(?:tep)?([+-])?(?:\s+(.*))?$/
53
+ ]
54
+
55
+ @@Show_annotations_postcmd = [
56
+ /^\s* down (?:\s+(.*))? .*$/x,
57
+ /^\s* f(?:rame)? (?:\s+ (.*))? \s*$/x,
58
+ /^\s* u(?:p)? (?:\s+(.*))?$/x
59
+ ]
60
+
61
+ def initialize(interface = LocalInterface.new)
62
+ @interface = interface
63
+ @display = []
64
+
65
+ @mutex = Mutex.new
66
+ @last_cmd = nil
67
+ @last_file = nil # Filename the last time we stopped
68
+ @last_line = nil # line number the last time we stopped
69
+ @debugger_breakpoints_were_empty = false # Show breakpoints 1st time
70
+ @debugger_displays_were_empty = true # No display 1st time
71
+ @debugger_context_was_dead = true # Assume we haven't started.
72
+ end
73
+
74
+ def interface=(interface)
75
+ @mutex.synchronize do
76
+ @interface.close if @interface
77
+ @interface = interface
78
+ end
79
+ end
80
+
81
+ require 'pathname' # For cleanpath
82
+
83
+ # Regularize file name.
84
+ # This is also used as a common funnel place if basename is
85
+ # desired or if we are working remotely and want to change the
86
+ # basename. Or we are eliding filenames.
87
+ def self.canonic_file(filename)
88
+ # For now we want resolved filenames
89
+ if Command.settings[:basename]
90
+ File.basename(filename)
91
+ else
92
+ # Cache this?
93
+ Pathname.new(filename).cleanpath.to_s
94
+ end
95
+ end
96
+
97
+ def self.print_location_and_text(file, line)
98
+ file_line = "%s:%s\n%s" % [canonic_file(file), line,
99
+ Debugger.line_at(file, line)]
100
+ # FIXME: use annotations routines
101
+ if Debugger.annotate.to_i > 2
102
+ file_line = "\032\032source #{file_line}"
103
+ elsif ENV['EMACS']
104
+ file_line = "\032\032#{file_line}"
105
+ end
106
+ print file_line
107
+ end
108
+
109
+ def self.protect(mname)
110
+ alias_method "__#{mname}", mname
111
+ module_eval %{
112
+ def #{mname}(*args)
113
+ @mutex.synchronize do
114
+ return unless @interface
115
+ __#{mname}(*args)
116
+ end
117
+ rescue IOError, Errno::EPIPE
118
+ self.interface = nil
119
+ rescue SignalException
120
+ raise
121
+ rescue Exception
122
+ print "INTERNAL ERROR!!! #\{$!\}\n" rescue nil
123
+ print $!.backtrace.map{|l| "\t#\{l\}"}.join("\n") rescue nil
124
+ end
125
+ }
126
+ end
127
+
128
+ def at_breakpoint(context, breakpoint)
129
+ aprint 'stopped' if Debugger.annotate.to_i > 2
130
+ n = Debugger.breakpoints.index(breakpoint) + 1
131
+ file = CommandProcessor.canonic_file(breakpoint.source)
132
+ line = breakpoint.pos
133
+ if Debugger.annotate.to_i > 2
134
+ print afmt("source #{file}:#{line}")
135
+ end
136
+ print "Breakpoint %d at %s:%s\n", n, file, line
137
+ end
138
+ protect :at_breakpoint
139
+
140
+ def at_catchpoint(context, excpt)
141
+ aprint 'stopped' if Debugger.annotate.to_i > 2
142
+ file = CommandProcessor.canonic_file(context.frame_file(0))
143
+ line = context.frame_line(0)
144
+ print afmt("%s:%d" % [file, line]) if ENV['EMACS']
145
+ print "Catchpoint at %s:%d: `%s' (%s)\n", file, line, excpt, excpt.class
146
+ fs = context.stack_size
147
+ tb = caller(0)[-fs..-1]
148
+ if tb
149
+ for i in tb
150
+ print "\tfrom %s\n", i
151
+ end
152
+ end
153
+ end
154
+ protect :at_catchpoint
155
+
156
+ def at_tracing(context, file, line)
157
+ return if defined?(Debugger::RDEBUG_FILE) &&
158
+ Debugger::RDEBUG_FILE == file # Don't trace ourself
159
+ @last_file = CommandProcessor.canonic_file(file)
160
+ file = CommandProcessor.canonic_file(file)
161
+ unless file == @last_file and @last_line == line and
162
+ Command.settings[:tracing_plus]
163
+ print "Tracing(%d):%s:%s %s",
164
+ context.thnum, file, line, Debugger.line_at(file, line)
165
+ @last_file = file
166
+ @last_line = line
167
+ end
168
+ always_run(context, file, line, 2)
169
+ end
170
+ protect :at_tracing
171
+
172
+ def at_line(context, file, line)
173
+ process_commands(context, file, line)
174
+ end
175
+ protect :at_line
176
+
177
+ def at_return(context, file, line)
178
+ context.stop_frame = -1
179
+ process_commands(context, file, line)
180
+ end
181
+
182
+ private
183
+
184
+ # The prompt shown before reading a command.
185
+ def prompt(context)
186
+ p = '(rdb:%s) ' % (context.dead? ? 'post-mortem' : context.thnum)
187
+ p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
188
+ Debugger.annotate.to_i > 2
189
+ return p
190
+ end
191
+
192
+ # Run these commands, for example display commands or possibly
193
+ # the list or irb in an "autolist" or "autoirb".
194
+ # We return a list of commands that are acceptable to run bound
195
+ # to the current state.
196
+ def always_run(context, file, line, run_level)
197
+ event_cmds = Command.commands.select{|cmd| cmd.event }
198
+
199
+ # Remove some commands in post-mortem
200
+ event_cmds = event_cmds.find_all do |cmd|
201
+ cmd.allow_in_post_mortem
202
+ end if context.dead?
203
+
204
+ state = State.new do |s|
205
+ s.context = context
206
+ s.file = file
207
+ s.line = line
208
+ s.binding = context.frame_binding(0)
209
+ s.display = display
210
+ s.interface = interface
211
+ s.commands = event_cmds
212
+ end
213
+ @interface.state = state if @interface.respond_to?('state=')
214
+
215
+ # Bind commands to the current state.
216
+ commands = event_cmds.map{|cmd| cmd.new(state)}
217
+
218
+ commands.select do |cmd|
219
+ cmd.class.always_run >= run_level
220
+ end.each {|cmd| cmd.execute}
221
+ return state, commands
222
+ end
223
+
224
+ # Handle debugger commands
225
+ def process_commands(context, file, line)
226
+ state, commands = always_run(context, file, line, 1)
227
+ $rdebug_state = state if Command.settings[:debuggertesting]
228
+ splitter = lambda do |str|
229
+ str.split(/;/).inject([]) do |m, v|
230
+ if m.empty?
231
+ m << v
232
+ else
233
+ if m.last[-1] == ?\\
234
+ m.last[-1,1] = ''
235
+ m.last << ';' << v
236
+ else
237
+ m << v
238
+ end
239
+ end
240
+ m
241
+ end
242
+ end
243
+
244
+ preloop(commands, context)
245
+ CommandProcessor.print_location_and_text(file, line)
246
+ while !state.proceed?
247
+ input = if @interface.command_queue.empty?
248
+ @interface.read_command(prompt(context))
249
+ else
250
+ @interface.command_queue.shift
251
+ end
252
+ break unless input
253
+ catch(:debug_error) do
254
+ if input == ""
255
+ next unless @last_cmd
256
+ input = @last_cmd
257
+ else
258
+ @last_cmd = input
259
+ end
260
+ splitter[input].each do |cmd|
261
+ one_cmd(commands, context, cmd)
262
+ postcmd(commands, context, cmd)
263
+ end
264
+ end
265
+ end
266
+ postloop(commands, context)
267
+ end # process_commands
268
+
269
+ def one_cmd(commands, context, input)
270
+ if cmd = commands.find{ |c| c.match(input) }
271
+ if context.dead? && cmd.class.need_context
272
+ p cmd
273
+ print "Command is unavailable\n"
274
+ else
275
+ cmd.execute
276
+ end
277
+ else
278
+ unknown_cmd = commands.find{|cmd| cmd.class.unknown }
279
+ if unknown_cmd
280
+ unknown_cmd.execute
281
+ else
282
+ errmsg "Unknown command: \"#{input}\". Try \"help\".\n"
283
+ end
284
+ end
285
+ end
286
+
287
+ def preloop(commands, context)
288
+ aprint('stopped') if Debugger.annotate.to_i > 2
289
+ if context.dead?
290
+ unless @debugger_context_was_dead
291
+ if Debugger.annotate.to_i > 2
292
+ aprint('exited')
293
+ print "The program finished.\n"
294
+ end
295
+ @debugger_context_was_dead = true
296
+ end
297
+ end
298
+
299
+ if Debugger.annotate.to_i > 2
300
+ # if we are here, the stack frames have changed outside the
301
+ # command loop (e.g. after a "continue" command), so we show
302
+ # the annotations again
303
+ breakpoint_annotations(commands, context)
304
+ display_annotations(commands, context)
305
+ annotation('stack', commands, context, "where")
306
+ annotation('variables', commands, context, "info variables") unless
307
+ context.dead?
308
+ end
309
+ end
310
+
311
+ def postcmd(commands, context, cmd)
312
+ if Debugger.annotate.to_i > 0
313
+ cmd = @last_cmd unless cmd
314
+ breakpoint_annotations(commands, context) if
315
+ @@Show_breakpoints_postcmd.find{|pat| cmd =~ pat}
316
+ display_annotations(commands, context)
317
+ if @@Show_annotations_postcmd.find{|pat| cmd =~ pat}
318
+ annotation('stack', commands, context, "where") if
319
+ context.stack_size > 0
320
+ annotation('variables', commands, context, "info variables") unless
321
+ context.dead?
322
+ end
323
+ if not context.dead? and @@Show_annotations_run.find{|pat| cmd =~ pat}
324
+ aprint 'starting' if Debugger.annotate.to_i > 2
325
+
326
+ @debugger_context_was_dead = false
327
+ end
328
+ end
329
+ end
330
+
331
+ def postloop(commands, context)
332
+ end
333
+
334
+ def annotation(label, commands, context, cmd)
335
+ print afmt(label)
336
+ one_cmd(commands, context, cmd)
337
+ ### FIXME ANNOTATE: the following line should be deleted
338
+ print "\032\032\n"
339
+ end
340
+
341
+ def breakpoint_annotations(commands, context)
342
+ unless Debugger.breakpoints.empty? and @debugger_breakpoints_were_empty
343
+ annotation('breakpoints', commands, context, "info breakpoints")
344
+ @debugger_breakpoints_were_empty = Debugger.breakpoints.empty?
345
+ end
346
+ end
347
+
348
+ def display_annotations(commands, context)
349
+ return if display.empty?
350
+ # have_display = display.find{|d| d[0]}
351
+ # return unless have_display and @debugger_displays_were_empty
352
+ # @debugger_displays_were_empty = have_display
353
+ annotation('display', commands, context, "display")
354
+ end
355
+
356
+ class State # :nodoc:
357
+ attr_accessor :context, :file, :line, :binding
358
+ attr_accessor :frame_pos, :previous_line, :display
359
+ attr_accessor :interface, :commands
360
+
361
+ def initialize
362
+ super()
363
+ @frame_pos = 0
364
+ @previous_line = nil
365
+ @proceed = false
366
+ yield self
367
+ end
368
+
369
+ # FIXME: use delegate?
370
+ def errmsg(*args)
371
+ @interface.errmsg(*args)
372
+ end
373
+
374
+ def print(*args)
375
+ @interface.print(*args)
376
+ end
377
+
378
+ def confirm(*args)
379
+ @interface.confirm(*args)
380
+ end
381
+
382
+ def proceed?
383
+ @proceed
384
+ end
385
+
386
+ def proceed
387
+ @proceed = true
388
+ end
389
+ end
390
+ end
391
+
392
+ class ControlCommandProcessor < Processor # :nodoc:
393
+ def initialize(interface)
394
+ super()
395
+ @interface = interface
396
+ @debugger_context_was_dead = true # Assume we haven't started.
397
+ end
398
+
399
+ def process_commands(verbose=false)
400
+ control_cmds = Command.commands.select do |cmd|
401
+ cmd.allow_in_control
402
+ end
403
+ state = State.new(@interface, control_cmds)
404
+ commands = control_cmds.map{|cmd| cmd.new(state) }
405
+
406
+ unless @debugger_context_was_dead
407
+ if Debugger.annotate.to_i > 2
408
+ aprint 'exited'
409
+ print "The program finished.\n"
410
+ end
411
+ @debugger_context_was_dead = true
412
+ end
413
+
414
+ while input = @interface.read_command(prompt(nil))
415
+ print "+#{input}" if verbose
416
+ catch(:debug_error) do
417
+ if cmd = commands.find{|c| c.match(input) }
418
+ cmd.execute
419
+ else
420
+ errmsg "Unknown command\n"
421
+ end
422
+ end
423
+ end
424
+ rescue IOError, Errno::EPIPE
425
+ rescue Exception
426
+ print "INTERNAL ERROR!!! #{$!}\n" rescue nil
427
+ print $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
428
+ ensure
429
+ @interface.close
430
+ end
431
+
432
+ # The prompt shown before reading a command.
433
+ # Note: have an unused 'context' parameter to match the local interface.
434
+ def prompt(context)
435
+ p = '(rdb:ctrl) '
436
+ p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
437
+ Debugger.annotate.to_i > 2
438
+ return p
439
+ end
440
+
441
+ class State # :nodoc:
442
+ attr_reader :commands, :interface
443
+
444
+ def initialize(interface, commands)
445
+ @interface = interface
446
+ @commands = commands
447
+ end
448
+
449
+ def proceed
450
+ end
451
+
452
+ def errmsg(*args)
453
+ @interface.print(*args)
454
+ end
455
+
456
+ def print(*args)
457
+ @interface.print(*args)
458
+ end
459
+
460
+ def confirm(*args)
461
+ 'y'
462
+ end
463
+
464
+ def context
465
+ nil
466
+ end
467
+
468
+ def file
469
+ errmsg "No filename given.\n"
470
+ throw :debug_error
471
+ end
472
+ end # State
473
+ end
474
+ end