byebug 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.
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