| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::CLI; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 2 |  |  | 2 |  | 2542 | use strict; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 68 |  | 
| 4 | 2 |  |  | 2 |  | 12 | use warnings; | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 52 |  | 
| 5 | 2 |  |  | 2 |  | 47 | use 5.006; | 
|  | 2 |  |  |  |  | 9 |  | 
| 6 | 2 |  |  | 2 |  | 562 | use Class::Load qw( load_class ); | 
|  | 2 |  |  |  |  | 22594 |  | 
|  | 2 |  |  |  |  | 247 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | our $VERSION = '0.50'; | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | =encoding utf8 | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =head1 NAME | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | App::CLI - Dispatcher module for command line interface programs | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | package MyApp; | 
| 19 |  |  |  |  |  |  | use base 'App::CLI';        # the DISPATCHER of your App | 
| 20 |  |  |  |  |  |  | # it's not necessary to put the dispatcher | 
| 21 |  |  |  |  |  |  | # on the top level of your App | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | package main; | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | MyApp->dispatch;            # call the dispatcher where you want | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | package MyApp::List; | 
| 29 |  |  |  |  |  |  | use base qw(App::CLI::Command); # any (SUB)COMMAND of your App | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | use constant options => ( | 
| 32 |  |  |  |  |  |  | "h|help"   => "help", | 
| 33 |  |  |  |  |  |  | "verbose"  => "verbose", | 
| 34 |  |  |  |  |  |  | 'n|name=s'  => 'name', | 
| 35 |  |  |  |  |  |  | ); | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | use constant subcommands => qw(User Nickname type); # if you want subcommands | 
| 38 |  |  |  |  |  |  | # automatically dispatch to subcommands | 
| 39 |  |  |  |  |  |  | # when invoke $ myapp list [user|nickname|--type] | 
| 40 |  |  |  |  |  |  | # note 'type' is not capitalized | 
| 41 |  |  |  |  |  |  | # it is a deprecated subcommand | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | sub run { | 
| 44 |  |  |  |  |  |  | my ($self, @args) = @_; | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | print "verbose" if $self->{verbose}; | 
| 47 |  |  |  |  |  |  | my $name = $self->{name}; # get arg following long option --name | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | if ($self->{help}) { | 
| 50 |  |  |  |  |  |  | # if $ myapp list --help or $ myapp list -h | 
| 51 |  |  |  |  |  |  | # only output PODs | 
| 52 |  |  |  |  |  |  | } else { | 
| 53 |  |  |  |  |  |  | # do something when invoking $ myapp list | 
| 54 |  |  |  |  |  |  | # without subcommand and --help | 
| 55 |  |  |  |  |  |  | } | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | package MyApp::List::User; | 
| 60 |  |  |  |  |  |  | use base qw(App::CLI::Command); | 
| 61 |  |  |  |  |  |  | use constant options => ( | 
| 62 |  |  |  |  |  |  | "h|help"  =>  "help", | 
| 63 |  |  |  |  |  |  | ); | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | sub run { | 
| 66 |  |  |  |  |  |  | my ($self,@args) = @_; | 
| 67 |  |  |  |  |  |  | # code for listing user | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | pakcage MyApp::List::Nickname; | 
| 72 |  |  |  |  |  |  | use base qw(App::CLI::Command); | 
| 73 |  |  |  |  |  |  | use constant options => ( | 
| 74 |  |  |  |  |  |  | "sort=s"  =>  "sort", | 
| 75 |  |  |  |  |  |  | ); | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | sub run { | 
| 78 |  |  |  |  |  |  | my ($self,@args) = @_; | 
| 79 |  |  |  |  |  |  | # code for listing nickname | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | package MyApp::List::type;   # old genre of subcommand could not cascade infinitely | 
| 84 |  |  |  |  |  |  | use base qw(MyApp::List);    # should inherit its parent's command | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | sub run { | 
| 87 |  |  |  |  |  |  | my ($self, @args); | 
| 88 |  |  |  |  |  |  | # run to here when invoking $ myapp list --type | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | package MyApp::Help; | 
| 93 |  |  |  |  |  |  | use base 'App::CLI::Command::Help'; | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | use constant options => ( | 
| 96 |  |  |  |  |  |  | 'verbose' => 'verbose', | 
| 97 |  |  |  |  |  |  | ); | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | sub run { | 
| 100 |  |  |  |  |  |  | my ($self, @arg) = @_; | 
| 101 |  |  |  |  |  |  | # do something | 
| 102 |  |  |  |  |  |  | $self->SUPER(@_); # App::CLI::Command::Help would output POD of each command | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | C dispatches CLI (command line interface) based commands | 
| 108 |  |  |  |  |  |  | into command classes.  It also supports subcommand and per-command | 
| 109 |  |  |  |  |  |  | options. | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | =head2 Methods | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | =cut | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 2 |  |  | 2 |  | 515 | use App::CLI::Helper; | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 17 |  | 
| 116 | 2 |  |  | 2 |  | 1608 | use Getopt::Long (); | 
|  | 2 |  |  |  |  | 26300 |  | 
|  | 2 |  |  |  |  | 86 |  | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 2 |  |  | 2 |  | 26 | use constant alias          => (); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 135 |  | 
| 119 | 2 |  |  | 2 |  | 15 | use constant global_options => (); | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 100 |  | 
| 120 | 2 |  |  | 2 |  | 14 | use constant options        => (); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 2078 |  | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub new { | 
| 123 | 30 |  |  | 30 | 0 | 44819 | my ( $class, @args ) = @_; | 
| 124 | 30 |  |  |  |  | 106 | my $self = bless {@args}, $class; | 
| 125 | 30 |  |  |  |  | 128 | $self->{'app_argv'} = undef; | 
| 126 |  |  |  |  |  |  |  | 
| 127 | 30 |  |  |  |  | 122 | return $self; | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | sub app_argv { | 
| 131 | 45 |  |  | 45 | 0 | 88 | my $self = shift; | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 45 | 100 |  |  |  | 135 | if (@_) { | 
| 134 | 42 |  |  |  |  | 119 | $self->{'app_argv'} = shift; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 45 |  |  |  |  | 108 | return $self->{'app_argv'}; | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | sub prepare { | 
| 141 | 22 |  |  | 22 | 0 | 52 | my $self = shift; | 
| 142 | 22 |  |  |  |  | 54 | my $data = {}; | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 22 |  |  |  |  | 168 | $self->get_opt( | 
| 145 |  |  |  |  |  |  | [qw(no_ignore_case bundling pass_through)], | 
| 146 |  |  |  |  |  |  | opt_map( $data, $self->global_options ) | 
| 147 |  |  |  |  |  |  | ); | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 22 |  |  |  |  | 8308 | my $command_name = shift @ARGV; | 
| 150 | 22 |  |  |  |  | 103 | my $cmd = $self->get_cmd( $command_name, @_, $data ); | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 20 |  |  |  |  | 100 | while ( $cmd->cascadable ) { | 
| 153 | 6 |  |  |  |  | 34 | $cmd = $cmd->cascading; | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 20 |  |  |  |  | 150 | $self->get_opt( [qw(no_ignore_case bundling)], | 
| 157 |  |  |  |  |  |  | opt_map( $cmd, $cmd->command_options ) ); | 
| 158 |  |  |  |  |  |  |  | 
| 159 | 20 |  |  |  |  | 4684 | $cmd = $cmd->subcommand; | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 20 |  |  |  |  | 85 | return $cmd; | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | =head3 get_opt([@config], %opt_map) | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | Give options map, processed by L. | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | =cut | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | sub get_opt { | 
| 171 | 42 |  |  | 42 | 1 | 124 | my ( $self, $config, @app_options ) = @_; | 
| 172 | 42 |  |  |  |  | 241 | my $p = Getopt::Long::Parser->new; | 
| 173 | 42 |  |  |  |  | 1057 | $p->configure(@$config); | 
| 174 | 42 |  |  |  |  | 3930 | my $err = ''; | 
| 175 |  |  |  |  |  |  | local $SIG{__WARN__} = sub { | 
| 176 | 0 |  |  | 0 |  | 0 | my $msg = shift; | 
| 177 | 0 |  |  |  |  | 0 | $err .= "$msg"; | 
| 178 | 42 |  |  |  |  | 399 | }; | 
| 179 | 42 |  |  |  |  | 154 | my @current_argv = @ARGV; | 
| 180 | 42 |  |  |  |  | 196 | $self->app_argv( \@current_argv ); | 
| 181 | 42 | 50 |  |  |  | 146 | die $self->error_opt($err) unless $p->getoptions(@app_options); | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub opt_map { | 
| 185 | 42 |  |  | 42 | 0 | 164 | my ( $self, %opt ) = @_; | 
| 186 |  |  |  |  |  |  | return | 
| 187 | 42 | 50 |  |  |  | 212 | map { $_ => ref( $opt{$_} ) ? $opt{$_} : \$self->{ $opt{$_} } } keys %opt; | 
|  | 55 |  |  |  |  | 351 |  | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | =head3 dispatch(@args) | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | Interface of dispatcher | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | =cut | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | sub dispatch { | 
| 197 | 22 |  |  | 22 | 1 | 10443 | my ( $self, @args ) = @_; | 
| 198 | 22 | 100 |  |  |  | 158 | $self = $self->new unless ref $self; | 
| 199 |  |  |  |  |  |  |  | 
| 200 | 22 | 50 |  |  |  | 215 | $self->app($self) if $self->can('app'); | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 22 |  |  |  |  | 127 | my $cmd = $self->prepare(@args); | 
| 203 | 20 |  |  |  |  | 120 | $cmd->run_command(@ARGV); | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | =head3 cmd_map($cmd) | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | Find the name of the package implementing the requested command. | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | The command is first searched for in C. If the alias exists and points | 
| 211 |  |  |  |  |  |  | to a package name starting with the C<+> sign, then that package name (minus | 
| 212 |  |  |  |  |  |  | the C<+> sign) is returned. This makes it possible to map commands to arbitrary | 
| 213 |  |  |  |  |  |  | packages. | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | Otherwise, the package is searched for in the result of calling C, | 
| 216 |  |  |  |  |  |  | and a package name is constructed by upper-casing the first character of the | 
| 217 |  |  |  |  |  |  | command name, and appending it to the package name of the app itself. | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | If both of these fail, and the command does not map to any package name, | 
| 220 |  |  |  |  |  |  | C is returned instead. | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | =cut | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | sub cmd_map { | 
| 225 | 24 |  |  | 24 | 1 | 65 | my ( $self, $cmd ) = @_; | 
| 226 |  |  |  |  |  |  |  | 
| 227 | 24 |  |  |  |  | 194 | my %alias = $self->alias; | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 24 | 100 |  |  |  | 88 | if ( exists $alias{$cmd} ) { | 
| 230 | 4 |  |  |  |  | 14 | $cmd = $alias{$cmd}; | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | # Alias points to package name, return immediately | 
| 233 | 4 | 100 |  |  |  | 40 | return $cmd if $cmd =~ s/^\+//; | 
| 234 |  |  |  |  |  |  | } | 
| 235 |  |  |  |  |  |  |  | 
| 236 | 21 |  |  |  |  | 125 | ($cmd) = grep { $_ eq $cmd } $self->commands; | 
|  | 31 |  |  |  |  | 182 |  | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | # No such command | 
| 239 | 21 | 100 |  |  |  | 73 | return unless $cmd; | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 19 |  |  |  |  | 53 | my $base = ref $self; | 
| 242 | 19 |  |  |  |  | 125 | return join '::', $base, ucfirst $cmd; | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | sub error_cmd { | 
| 246 | 4 |  |  | 4 | 0 | 15 | my ( $self, $pkg ) = @_; | 
| 247 |  |  |  |  |  |  |  | 
| 248 | 4 |  |  |  |  | 9 | my $cmd; | 
| 249 | 4 | 100 |  |  |  | 16 | if ( defined($pkg) ) { | 
|  |  | 100 |  |  |  |  |  | 
| 250 | 2 |  | 33 |  |  | 14 | $cmd = ref($pkg) || $pkg; | 
| 251 |  |  |  |  |  |  | } | 
| 252 | 2 |  |  |  |  | 5 | elsif ( ${ $self->app_argv }[0] ) { | 
| 253 | 1 |  |  |  |  | 2 | $cmd = ${ $self->app_argv }[0]; | 
|  | 1 |  |  |  |  | 14 |  | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  | else { | 
| 256 | 1 |  |  |  |  | 2 | $cmd = ''; | 
| 257 |  |  |  |  |  |  | } | 
| 258 |  |  |  |  |  |  |  | 
| 259 | 4 |  |  |  |  | 70 | return "Command '$cmd' not recognized, try $0 --help.\n"; | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  |  | 
| 262 | 0 |  |  | 0 | 0 | 0 | sub error_opt { $_[1] } | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | =head3 get_cmd($cmd, @arg) | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | Return subcommand of first level via C<$ARGV[0]>. | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | =cut | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | sub get_cmd { | 
| 271 | 26 |  |  | 26 | 1 | 80 | my ( $self, $cmd, $data ) = @_; | 
| 272 | 26 | 100 | 66 |  |  | 176 | die $self->error_cmd($cmd) unless $cmd && $cmd eq lc($cmd); | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 24 |  |  |  |  | 65 | my $base = ref $self; | 
| 275 | 24 |  |  |  |  | 104 | my $pkg  = $self->cmd_map($cmd); | 
| 276 |  |  |  |  |  |  |  | 
| 277 | 24 | 100 |  |  |  | 124 | die $self->error_cmd($cmd) unless $pkg; | 
| 278 |  |  |  |  |  |  |  | 
| 279 | 22 |  |  |  |  | 174 | load_class $pkg; | 
| 280 |  |  |  |  |  |  |  | 
| 281 | 22 | 50 |  |  |  | 5143 | die $self->error_cmd($cmd) unless $pkg->can('run'); | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 22 | 100 |  |  |  | 134 | my @arg = defined $data ? %$data : (); | 
| 284 | 22 |  |  |  |  | 122 | $cmd = $pkg->new(@arg); | 
| 285 | 22 |  |  |  |  | 115 | $cmd->app($self); | 
| 286 | 22 |  |  |  |  | 79 | return $cmd; | 
| 287 |  |  |  |  |  |  | } | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | =over 4 | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | =item * | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | L | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | =item * | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | L | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | =back | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | =head1 AUTHORS | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | =over 4 | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | =item * | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | Chia-liang Kao Eclkao@clkao.orgE | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | =item * | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | Alex Vandiver  Ealexmv@bestpractical.comE | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | =item * | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | Yo-An Lin      Ecornelius.howl@gmail.comE | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | =item * | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | Shelling       Enavyblueshellingford@gmail.comE | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | =item * | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | Paul Cochrane  Epaul@liekut.deE (current maintainer) | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | =back | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | =head1 CONTRIBUTORS | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | The following people have contributed patches to the project: | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | =over 4 | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | =item * | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | José Joaquín Atria Ejjatria@gmail.comE | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | =item * | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | sunnavy            Esunnavy@gmail.comE | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | =item * | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | Ildar Shaimordanov Eildar.shaimordanov@gmail.comE | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | =back | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | Copyright 2005-2010 by Chia-liang Kao Eclkao@clkao.orgE. | 
| 353 |  |  |  |  |  |  | Copyright 2010 by Yo-An Lin Ecornelius.howl@gmail.comE | 
| 354 |  |  |  |  |  |  | and Shelling Enavyblueshellingford@gmail.comE. | 
| 355 |  |  |  |  |  |  | Copyright 2017-2018 by Paul Cochrane Epaul@liekut.deE | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | This program is free software; you can redistribute it and/or modify it | 
| 358 |  |  |  |  |  |  | under the same terms as Perl itself. | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | See L | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | =cut | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | 1; |