File Coverage

blib/lib/App/Cmd.pm
Criterion Covered Total %
statement 185 195 94.8
branch 35 52 67.3
condition 16 30 53.3
subroutine 48 50 96.0
pod 21 22 95.4
total 305 349 87.3


line stmt bran cond sub pod time code
1             # The "experimental" below is not actually scary. The feature went on to be
2             # de-experimental-ized with no changes and is now on by default in perl v5.24
3             # and later. -- rjbs, 2021-03-14
4 16     16   143105 use 5.020;
  16         87  
5 16     16   83 use warnings;
  16         35  
  16         522  
6 16     16   4775 use experimental qw(postderef postderef_qq);
  16         33967  
  16         134  
7              
8             package App::Cmd 0.335;
9              
10 16     16   5576 use parent 'App::Cmd::ArgProcessor';
  16         1876  
  16         127  
11             # ABSTRACT: write command line apps with less suffering
12              
13 16     16   857 use File::Basename ();
  16         33  
  16         289  
14 16     16   7861 use Module::Pluggable::Object ();
  16         153364  
  16         411  
15 16     16   7935 use Class::Load ();
  16         197414  
  16         1900  
16              
17             use Sub::Exporter -setup => {
18             collectors => {
19             -ignore => \'_setup_ignore',
20             -command => \'_setup_command',
21             -run => sub {
22 0         0 warn "using -run to run your command is deprecated\n";
23 0         0 $_[1]->{class}->run; 1
  0         0  
24             },
25             },
26 16     16   4382 };
  16         26013  
  16         228  
27              
28             sub _setup_command {
29 5     5   2897 my ($self, $val, $data) = @_;
30 5         11 my $into = $data->{into};
31              
32 5 50       56 Carp::confess "App::Cmd -command setup requested for already-setup class"
33             if $into->isa('App::Cmd::Command');
34              
35             {
36 5         10 my $base = $self->_default_command_base;
  5         15  
37 5         23 Class::Load::load_class($base);
38 16     16   10020 no strict 'refs';
  16         42  
  16         22449  
39 5         409 push @{"$into\::ISA"}, $base;
  5         66  
40             }
41              
42 5         38 $self->_register_command($into);
43              
44 5         36 for my $plugin ($self->_plugin_plugins) {
45 4         23 $plugin->import_from_plugin({ into => $into });
46             }
47              
48 5         324 1;
49             }
50              
51             sub _setup_ignore {
52 1     1   927 my ($self, $val, $data) = @_;
53 1         3 my $into = $data->{into};
54              
55 1 50       12 Carp::confess "App::Cmd -ignore setup requested for already-setup class"
56             if $into->isa('App::Cmd::Command');
57              
58 1         9 $self->_register_ignore($into);
59              
60 1         4 1;
61             }
62              
63 0     0   0 sub _plugin_plugins { return }
64              
65             #pod =head1 SYNOPSIS
66             #pod
67             #pod in F:
68             #pod
69             #pod use YourApp;
70             #pod YourApp->run;
71             #pod
72             #pod in F:
73             #pod
74             #pod package YourApp;
75             #pod use App::Cmd::Setup -app;
76             #pod 1;
77             #pod
78             #pod in F:
79             #pod
80             #pod package YourApp::Command::blort;
81             #pod use YourApp -command;
82             #pod use strict; use warnings;
83             #pod
84             #pod sub abstract { "blortex algorithm" }
85             #pod
86             #pod sub description { "Long description on blortex algorithm" }
87             #pod
88             #pod sub opt_spec {
89             #pod return (
90             #pod [ "blortex|X", "use the blortex algorithm" ],
91             #pod [ "recheck|r", "recheck all results" ],
92             #pod );
93             #pod }
94             #pod
95             #pod sub validate_args {
96             #pod my ($self, $opt, $args) = @_;
97             #pod
98             #pod # no args allowed but options!
99             #pod $self->usage_error("No args allowed") if @$args;
100             #pod }
101             #pod
102             #pod sub execute {
103             #pod my ($self, $opt, $args) = @_;
104             #pod
105             #pod my $result = $opt->{blortex} ? blortex() : blort();
106             #pod
107             #pod recheck($result) if $opt->{recheck};
108             #pod
109             #pod print $result;
110             #pod }
111             #pod
112             #pod and, finally, at the command line:
113             #pod
114             #pod knight!rjbs$ yourcmd blort --recheck
115             #pod
116             #pod All blorts successful.
117             #pod
118             #pod =head1 DESCRIPTION
119             #pod
120             #pod App::Cmd is intended to make it easy to write complex command-line applications
121             #pod without having to think about most of the annoying things usually involved.
122             #pod
123             #pod For information on how to start using App::Cmd, see L.
124             #pod
125             #pod =method new
126             #pod
127             #pod my $cmd = App::Cmd->new(\%arg);
128             #pod
129             #pod This method returns a new App::Cmd object. During initialization, command
130             #pod plugins will be loaded.
131             #pod
132             #pod Valid arguments are:
133             #pod
134             #pod no_commands_plugin - if true, the command list plugin is not added
135             #pod
136             #pod no_help_plugin - if true, the help plugin is not added
137             #pod
138             #pod no_version_plugin - if true, the version plugin is not added
139             #pod
140             #pod show_version_cmd - if true, the version command will be shown in the
141             #pod command list
142             #pod
143             #pod plugin_search_path - The path to search for commands in. Defaults to
144             #pod results of plugin_search_path method
145             #pod
146             #pod If C is not given, L will be
147             #pod required, and it will be registered to handle all of its command names not
148             #pod handled by other plugins.
149             #pod
150             #pod If C is not given, L will be required,
151             #pod and it will be registered to handle all of its command names not handled by
152             #pod other plugins. B "help" is the default command, so if you do not load
153             #pod the default help plugin, you should provide your own or override the
154             #pod C method.
155             #pod
156             #pod If C is not given, L will be
157             #pod required to show the application's version with command C<--version>. By
158             #pod default, the version command is not included in the command list. Pass
159             #pod C to include the version command in the list.
160             #pod
161             #pod =cut
162              
163             sub new {
164 24     24 1 5825 my ($class, $arg) = @_;
165              
166 24         75 my $arg0 = $0;
167 24         1293 my $base = File::Basename::basename $arg0;
168              
169             my $self = {
170             arg0 => $base,
171             full_arg0 => $arg0,
172 24   100     253 show_version => $arg->{show_version_cmd} // 0,
173             };
174              
175 24         68 bless $self, $class;
176              
177 24         137 $self->{command} = $self->_command($arg);
178              
179 24         118 return $self;
180             }
181              
182             # effectively, returns the command-to-plugin mapping guts of a Cmd
183             # if called on a class or on a Cmd with no mapping, construct a new hashref
184             # suitable for use as the object's mapping
185             sub _command {
186 169     169   307 my ($self, $arg) = @_;
187 169 100 66     1419 return $self->{command} if ref $self and $self->{command};
188              
189             # TODO _default_command_base can be wrong if people are not using
190             # ::Setup and have no ::Command :(
191             #
192             # my $want_isa = $self->_default_command_base;
193             # -- kentnl, 2010-12
194 28         58 my $want_isa = 'App::Cmd::Command';
195              
196 28         50 my %plugin;
197 28         113 for my $plugin ($self->_plugins) {
198              
199 80         292 Class::Load::load_class($plugin);
200              
201             # relies on either the plugin itself registering as ignored
202             # during compile ( use MyApp::Cmd -ignore )
203             # or being explicitly registered elsewhere ( blacklisted )
204             # via $app_cmd->_register_ignore( $class )
205             # -- kentnl, 2011-09
206 80 50       186622 next if $self->should_ignore( $plugin );
207              
208 80 50       495 die "$plugin is not a " . $want_isa
209             unless $plugin->isa($want_isa);
210              
211 80 50       451 next unless $plugin->can("command_names");
212              
213 80         249 foreach my $command (map { lc } $plugin->command_names) {
  92         331  
214             die "two plugins for command $command: $plugin and $plugin{$command}\n"
215 92 50       250 if exists $plugin{$command};
216              
217 92         260 $plugin{$command} = $plugin;
218             }
219             }
220              
221 28         197 $self->_load_default_plugin($_, $arg, \%plugin) for qw(commands help version);
222              
223 28 100       174 if ($self->allow_any_unambiguous_abbrev) {
224             # add abbreviations to list of authorized commands
225 1         731 require Text::Abbrev;
226 1         61 my %abbrev = Text::Abbrev::abbrev( keys %plugin );
227 1         70 @plugin{ keys %abbrev } = @plugin{ values %abbrev };
228             }
229              
230 28         105 return \%plugin;
231             }
232              
233             # ->_plugins won't be called more than once on any given App::Cmd, but since
234             # finding plugins can be a bit expensive, we'll do a lousy cache here.
235             # -- rjbs, 2007-10-09
236             my %plugins_for;
237             sub _plugins {
238 30     30   832 my ($self) = @_;
239 30   66     127 my $class = ref $self || $self;
240              
241 30 100       135 return $plugins_for{$class}->@* if $plugins_for{$class};
242              
243 11         79 my $finder = Module::Pluggable::Object->new(
244             search_path => $self->plugin_search_path,
245             $self->_module_pluggable_options,
246             );
247              
248 11         189 my @plugins = $finder->plugins;
249 11         20766 $plugins_for{$class} = \@plugins;
250              
251 11         192 return @plugins;
252             }
253              
254             sub _register_command {
255 5     5   13 my ($self, $cmd_class) = @_;
256 5         15 $self->_plugins;
257              
258 5   33     24 my $class = ref $self || $self;
259             push $plugins_for{ $class }->@*, $cmd_class
260 5 50       14 unless grep { $_ eq $cmd_class } $plugins_for{ $class }->@*;
  7         42  
261             }
262              
263             my %ignored_for;
264              
265             sub should_ignore {
266 80     80 0 175 my ($self, $cmd_class) = @_;
267 80   33     237 my $class = ref $self || $self;
268 80         212 for ($ignored_for{ $class }->@*) {
269 0 0       0 return 1 if $_ eq $cmd_class;
270             }
271 80         199 return;
272             }
273              
274             sub _register_ignore {
275 1     1   4 my ($self, $cmd_class) = @_;
276 1   33     15 my $class = ref $self || $self;
277             push $ignored_for{ $class }->@*, $cmd_class
278 1 50       11 unless grep { $_ eq $cmd_class } $ignored_for{ $class }->@*;
  0         0  
279             }
280              
281             sub _module_pluggable_options {
282             # my ($self) = @_; # no point in creating these ops, just to toss $self
283 11     11   131 return;
284             }
285              
286             # load one of the stock plugins, unless requested to squash; unlike normal
287             # plugin loading, command-to-plugin mapping conflicts are silently ignored
288             sub _load_default_plugin {
289 84     84   219 my ($self, $plugin_name, $arg, $plugin_href) = @_;
290 84 100       291 unless ($arg->{"no_$plugin_name\_plugin"}) {
291 77         187 my $plugin = "App::Cmd::Command::$plugin_name";
292 77         209 Class::Load::load_class($plugin);
293 77         3974 for my $command (map { lc } $plugin->command_names) {
  185         432  
294 185   33     846 $plugin_href->{$command} //= $plugin;
295             }
296             }
297             }
298              
299             #pod =method run
300             #pod
301             #pod $cmd->run;
302             #pod
303             #pod This method runs the application. If called the class, it will instantiate a
304             #pod new App::Cmd object to run.
305             #pod
306             #pod It determines the requested command (generally by consuming the first
307             #pod command-line argument), finds the plugin to handle that command, parses the
308             #pod remaining arguments according to that plugin's rules, and runs the plugin.
309             #pod
310             #pod It passes the contents of the global argument array (C<@ARGV>) to
311             #pod L>, but C<@ARGV> is not altered by running an App::Cmd.
312             #pod
313             #pod =cut
314              
315             sub run {
316 23     23 1 2320 my ($self) = @_;
317              
318             # We should probably use Class::Default.
319 23 50       95 $self = $self->new unless ref $self;
320              
321             # prepare the command we're going to run...
322 23         97 my @argv = $self->prepare_args();
323 23         102 my ($cmd, $opt, @args) = $self->prepare_command(@argv);
324              
325             # ...and then run it
326 22         120 $self->execute_command($cmd, $opt, @args);
327             }
328              
329             #pod =method prepare_args
330             #pod
331             #pod Normally App::Cmd uses C<@ARGV> for its commandline arguments. You can override
332             #pod this method to change that behavior for testing or otherwise.
333             #pod
334             #pod =cut
335              
336             sub prepare_args {
337 23     23 1 56 my ($self) = @_;
338 23 100       105 return scalar(@ARGV)
339             ? (@ARGV)
340             : ($self->default_args->@*);
341             }
342              
343             #pod =method default_args
344             #pod
345             #pod If C> is not changed and there are no arguments in C<@ARGV>,
346             #pod this method is called and should return an arrayref to be used as the arguments
347             #pod to the program. By default, it returns an empty arrayref.
348             #pod
349             #pod =cut
350              
351 16     16   137 use constant default_args => [];
  16         35  
  16         28520  
352              
353             #pod =method abstract
354             #pod
355             #pod sub abstract { "command description" }
356             #pod
357             #pod Defines the command abstract: a short description that will be printed in the
358             #pod main command options list.
359             #pod
360             #pod =method description
361             #pod
362             #pod sub description { "Long description" }
363             #pod
364             #pod Defines a longer command description that will be shown when the user asks for
365             #pod help on a specific command.
366             #pod
367             #pod =method arg0
368             #pod
369             #pod =method full_arg0
370             #pod
371             #pod my $program_name = $app->arg0;
372             #pod
373             #pod my $full_program_name = $app->full_arg0;
374             #pod
375             #pod These methods return the name of the program invoked to run this application.
376             #pod This is determined by inspecting C<$0> when the App::Cmd object is
377             #pod instantiated, so it's probably correct, but doing weird things with App::Cmd
378             #pod could lead to weird values from these methods.
379             #pod
380             #pod If the program was run like this:
381             #pod
382             #pod knight!rjbs$ ~/bin/rpg dice 3d6
383             #pod
384             #pod Then the methods return:
385             #pod
386             #pod arg0 - rpg
387             #pod full_arg0 - /Users/rjbs/bin/rpg
388             #pod
389             #pod These values are captured when the App::Cmd object is created, so it is safe to
390             #pod assign to C<$0> later.
391             #pod
392             #pod =cut
393              
394 1     1 1 6 sub arg0 { $_[0]->{arg0} }
395 1     1 1 8 sub full_arg0 { $_[0]->{full_arg0} }
396              
397             #pod =method prepare_command
398             #pod
399             #pod my ($cmd, $opt, @args) = $app->prepare_command(@ARGV);
400             #pod
401             #pod This method will load the plugin for the requested command, use its options to
402             #pod parse the command line arguments, and eventually return everything necessary to
403             #pod actually execute the command.
404             #pod
405             #pod =cut
406              
407             sub prepare_command {
408 30     30 1 162 my ($self, @args) = @_;
409              
410             # figure out first-level dispatch
411 30         127 my ($command, $opt, @sub_args) = $self->get_command(@args);
412              
413             # set up the global options (which we just determined)
414 30         166 $self->set_global_options($opt);
415              
416             # find its plugin or else call default plugin (default default is help)
417 30 50       79 if ($command) {
418 30         145 $self->_prepare_command($command, $opt, @sub_args);
419             } else {
420 0         0 $self->_prepare_default_command($opt, @sub_args);
421             }
422             }
423              
424             sub _prepare_command {
425 32     32   96 my ($self, $command, $opt, @args) = @_;
426 32 100       111 if (my $plugin = $self->plugin_for($command)) {
427 31         347 return $plugin->prepare($self, @args);
428             } else {
429 1         10 return $self->_bad_command($command, $opt, @args);
430             }
431             }
432              
433             sub _prepare_default_command {
434 1     1   3 my ($self, $opt, @sub_args) = @_;
435 1         7 $self->_prepare_command($self->default_command, $opt, @sub_args);
436             }
437              
438             sub _bad_command {
439 1     1   3 my ($self, $command, $opt, @args) = @_;
440 1 50       10 print "Unrecognized command: $command.\n\nUsage:\n" if defined($command);
441              
442             # This should be class data so that, in Bizarro World, two App::Cmds will not
443             # conflict.
444 1         19 our $_bad++;
445 1         6 $self->prepare_command(qw(commands --stderr));
446             }
447              
448 16 50   16   23637 END { exit 1 if our $_bad };
449              
450             #pod =method default_command
451             #pod
452             #pod This method returns the name of the command to run if none is given on the
453             #pod command line. The default default is "help"
454             #pod
455             #pod =cut
456              
457 1     1 1 3 sub default_command { "help" }
458              
459             #pod =method execute_command
460             #pod
461             #pod $app->execute_command($cmd, \%opt, @args);
462             #pod
463             #pod This method will invoke C and then C on C<$cmd>.
464             #pod
465             #pod =cut
466              
467             sub execute_command {
468 22     22 1 103 my ($self, $cmd, $opt, @args) = @_;
469              
470 22         45 local our $active_cmd = $cmd;
471              
472 22         144 $cmd->validate_args($opt, \@args);
473 21         105 $cmd->execute($opt, \@args);
474             }
475              
476             #pod =method plugin_search_path
477             #pod
478             #pod This method returns the plugin_search_path as set. The default implementation,
479             #pod if called on "YourApp::Cmd" will return "YourApp::Cmd::Command"
480             #pod
481             #pod This is a method because it's fun to override it with, for example:
482             #pod
483             #pod use constant plugin_search_path => __PACKAGE__;
484             #pod
485             #pod =cut
486              
487             sub _default_command_base {
488 25     25   59 my ($self) = @_;
489 25   66     91 my $class = ref $self || $self;
490 25         95 return "$class\::Command";
491             }
492              
493             sub _default_plugin_base {
494 10     10   26 my ($self) = @_;
495 10   66     43 my $class = ref $self || $self;
496 10         50 return "$class\::Plugin";
497             }
498              
499             sub plugin_search_path {
500 10     10 1 61 my ($self) = @_;
501              
502 10         51 my $dcb = $self->_default_command_base;
503 10 100       57 my $ccb = $dcb eq 'App::Cmd::Command'
504             ? $self->App::Cmd::_default_command_base
505             : $self->_default_command_base;
506              
507 10         51 my @default = ($ccb, $self->_default_plugin_base);
508              
509 10 100       53 if (ref $self) {
510 8   50     122 return $self->{plugin_search_path} //= \@default;
511             } else {
512 2         25 return \@default;
513             }
514             }
515              
516             #pod =method allow_any_unambiguous_abbrev
517             #pod
518             #pod If this method returns true (which, by default, it does I), then any
519             #pod unambiguous abbreviation for a registered command name will be allowed as a
520             #pod means to use that command. For example, given the following commands:
521             #pod
522             #pod reticulate
523             #pod reload
524             #pod rasterize
525             #pod
526             #pod Then the user could use C for C or C for C and
527             #pod so on.
528             #pod
529             #pod =cut
530              
531 27     27 1 137 sub allow_any_unambiguous_abbrev { return 0 }
532              
533             #pod =method global_options
534             #pod
535             #pod if ($cmd->app->global_options->{verbose}) { ... }
536             #pod
537             #pod This method returns the running application's global options as a hashref. If
538             #pod there are no options specified, an empty hashref is returned.
539             #pod
540             #pod =cut
541              
542             sub global_options {
543 2     2 1 12 my $self = shift;
544 2 50 50     22 return $self->{global_options} //= {} if ref $self;
545 0         0 return {};
546             }
547              
548             #pod =method set_global_options
549             #pod
550             #pod $app->set_global_options(\%opt);
551             #pod
552             #pod This method sets the global options.
553             #pod
554             #pod =cut
555              
556             sub set_global_options {
557 32     32 1 73 my ($self, $opt) = @_;
558 32         75 return $self->{global_options} = $opt;
559             }
560              
561             #pod =method command_names
562             #pod
563             #pod my @names = $cmd->command_names;
564             #pod
565             #pod This returns the commands names which the App::Cmd object will handle.
566             #pod
567             #pod =cut
568              
569             sub command_names {
570 6     6 1 657 my ($self) = @_;
571 6         34 keys $self->_command->%*;
572             }
573              
574             #pod =method command_groups
575             #pod
576             #pod my @groups = $cmd->commands_groups;
577             #pod
578             #pod This method can be implemented to return a grouped list of command names with
579             #pod optional headers. Each group is given as arrayref and each header as string.
580             #pod If an empty list is returned, the commands plugin will show two groups without
581             #pod headers: the first group is for the "help" and "commands" commands, and all
582             #pod other commands are in the second group.
583             #pod
584             #pod =cut
585              
586       4 1   sub command_groups { }
587              
588             #pod =method command_plugins
589             #pod
590             #pod my @plugins = $cmd->command_plugins;
591             #pod
592             #pod This method returns the package names of the plugins that implement the
593             #pod App::Cmd object's commands.
594             #pod
595             #pod =cut
596              
597             sub command_plugins {
598 8     8 1 32 my ($self) = @_;
599 8         28 my %seen = map {; $_ => 1 } values $self->_command->%*;
  90         165  
600 8         75 keys %seen;
601             }
602              
603             #pod =method plugin_for
604             #pod
605             #pod my $plugin = $cmd->plugin_for($command);
606             #pod
607             #pod This method returns the plugin (module) for the given command. If no plugin
608             #pod implements the command, it returns false.
609             #pod
610             #pod =cut
611              
612             sub plugin_for {
613 67     67 1 145 my ($self, $command) = @_;
614 67 50       164 return unless $command;
615 67 100       213 return unless exists $self->_command->{ $command };
616              
617 64         149 return $self->_command->{ $command };
618             }
619              
620             #pod =method get_command
621             #pod
622             #pod my ($command_name, $opt, @args) = $app->get_command(@args);
623             #pod
624             #pod Process arguments and into a command name and (optional) global options.
625             #pod
626             #pod =cut
627              
628             sub get_command {
629 32     32 1 80 my ($self, @args) = @_;
630              
631 32         157 my ($opt, $args, %fields)
632             = $self->_process_args(\@args, $self->_global_option_processing_params);
633              
634             # map --help to help command
635 32 100       272 if ($opt->{help}) {
636 2         7 unshift @$args, 'help';
637 2         6 delete $opt->{help};
638             }
639              
640 32         189 my ($command, $rest) = $self->_cmd_from_args($args);
641              
642 32         109 $self->{usage} = $fields{usage};
643              
644 32         147 return ($command, $opt, @$rest);
645             }
646              
647             sub _cmd_from_args {
648 27     27   68 my ($self, $args) = @_;
649              
650 27         59 my $command = shift @$args;
651 27         77 return ($command, $args);
652             }
653              
654             sub _global_option_processing_params {
655 32     32   77 my ($self, @args) = @_;
656              
657             return (
658 32         135 $self->usage_desc(@args),
659             $self->global_opt_spec(@args),
660             { getopt_conf => [qw/pass_through/] },
661             );
662             }
663              
664             #pod =method usage
665             #pod
666             #pod print $self->app->usage->text;
667             #pod
668             #pod Returns the usage object for the global options.
669             #pod
670             #pod =cut
671              
672 1     1 1 6 sub usage { $_[0]{usage} };
673              
674             #pod =method usage_desc
675             #pod
676             #pod The top level usage line. Looks something like
677             #pod
678             #pod "yourapp [options]"
679             #pod
680             #pod =cut
681              
682             sub usage_desc {
683             # my ($self) = @_; # no point in creating these ops, just to toss $self
684 30     30 1 115 return "%c %o";
685             }
686              
687             #pod =method global_opt_spec
688             #pod
689             #pod Returns a list with help command unless C has been specified or
690             #pod an empty list. Can be overridden for pre-dispatch option processing. This is
691             #pod useful for flags like --verbose.
692             #pod
693             #pod =cut
694              
695             sub global_opt_spec {
696 28     28 1 62 my ($self) = @_;
697              
698 28         53 my $cmd = $self->{command};
699 28         41 my %seen;
700 112         306 my @help = grep { ! $seen{$_}++ }
701 112         325 reverse sort map { s/^--?//; $_ }
  112         293  
702 28         134 grep { $cmd->{$_} eq 'App::Cmd::Command::help' } keys %$cmd;
  306         544  
703              
704 28 50       379 return (@help ? [ join('|', @help) => "show help" ] : ());
705             }
706              
707             #pod =method usage_error
708             #pod
709             #pod $self->usage_error("Something's wrong!");
710             #pod
711             #pod Used to die with nice usage output, during C.
712             #pod
713             #pod =cut
714              
715             sub usage_error {
716 0     0 1 0 my ($self, $error) = @_;
717 0         0 die "Error: $error\nUsage: " . $self->_usage_text;
718             }
719              
720             sub _usage_text {
721 1     1   3 my ($self) = @_;
722 1         15 my $text = $self->usage->text;
723 1         185 $text =~ s/\A(\s+)/!/;
724 1         4 return $text;
725             }
726              
727             #pod =head1 TODO
728             #pod
729             #pod =for :list
730             #pod * publish and bring in Log::Speak (simple quiet/verbose output)
731             #pod * publish and use our internal enhanced describe_options
732             #pod * publish and use our improved simple input routines
733             #pod
734             #pod =cut
735              
736             1;
737              
738             __END__