ruby-debug193 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +10 -0
- data/LICENSE +23 -0
- data/bin/rdebug +398 -0
- data/cli/ruby-debug.rb +173 -0
- data/cli/ruby-debug/command.rb +228 -0
- data/cli/ruby-debug/commands/breakpoints.rb +153 -0
- data/cli/ruby-debug/commands/catchpoint.rb +55 -0
- data/cli/ruby-debug/commands/condition.rb +49 -0
- data/cli/ruby-debug/commands/continue.rb +38 -0
- data/cli/ruby-debug/commands/control.rb +107 -0
- data/cli/ruby-debug/commands/display.rb +120 -0
- data/cli/ruby-debug/commands/edit.rb +48 -0
- data/cli/ruby-debug/commands/enable.rb +202 -0
- data/cli/ruby-debug/commands/eval.rb +176 -0
- data/cli/ruby-debug/commands/finish.rb +42 -0
- data/cli/ruby-debug/commands/frame.rb +301 -0
- data/cli/ruby-debug/commands/help.rb +56 -0
- data/cli/ruby-debug/commands/info.rb +469 -0
- data/cli/ruby-debug/commands/irb.rb +123 -0
- data/cli/ruby-debug/commands/jump.rb +66 -0
- data/cli/ruby-debug/commands/kill.rb +51 -0
- data/cli/ruby-debug/commands/list.rb +94 -0
- data/cli/ruby-debug/commands/method.rb +84 -0
- data/cli/ruby-debug/commands/quit.rb +39 -0
- data/cli/ruby-debug/commands/reload.rb +40 -0
- data/cli/ruby-debug/commands/save.rb +90 -0
- data/cli/ruby-debug/commands/set.rb +237 -0
- data/cli/ruby-debug/commands/show.rb +253 -0
- data/cli/ruby-debug/commands/source.rb +36 -0
- data/cli/ruby-debug/commands/stepping.rb +81 -0
- data/cli/ruby-debug/commands/threads.rb +189 -0
- data/cli/ruby-debug/commands/tmate.rb +36 -0
- data/cli/ruby-debug/commands/trace.rb +57 -0
- data/cli/ruby-debug/commands/variables.rb +199 -0
- data/cli/ruby-debug/debugger.rb +5 -0
- data/cli/ruby-debug/helper.rb +69 -0
- data/cli/ruby-debug/interface.rb +232 -0
- data/cli/ruby-debug/processor.rb +474 -0
- data/rdbg.rb +33 -0
- metadata +144 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
# Implements debugger "help" command.
|
4
|
+
class HelpCommand < Command
|
5
|
+
self.allow_in_control = true
|
6
|
+
|
7
|
+
def regexp
|
8
|
+
/^\s* h(?:elp)? (?:\s+(.+))? $/x
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
if @match[1]
|
13
|
+
args = @match[1].split
|
14
|
+
cmds = @state.commands.select do |cmd|
|
15
|
+
[cmd.help_command].flatten.include?(args[0])
|
16
|
+
end
|
17
|
+
else
|
18
|
+
args = @match[1]
|
19
|
+
cmds = []
|
20
|
+
end
|
21
|
+
unless cmds.empty?
|
22
|
+
help = cmds.map{ |cmd| cmd.help(args) }.join
|
23
|
+
help = help.split("\n").map{|l| l.gsub(/^ +/, '')}
|
24
|
+
help.shift if help.first && help.first.empty?
|
25
|
+
help.pop if help.last && help.last.empty?
|
26
|
+
print help.join("\n")
|
27
|
+
else
|
28
|
+
if args and args[0]
|
29
|
+
errmsg "Undefined command: \"#{args[0]}\". Try \"help\"."
|
30
|
+
else
|
31
|
+
print "ruby-debug help v#{Debugger::VERSION}\n" unless
|
32
|
+
self.class.settings[:debuggertesting]
|
33
|
+
print "Type 'help <command-name>' for help on a specific command\n\n"
|
34
|
+
print "Available commands:\n"
|
35
|
+
cmds = @state.commands.map{ |cmd| cmd.help_command }
|
36
|
+
cmds = cmds.flatten.uniq.sort
|
37
|
+
print columnize(cmds, self.class.settings[:width])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
print "\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def help_command
|
45
|
+
'help'
|
46
|
+
end
|
47
|
+
|
48
|
+
def help(cmd)
|
49
|
+
%{
|
50
|
+
h[elp]\t\tprint this help
|
51
|
+
h[elp] command\tprint help on command
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,469 @@
|
|
1
|
+
module Debugger
|
2
|
+
module InfoFunctions # :nodoc:
|
3
|
+
def info_catch(*args)
|
4
|
+
unless @state.context
|
5
|
+
print "No frame selected.\n"
|
6
|
+
return
|
7
|
+
end
|
8
|
+
if Debugger.catchpoints and not Debugger.catchpoints.empty?
|
9
|
+
# FIXME: show whether Exception is valid or not
|
10
|
+
# print "Exception: is_a?(Class)\n"
|
11
|
+
Debugger.catchpoints.each do |exception, hits|
|
12
|
+
# print "#{exception}: #{exception.is_a?(Class)}\n"
|
13
|
+
print "#{exception}\n"
|
14
|
+
end
|
15
|
+
else
|
16
|
+
print "No exceptions set to be caught.\n"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Implements debugger "info" command.
|
22
|
+
class InfoCommand < Command
|
23
|
+
self.allow_in_control = true
|
24
|
+
Subcommands =
|
25
|
+
[
|
26
|
+
['args', 1, 'Argument variables of current stack frame'],
|
27
|
+
['breakpoints', 1, 'Status of user-settable breakpoints',
|
28
|
+
'Without argument, list info about all breakpoints. With an
|
29
|
+
integer argument, list info on that breakpoint.'],
|
30
|
+
['catch', 3, 'Exceptions that can be caught in the current stack frame'],
|
31
|
+
['display', 2, 'Expressions to display when program stops'],
|
32
|
+
['file', 4, 'Info about a particular file read in',
|
33
|
+
'
|
34
|
+
After the file name is supplied, you can list file attributes that
|
35
|
+
you wish to see.
|
36
|
+
|
37
|
+
Attributes include: "all", "basic", "breakpoint", "lines", "mtime", "path"
|
38
|
+
and "sha1".'],
|
39
|
+
['files', 5, 'File names and timestamps of files read in'],
|
40
|
+
['global_variables', 2, 'Global variables'],
|
41
|
+
['instance_variables', 2,
|
42
|
+
'Instance variables of the current stack frame'],
|
43
|
+
['line', 2,
|
44
|
+
'Line number and file name of current position in source file'],
|
45
|
+
['locals', 2, 'Local variables of the current stack frame'],
|
46
|
+
['program', 2, 'Execution status of the program'],
|
47
|
+
['stack', 2, 'Backtrace of the stack'],
|
48
|
+
['thread', 6, 'List info about thread NUM', '
|
49
|
+
If no thread number is given, we list info for all threads. \'terse\' and \'verbose\'
|
50
|
+
options are possible. If terse, just give summary thread name information. See
|
51
|
+
"help info threads" for more detail about this summary information.
|
52
|
+
|
53
|
+
If \'verbose\' appended to the end of the command, then the entire
|
54
|
+
stack trace is given for each thread.'],
|
55
|
+
['threads', 7, 'information of currently-known threads', '
|
56
|
+
This information includes whether the thread is current (+), if it is
|
57
|
+
suspended ($), or ignored (!). The thread number and the top stack
|
58
|
+
item. If \'verbose\' is given then the entire stack frame is shown.'],
|
59
|
+
['variables', 1,
|
60
|
+
'Local and instance variables of the current stack frame']
|
61
|
+
].map do |name, min, short_help, long_help|
|
62
|
+
SubcmdStruct.new(name, min, short_help, long_help)
|
63
|
+
end unless defined?(Subcommands)
|
64
|
+
|
65
|
+
InfoFileSubcommands =
|
66
|
+
[
|
67
|
+
['all', 1,
|
68
|
+
'All file information available - breakpoints, lines, mtime, path, and sha1'],
|
69
|
+
['basic', 2,
|
70
|
+
'basic information - path, number of lines'],
|
71
|
+
['breakpoints', 2, 'Show trace line numbers',
|
72
|
+
'These are the line number where a breakpoint can be set.'],
|
73
|
+
['lines', 1, 'Show number of lines in the file'],
|
74
|
+
['mtime', 1, 'Show modification time of file'],
|
75
|
+
['path', 4, 'Show full file path name for file'],
|
76
|
+
['sha1', 1, 'Show SHA1 hash of contents of the file']
|
77
|
+
].map do |name, min, short_help, long_help|
|
78
|
+
SubcmdStruct.new(name, min, short_help, long_help)
|
79
|
+
end unless defined?(InfoFileSubcommands)
|
80
|
+
|
81
|
+
InfoThreadSubcommands =
|
82
|
+
[
|
83
|
+
['terse', 1, 'summary information'],
|
84
|
+
['verbose', 1, 'summary information and stack frame info'],
|
85
|
+
].map do |name, min, short_help, long_help|
|
86
|
+
SubcmdStruct.new(name, min, short_help, long_help)
|
87
|
+
end unless defined?(InfoThreadSubcommands)
|
88
|
+
|
89
|
+
def regexp
|
90
|
+
/^\s* i(?:nfo)? (?:\s+(.*))?$/ix
|
91
|
+
end
|
92
|
+
|
93
|
+
def execute
|
94
|
+
if !@match[1]
|
95
|
+
errmsg "\"info\" must be followed by the name of an info command:\n"
|
96
|
+
print "List of info subcommands:\n\n"
|
97
|
+
for subcmd in Subcommands do
|
98
|
+
print "info #{subcmd.name} -- #{subcmd.short_help}\n"
|
99
|
+
end
|
100
|
+
else
|
101
|
+
args = @match[1].split(/[ \t]+/)
|
102
|
+
param = args.shift
|
103
|
+
subcmd = find(Subcommands, param)
|
104
|
+
if subcmd
|
105
|
+
send("info_#{subcmd.name}", *args)
|
106
|
+
else
|
107
|
+
errmsg "Unknown info command #{param}\n"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def info_args(*args)
|
113
|
+
unless @state.context
|
114
|
+
print "No frame selected.\n"
|
115
|
+
return
|
116
|
+
end
|
117
|
+
locals = @state.context.frame_locals(@state.frame_pos)
|
118
|
+
args = @state.context.frame_args(@state.frame_pos)
|
119
|
+
args.each do |name|
|
120
|
+
s = "#{name} = #{locals[name].inspect}"
|
121
|
+
if s.size > self.class.settings[:width]
|
122
|
+
s[self.class.settings[:width]-3 .. -1] = "..."
|
123
|
+
end
|
124
|
+
print "#{s}\n"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def info_breakpoints(*args)
|
129
|
+
unless @state.context
|
130
|
+
print "info breakpoints not available here.\n"
|
131
|
+
return
|
132
|
+
end
|
133
|
+
unless Debugger.breakpoints.empty?
|
134
|
+
brkpts = Debugger.breakpoints.sort_by{|b| b.id}
|
135
|
+
unless args.empty?
|
136
|
+
a = args.map{|a| a.to_i}
|
137
|
+
brkpts = brkpts.select{|b| a.member?(b.id)}
|
138
|
+
if brkpts.empty?
|
139
|
+
errmsg "No breakpoints found among list given.\n"
|
140
|
+
return
|
141
|
+
end
|
142
|
+
end
|
143
|
+
print "Num Enb What\n"
|
144
|
+
brkpts.each do |b|
|
145
|
+
if b.expr.nil?
|
146
|
+
print "%3d %s at %s:%s\n",
|
147
|
+
b.id, (b.enabled? ? 'y' : 'n'), b.source, b.pos
|
148
|
+
else
|
149
|
+
print "%3d %s at %s:%s if %s\n",
|
150
|
+
b.id, (b.enabled? ? 'y' : 'n'), b.source, b.pos, b.expr
|
151
|
+
end
|
152
|
+
hits = b.hit_count
|
153
|
+
if hits > 0
|
154
|
+
s = (hits > 1) ? 's' : ''
|
155
|
+
print "\tbreakpoint already hit #{hits} time#{s}\n"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
else
|
159
|
+
print "No breakpoints.\n"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def info_display(*args)
|
164
|
+
unless @state.context
|
165
|
+
print "info display not available here.\n"
|
166
|
+
return
|
167
|
+
end
|
168
|
+
if @state.display.find{|d| d[0]}
|
169
|
+
print "Auto-display expressions now in effect:\n"
|
170
|
+
print "Num Enb Expression\n"
|
171
|
+
n = 1
|
172
|
+
for d in @state.display
|
173
|
+
print "%3d: %s %s\n", n, (d[0] ? 'y' : 'n'), d[1] if
|
174
|
+
d[0] != nil
|
175
|
+
n += 1
|
176
|
+
end
|
177
|
+
else
|
178
|
+
print "There are no auto-display expressions now.\n"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def info_file(*args)
|
183
|
+
unless args[0]
|
184
|
+
info_files
|
185
|
+
return
|
186
|
+
end
|
187
|
+
file = args[0]
|
188
|
+
param = args[1]
|
189
|
+
|
190
|
+
param = 'basic' unless param
|
191
|
+
subcmd = find(InfoFileSubcommands, param)
|
192
|
+
unless subcmd
|
193
|
+
errmsg "Invalid parameter #{param}\n"
|
194
|
+
return
|
195
|
+
end
|
196
|
+
|
197
|
+
unless LineCache::cached?(file)
|
198
|
+
unless LineCache::cached_script?(file)
|
199
|
+
print "File #{file} is not cached\n"
|
200
|
+
return
|
201
|
+
end
|
202
|
+
LineCache::cache(file, Command.settings[:reload_source_on_change])
|
203
|
+
end
|
204
|
+
|
205
|
+
print "File %s", file
|
206
|
+
path = LineCache.path(file)
|
207
|
+
if %w(all basic path).member?(subcmd.name) and path != file
|
208
|
+
print " - %s\n", path
|
209
|
+
else
|
210
|
+
print "\n"
|
211
|
+
end
|
212
|
+
|
213
|
+
if %w(all basic lines).member?(subcmd.name)
|
214
|
+
lines = LineCache.size(file)
|
215
|
+
print "\t %d lines\n", lines if lines
|
216
|
+
end
|
217
|
+
|
218
|
+
if %w(all breakpoints).member?(subcmd.name)
|
219
|
+
breakpoints = LineCache.trace_line_numbers(file)
|
220
|
+
if breakpoints
|
221
|
+
print "\tbreakpoint line numbers:\n"
|
222
|
+
print columnize(breakpoints.to_a.sort, self.class.settings[:width])
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
if %w(all mtime).member?(subcmd.name)
|
227
|
+
stat = LineCache.stat(file)
|
228
|
+
print "\t%s\n", stat.mtime if stat
|
229
|
+
end
|
230
|
+
if %w(all sha1).member?(subcmd.name)
|
231
|
+
print "\t%s\n", LineCache.sha1(file)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def info_files(*args)
|
236
|
+
files = LineCache::cached_files
|
237
|
+
files += SCRIPT_LINES__.keys unless 'stat' == args[0]
|
238
|
+
files.uniq.sort.each do |file|
|
239
|
+
stat = LineCache::stat(file)
|
240
|
+
path = LineCache::path(file)
|
241
|
+
print "File %s", file
|
242
|
+
if path and path != file
|
243
|
+
print " - %s\n", path
|
244
|
+
else
|
245
|
+
print "\n"
|
246
|
+
end
|
247
|
+
print "\t%s\n", stat.mtime if stat
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def info_instance_variables(*args)
|
252
|
+
unless @state.context
|
253
|
+
print "info instance_variables not available here.\n"
|
254
|
+
return
|
255
|
+
end
|
256
|
+
obj = debug_eval('self')
|
257
|
+
var_list(obj.instance_variables)
|
258
|
+
end
|
259
|
+
|
260
|
+
def info_line(*args)
|
261
|
+
unless @state.context
|
262
|
+
errmsg "info line not available here.\n"
|
263
|
+
return
|
264
|
+
end
|
265
|
+
print "Line %d of \"%s\"\n", @state.line, @state.file
|
266
|
+
end
|
267
|
+
|
268
|
+
def info_locals(*args)
|
269
|
+
unless @state.context
|
270
|
+
errmsg "info line not available here.\n"
|
271
|
+
return
|
272
|
+
end
|
273
|
+
locals = @state.context.frame_locals(@state.frame_pos)
|
274
|
+
locals.keys.sort.each do |name|
|
275
|
+
### FIXME: make a common routine
|
276
|
+
begin
|
277
|
+
s = "#{name} = #{locals[name].inspect}"
|
278
|
+
rescue
|
279
|
+
begin
|
280
|
+
s = "#{name} = #{locals[name].to_s}"
|
281
|
+
rescue
|
282
|
+
s = "*Error in evaluation*"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
if s.size > self.class.settings[:width]
|
286
|
+
s[self.class.settings[:width]-3 .. -1] = "..."
|
287
|
+
end
|
288
|
+
print "#{s}\n"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def info_program(*args)
|
293
|
+
if not @state.context
|
294
|
+
print "The program being debugged is not being run.\n"
|
295
|
+
return
|
296
|
+
elsif @state.context.dead?
|
297
|
+
print "The program crashed.\n"
|
298
|
+
if Debugger.last_exception
|
299
|
+
print("Exception: #{Debugger.last_exception.inspect}\n")
|
300
|
+
end
|
301
|
+
return
|
302
|
+
end
|
303
|
+
|
304
|
+
print "Program stopped. "
|
305
|
+
case @state.context.stop_reason
|
306
|
+
when :step
|
307
|
+
print "It stopped after stepping, next'ing or initial start.\n"
|
308
|
+
when :breakpoint
|
309
|
+
print("It stopped at a breakpoint.\n")
|
310
|
+
when :catchpoint
|
311
|
+
print("It stopped at a catchpoint.\n")
|
312
|
+
when :catchpoint
|
313
|
+
print("It stopped at a catchpoint.\n")
|
314
|
+
else
|
315
|
+
print "unknown reason: %s\n" % @state.context.stop_reason.to_s
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def info_stack(*args)
|
320
|
+
if not @state.context
|
321
|
+
errmsg "info stack not available here.\n"
|
322
|
+
return
|
323
|
+
end
|
324
|
+
(0...@state.context.stack_size).each do |idx|
|
325
|
+
if idx == @state.frame_pos
|
326
|
+
print "--> "
|
327
|
+
else
|
328
|
+
print " "
|
329
|
+
end
|
330
|
+
print_frame(idx)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def info_thread_preamble(arg)
|
335
|
+
if not @state.context
|
336
|
+
errmsg "info threads not available here.\n"
|
337
|
+
return false, false
|
338
|
+
end
|
339
|
+
verbose = if arg
|
340
|
+
subcmd = find(InfoThreadSubcommands, arg)
|
341
|
+
unless subcmd
|
342
|
+
errmsg "'terse' or 'verbose' expected. Got '#{arg}'\n"
|
343
|
+
return false, false
|
344
|
+
end
|
345
|
+
'verbose' == subcmd.name
|
346
|
+
else
|
347
|
+
false
|
348
|
+
end
|
349
|
+
return true, verbose
|
350
|
+
end
|
351
|
+
private :info_thread_preamble
|
352
|
+
|
353
|
+
def info_threads(*args)
|
354
|
+
ok, verbose = info_thread_preamble(args[0])
|
355
|
+
return unless ok
|
356
|
+
threads = Debugger.contexts.sort_by{|c| c.thnum}.each do |c|
|
357
|
+
display_context(c, !verbose)
|
358
|
+
if verbose and not c.ignored?
|
359
|
+
(0...c.stack_size).each do |idx|
|
360
|
+
print "\t"
|
361
|
+
print_frame(idx, false, c)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def info_thread(*args)
|
368
|
+
unless args[0]
|
369
|
+
info_threads(args[0])
|
370
|
+
return
|
371
|
+
end
|
372
|
+
ok, verbose = info_thread_preamble(args[1])
|
373
|
+
return unless ok
|
374
|
+
c = parse_thread_num("info thread" , args[0])
|
375
|
+
return unless c
|
376
|
+
display_context(c, !verbose)
|
377
|
+
if verbose and not c.ignored?
|
378
|
+
(0...c.stack_size).each do |idx|
|
379
|
+
print "\t"
|
380
|
+
print_frame(idx, false, c)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def info_global_variables(*args)
|
386
|
+
unless @state.context
|
387
|
+
errmsg "info global_variables not available here.\n"
|
388
|
+
return
|
389
|
+
end
|
390
|
+
var_list(global_variables)
|
391
|
+
end
|
392
|
+
|
393
|
+
def info_variables(*args)
|
394
|
+
if not @state.context
|
395
|
+
errmsg "info variables not available here.\n"
|
396
|
+
return
|
397
|
+
end
|
398
|
+
obj = debug_eval('self')
|
399
|
+
locals = @state.context.frame_locals(@state.frame_pos)
|
400
|
+
locals['self'] = @state.context.frame_self(@state.frame_pos)
|
401
|
+
locals.keys.sort.each do |name|
|
402
|
+
next if name =~ /^__dbg_/ # skip debugger pollution
|
403
|
+
### FIXME: make a common routine
|
404
|
+
begin
|
405
|
+
s = "#{name} = #{locals[name].inspect}"
|
406
|
+
rescue
|
407
|
+
begin
|
408
|
+
s = "#{name} = #{locals[name].to_s}"
|
409
|
+
rescue
|
410
|
+
s = "#{name} = *Error in evaluation*"
|
411
|
+
end
|
412
|
+
end
|
413
|
+
if s.size > self.class.settings[:width]
|
414
|
+
s[self.class.settings[:width]-3 .. -1] = "..."
|
415
|
+
end
|
416
|
+
s.gsub!('%', '%%') # protect against printf format strings
|
417
|
+
print "#{s}\n"
|
418
|
+
end
|
419
|
+
var_list(obj.instance_variables, obj.instance_eval{binding()})
|
420
|
+
var_class_self
|
421
|
+
end
|
422
|
+
|
423
|
+
class << self
|
424
|
+
def help_command
|
425
|
+
'info'
|
426
|
+
end
|
427
|
+
|
428
|
+
def help(args)
|
429
|
+
if args[1]
|
430
|
+
s = args[1]
|
431
|
+
subcmd = Subcommands.find do |try_subcmd|
|
432
|
+
(s.size >= try_subcmd.min) and
|
433
|
+
(try_subcmd.name[0..s.size-1] == s)
|
434
|
+
end
|
435
|
+
if subcmd
|
436
|
+
str = subcmd.short_help + '.'
|
437
|
+
if 'file' == subcmd.name and args[2]
|
438
|
+
s = args[2]
|
439
|
+
subsubcmd = InfoFileSubcommands.find do |try_subcmd|
|
440
|
+
(s.size >= try_subcmd.min) and
|
441
|
+
(try_subcmd.name[0..s.size-1] == s)
|
442
|
+
end
|
443
|
+
if subsubcmd
|
444
|
+
str += "\n" + subsubcmd.short_help + '.'
|
445
|
+
else
|
446
|
+
str += "\nInvalid file attribute #{args[2]}."
|
447
|
+
end
|
448
|
+
else
|
449
|
+
str += "\n" + subcmd.long_help if subcmd.long_help
|
450
|
+
end
|
451
|
+
return str
|
452
|
+
else
|
453
|
+
return "Invalid 'info' subcommand '#{args[1]}'."
|
454
|
+
end
|
455
|
+
end
|
456
|
+
s = %{
|
457
|
+
Generic command for showing things about the program being debugged.
|
458
|
+
--
|
459
|
+
List of info subcommands:
|
460
|
+
--
|
461
|
+
}
|
462
|
+
for subcmd in Subcommands do
|
463
|
+
s += "info #{subcmd.name} -- #{subcmd.short_help}\n"
|
464
|
+
end
|
465
|
+
return s
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|