File Coverage

lib/Devel/Trepan/CmdProcessor/Command/Subcmd/SubsubMgr.pm
Criterion Covered Total %
statement 104 192 54.1
branch 12 58 20.6
condition 4 12 33.3
subroutine 18 27 66.6
pod n/a
total 138 289 47.7


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2011, 2013 Rocky Bernstein <rocky@cpan.org>
3              
4 12     12   87 use warnings; no warnings 'redefine';
  12     12   34  
  12         378  
  12         73  
  12         31  
  12         420  
5              
6 12     12   73 use rlib '../../../../..';
  12         31  
  12         75  
7             package Devel::Trepan::CmdProcessor::Command::SubsubcmdMgr;
8 12     12   6308 no warnings 'redefine';
  12         34  
  12         364  
9              
10 12     12   76 use File::Basename;
  12         31  
  12         826  
11 12     12   86 use File::Spec;
  12         34  
  12         304  
12              
13             ## FIXME core is not exporting properly.
14 12     12   66 use Devel::Trepan::CmdProcessor::Command::Subcmd::Core;
  12         41  
  12         292  
15              
16 12     12   67 use strict;
  12         37  
  12         357  
17             ## FIXME: @SUBCMD_ISA and @SUBCMD_VARS should come from Core.
18 12         1065 use vars qw(@ISA @EXPORT $HELP $NAME @ALIASES
19 12     12   67 @SUBCMD_ISA @SUBCMD_VARS);
  12         36  
20             @ISA = qw(Devel::Trepan::CmdProcessor::Command::Subcmd);
21 12     12   69 use vars @SUBCMD_VARS; # Value inherited from parent
  12         33  
  12         293  
22              
23 12     12   72 use constant MIN_ARGS => 0;
  12         31  
  12         929  
24 12     12   84 use constant MAX_ARGS => undef;
  12         31  
  12         491  
25 12     12   73 use constant NEED_STACK => 0;
  12         30  
  12         3615  
26              
27             # attr_accessor :subcmds # Trepan::Subcmd
28             # attr_reader :name # Name of command
29             # attr_reader :last_args # Last arguments seen
30              
31             # Initialize show subcommands. Note: instance variable name
32             # has to be setcmds ('set' + 'cmds') for subcommand completion
33             # to work.
34             sub new($$$)
35             {
36 167     167   767 my ($class, $parent, $name) = @_;
37 167         1280 my @prefix = split('::', $class);
38 167         501 shift @prefix; shift @prefix; shift @prefix; shift @prefix;
  167         516  
  167         443  
  167         466  
39             my $self = {
40             subcmds => {},
41             name => $name,
42             proc => $parent->{proc},
43             parent => $parent,
44             prefix => \@prefix,
45 167         941 cmd_str => join(' ', map {lc $_} @prefix)
  334         1952  
46             };
47             # Initialization
48 167         692 my $parent_name = ucfirst $parent->{name};
49 167         587 my $base_prefix="Devel::Trepan::CmdProcessor::Command::$parent_name";
50 167         733 my $excluded_cmd_vars = {'$HELP' => 1,
51             '$NAME'=>2};
52 167         638 for my $field (@Devel::Trepan::CmdProcessor::Command::Subcmd::SUBCMD_VARS) {
53             next if exists $excluded_cmd_vars->{$field} &&
54 1670 100 100     6934 $excluded_cmd_vars->{$field} == 2;
55 1503         3464 my $sigil = substr($field, 0, 1);
56 1503 50       4665 my $new_field = index('$@', $sigil) >= 0 ? substr($field, 1) : $field;
57 1503 100       4396 if ($sigil eq '$') {
58 1169         2580 my $lc_field = lc $new_field;
59 1169         58531 $self->{$lc_field} = eval "\$${class}::${new_field}";
60             }
61             }
62 167         9368 my @ary = eval "${class}::ALIASES()";
63 167 50       1258 $self->{aliases} = @ary ? [@ary] : [];
64 12     12   88 no strict 'refs';
  12         51  
  12         19927  
65 167         8680 my $short_help = eval "${class}::SHORT_HELP()";
66 167 50       1010 $self->{short_help} = $short_help if $short_help;
67 167         559 bless $self, $class;
68 167         1344 $self->load_debugger_subsubcommands($parent);
69 167         5291 $self;
70             }
71              
72             # Create an instance of each of the debugger subcommands. Commands are
73             # found by importing files in the directory 'name' + '_Subcmd'. Some
74             # files are excluded via an array set in initialize. For each of the
75             # remaining files, we 'require' them and scan for class names inside
76             # those files and for each class name, we will create an instance of
77             # that class. The set of TrepanCommand class instances form set of
78             # possible debugger commands.
79             sub load_debugger_subsubcommands($$)
80             {
81 167     167   619 my ($self,$parent) = @_;
82 167         1125 $self->{cmd_names} = ();
83 167         796 $self->{subcmd_names} = ();
84 167         492 $self->{cmd_basenames} = ();
85 167         5908 my $cmd_dir = dirname(__FILE__);
86 167         724 my $parent_name = ucfirst $self->{name};
87 167         533 my $cmd_name = $self->{prefix}[0];
88 167         892 my @path = ($cmd_dir, '..', "${cmd_name}_Subcmd",
89             $parent_name . '_Subcmd');
90 167         1922 my $subcmd_dir = File::Spec->catfile(@path);
91 167 50       4726 if (-d $subcmd_dir) {
92 167         20470 my @files = glob(File::Spec->catfile($subcmd_dir, '*.pm'));
93 167         1048 for my $pm (@files) {
94 431         20211 my $basename = basename($pm, '.pm');
95 431         2862 my $item = sprintf("%s::%s::%s",
96             ucfirst($cmd_name),
97             ucfirst($parent_name),
98             ucfirst($basename));
99 431 50       26062 if (-d File::Spec->catfile(dirname($pm), $basename . '_Subcmd')) {
100 0         0 push @{$self->{subcmd_names}}, $item;
  0         0  
101             } else {
102 431         1418 push @{$self->{cmd_names}}, $item;
  431         1645  
103 431         1083 push @{$self->{cmd_basenames}}, $basename;
  431         1292  
104             }
105 431         1421 my $rc=0;
106 431         1065 eval{require "$pm"; $rc=1};
  431         163782  
  431         1616  
107 431 50       2168 if ($rc eq 'Skip me!') {
    50          
108             ;
109             } elsif ($rc) {
110 431         4465 $self->setup_subsubcommand($parent, $item, $basename);
111             } else {
112 0         0 my $proc = $parent->{proc};
113 0         0 $proc->errmsg("Trouble reading ${pm}:");
114 0         0 $proc->errmsg($@);
115             }
116             }
117             }
118             }
119              
120             sub setup_subsubcommand($$$$)
121             {
122 431     431   1558 my ($self, $parent, $cmd_prefix, $name) = @_;
123 431         1296 my $parent_name = $parent->{name};
124 431         954 my $cmd_obj;
125 431         1184 my $cmd_name = lc $name;
126 431         1653 my $new_cmd = "\$cmd_obj=Devel::Trepan::CmdProcessor::Command::" .
127             "${cmd_prefix}->new(\$self, '$cmd_name'); 1";
128 431 50       30246 if (eval $new_cmd) {
129             # Add to hash of commands, and list of subcmds
130 431         1912 $self->{subcmds}{$cmd_name} = $cmd_obj;
131 431         3110 $self->add($cmd_obj, $cmd_name);
132             } else {
133 0         0 my $proc = $parent->{proc};
134 0         0 $proc->errmsg("Error instantiating ${cmd_prefix}");
135 0         0 $proc->errmsg($@);
136             }
137              
138             }
139              
140             # Find subcmd in self->subcmds
141             sub lookup($$;$)
142             {
143 0     0   0 my ($self, $subcmd_prefix, $use_regexp) = @_;
144 0 0       0 $use_regexp = 0 if scalar @_ < 3;
145 0         0 my $compare;
146 0 0       0 if (!$self->{proc}{settings}{abbrev}) {
    0          
147 0     0   0 $compare = sub($) { my $name = shift; $name eq $subcmd_prefix};
  0         0  
  0         0  
148             } elsif ($use_regexp) {
149 0     0   0 $compare = sub($) { my $name = shift; $name =~ /^${subcmd_prefix}/};
  0         0  
  0         0  
150             } else {
151             $compare = sub($) {
152 0     0   0 my $name = shift; 0 == index($name, $subcmd_prefix)
  0         0  
153 0         0 };
154             }
155 0         0 my @candidates = ();
156 0         0 while (my ($subcmd_name, $subcmd) = each %{$self->{subcmds}}) {
  0         0  
157 0 0       0 if ($compare->($subcmd_name)) {
158 0         0 push @candidates, $subcmd;
159             }
160              
161             }
162 0 0       0 if (scalar @candidates == 1) {
163 0         0 return $candidates[0];
164             }
165 0         0 return undef;
166             }
167              
168             # Show short help for a subcommand.
169             sub short_help($$$;$)
170             {
171 0     0   0 my ($self, $subcmd_cb, $subcmd_name, $label) = @_;
172 0 0       0 $label = 0 unless defined $label;
173 0         0 my $entry = $self->lookup($subcmd_name);
174 0 0       0 if ($entry) {
175 0         0 my $prefix = '';
176 0 0       0 $prefix = $entry->{name} if $label;
177 0 0       0 if (exist $entry->{short_help}) {
178 0 0       0 $prefix .= ' -- ' if $prefix;
179 0         0 $self->{proc}->msg($prefix . $entry->{short_help});
180             }
181             } else {
182 0         0 $self->{proc}->undefined_subcmd($self->{cmd_str}, $subcmd_name);
183             }
184             }
185              
186             # Add subcmd to the available subcommands for this object.
187             # It will have the supplied docstring, and subcmd_cb will be called
188             # when we want to run the command. min_len is the minimum length
189             # allowed to abbreviate the command. in_list indicates with the
190             # show command will be run when giving a list of all sub commands
191             # of this object. Some commands have long output like "show commands"
192             # so we might not want to show that.
193             sub add($$;$)
194             {
195 431     431   1431 my ($self, $subcmd_cb, $subcmd_name) = @_;
196 431   33     1389 $subcmd_name ||= $subcmd_cb->{name};
197              
198             # We keep a list of subcommands to assist command completion
199 431         882 push @{$self->{cmdlist}}, $subcmd_name;
  431         3372  
200             }
201              
202             # FIXME: remove this completely.
203             # help for subcommands
204             # Note: format of help is compatible with ddd.
205             sub help($$)
206             {
207 0     0     my ($self, $args) = @_;
208 0 0         if (scalar @$args <= 3) {
209             # "help cmd subcmd". Give the general help for the command part.
210 0           return $self->{help};
211             }
212              
213 0           my $subcmd_name = $args->[3];
214 0           my @help_text = ();
215 0           my $subcmds_ref = $self->{subcmds};
216 0           my @subcmds = $self->list();
217              
218 0 0         if ('*' eq $subcmd_name) {
219             @help_text = (sprintf("B<List of subcommands for command I<%s>:>",
220 0           $self->{cmd_str}));
221              
222 0           my $subcmds = $self->{parent}->columnize_commands(\@subcmds);
223 0           chomp $subcmds;
224 0           push @help_text, $subcmds;
225             # Double carriage return because of perlpod
226 0           return join("\n\n", @help_text);
227             }
228              
229             # "help cmd subcmd". Give help specific for that subcommand.
230 0           my $cmd = $self->lookup($subcmd_name, 0);
231 0 0         if (defined $cmd) {
232 0 0         if ($cmd->can("help")) {
233 0           return $cmd->help($args);
234             } else {
235 0           return $cmd->{help};
236             }
237             } else {
238 0           my $proc = $self->{proc};
239 0           my @matches = sort(grep /^${subcmd_name}/, @subcmds);
240 0           my $name = $self->{cmd_str};
241 0           print "HI!\n";
242 0 0         if (0 == scalar @matches) {
    0          
243 0           $proc->errmsg("No ${name} subcommands found matching /^{$subcmd_name}/. Try \"help\" $name.");
244 0           return undef;
245             } elsif (1 == scalar @matches) {
246 0           $args->[-1] = $matches[0];
247 0           $self->help($args);
248             } else {
249 0           @help_text = ("Subcommands of \"$name\" matching /^${subcmd_name}/:");
250 0           my @sort_matches = sort @matches;
251 0           push @help_text, $self->{parent}{cmd}->columnize_commands(\@sort_matches);
252 0           return @help_text;
253             }
254             }
255             }
256              
257             sub list($) {
258 0     0     my $self = shift;
259 0           sort keys %{$self->{subcmds}};
  0            
260             }
261              
262             # # Return an Array of subcommands that can start with +arg+. If none
263             # # found we just return +arg+.
264             # # FIXME: Not used any more?
265             # sub complete(prefix)
266             # Trepan::Complete.complete_token(@subcmds.subcmds.keys, prefix)
267             # }
268              
269             sub complete_token_with_next($)
270             {
271 0     0     my ($self, $prefix) = @_;
272 0           my $subcmds = $self->{subcmds};
273 0           return Devel::Trepan::Complete::complete_token_with_next($subcmds,
274             $prefix);
275             }
276              
277             sub run($$)
278             {
279 0     0     my ($self, $args) = @_;
280 0           $self->{last_args} = $args;
281             # require Enbugger; Enbugger->stop;
282 0           my $args_len = scalar @$args;
283 0 0 0       if ($args_len < 3 || $args_len == 3 && $args->[-1] eq '*') {
      0        
284 0           $self->{proc}->summary_list($self->{cmd_str}, $self->{subcmds});
285 0           return 0;
286             }
287              
288 0           my $subcmd_prefix = $args->[2];
289             # We were given: cmd subcmd subcmd ...
290             # Run that.
291 0           my $subcmd = $self->lookup($subcmd_prefix);
292 0 0         if ($subcmd) {
293 0 0         if ($self->{proc}->ok_for_running($subcmd, $subcmd->{cmd_str},
294             $args_len-3)) {
295 0           $subcmd->run($args);
296             }
297             } else {
298 0           $self->{proc}->undefined_subcmd($self->{name}, $subcmd_prefix);
299             }
300             }
301              
302             unless(caller) {
303             # Demo it.
304             require Devel::Trepan::CmdProcessor;
305             require Devel::Trepan::CmdProcessor::Command::Set;
306             my $proc = Devel::Trepan::CmdProcessor->new(undef, 'bogus');
307             my $set_cmd = Devel::Trepan::CmdProcessor::Command::Set->new($proc, 'Set');
308             my $mgr = __PACKAGE__->new($proc, $set_cmd);
309             print $mgr, "\n";
310             print join(', ', %{$mgr->{subcmds}}), "\n";
311             $set_cmd->lookup('a');
312             }
313              
314             1;