byebug 0.0.1

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