File Coverage

blib/lib/App/Cpan.pm
Criterion Covered Total %
statement 136 519 26.2
branch 12 142 8.4
condition 10 40 25.0
subroutine 44 95 46.3
pod 1 1 100.0
total 203 797 25.4


line stmt bran cond sub pod time code
1             package App::Cpan;
2              
3 4     4   73588 use 5.008;
  4         79  
  4         272  
4 4     4   30 use strict;
  4         9  
  4         295  
5 4     4   31 use warnings;
  4         9  
  4         234  
6 4     4   28 use vars qw($VERSION);
  4         8  
  4         1117  
7              
8             $VERSION = '1.61_03';
9              
10             =head1 NAME
11              
12             App::Cpan - easily interact with CPAN from the command line
13              
14             =head1 SYNOPSIS
15              
16             # with arguments and no switches, installs specified modules
17             cpan module_name [ module_name ... ]
18              
19             # with switches, installs modules with extra behavior
20             cpan [-cfFimtTw] module_name [ module_name ... ]
21              
22             # use local::lib
23             cpan -I module_name [ module_name ... ]
24              
25             # one time mirror override for faster mirrors
26             cpan -p ...
27              
28             # with just the dot, install from the distribution in the
29             # current directory
30             cpan .
31              
32             # without arguments, starts CPAN.pm shell
33             cpan
34              
35             # without arguments, but some switches
36             cpan [-ahpruvACDLOP]
37              
38             =head1 DESCRIPTION
39              
40             This script provides a command interface (not a shell) to CPAN. At the
41             moment it uses CPAN.pm to do the work, but it is not a one-shot command
42             runner for CPAN.pm.
43              
44             =head2 Options
45              
46             =over 4
47              
48             =item -a
49              
50             Creates a CPAN.pm autobundle with CPAN::Shell->autobundle.
51              
52             =item -A module [ module ... ]
53              
54             Shows the primary maintainers for the specified modules.
55              
56             =item -c module
57              
58             Runs a `make clean` in the specified module's directories.
59              
60             =item -C module [ module ... ]
61              
62             Show the F files for the specified modules
63              
64             =item -D module [ module ... ]
65              
66             Show the module details. This prints one line for each out-of-date module
67             (meaning, modules locally installed but have newer versions on CPAN).
68             Each line has three columns: module name, local version, and CPAN
69             version.
70              
71             =item -f
72              
73             Force the specified action, when it normally would have failed. Use this
74             to install a module even if its tests fail. When you use this option,
75             -i is not optional for installing a module when you need to force it:
76              
77             % cpan -f -i Module::Foo
78              
79             =item -F
80              
81             Turn off CPAN.pm's attempts to lock anything. You should be careful with
82             this since you might end up with multiple scripts trying to muck in the
83             same directory. This isn't so much of a concern if you're loading a special
84             config with C<-j>, and that config sets up its own work directories.
85              
86             =item -g module [ module ... ]
87              
88             Downloads to the current directory the latest distribution of the module.
89              
90             =item -G module [ module ... ]
91              
92             UNIMPLEMENTED
93              
94             Download to the current directory the latest distribution of the
95             modules, unpack each distribution, and create a git repository for each
96             distribution.
97              
98             If you want this feature, check out Yanick Champoux's C
99             distribution.
100              
101             =item -h
102              
103             Print a help message and exit. When you specify C<-h>, it ignores all
104             of the other options and arguments.
105              
106             =item -i
107              
108             Install the specified modules. With no other switches, this switch
109             is implied.
110              
111             =item -I
112              
113             Load C (think like C<-I> for loading lib paths). Too bad
114             C<-l> was already taken.
115              
116             =item -j Config.pm
117              
118             Load the file that has the CPAN configuration data. This should have the
119             same format as the standard F file, which defines
120             C<$CPAN::Config> as an anonymous hash.
121              
122             =item -J
123              
124             Dump the configuration in the same format that CPAN.pm uses. This is useful
125             for checking the configuration as well as using the dump as a starting point
126             for a new, custom configuration.
127              
128             =item -l
129              
130             List all installed modules wth their versions
131              
132             =item -L author [ author ... ]
133              
134             List the modules by the specified authors.
135              
136             =item -m
137              
138             Make the specified modules.
139              
140             =item -M mirror1,mirror2,...
141              
142             A comma-separated list of mirrors to use for just this run. The C<-P>
143             option can find them for you automatically.
144              
145             =item -n
146              
147             Do a dry run, but don't actually install anything. (unimplemented)
148              
149             =item -O
150              
151             Show the out-of-date modules.
152              
153             =item -p
154              
155             Ping the configured mirrors and print a report
156              
157             =item -P
158              
159             Find the best mirrors you could be using and use them for the current
160             session.
161              
162             =item -r
163              
164             Recompiles dynamically loaded modules with CPAN::Shell->recompile.
165              
166             =item -t
167              
168             Run a `make test` on the specified modules.
169              
170             =item -T
171              
172             Do not test modules. Simply install them.
173              
174             =item -u
175              
176             Upgrade all installed modules. Blindly doing this can really break things,
177             so keep a backup.
178              
179             =item -v
180              
181             Print the script version and CPAN.pm version then exit.
182              
183             =item -V
184              
185             Print detailed information about the cpan client.
186              
187             =item -w
188              
189             UNIMPLEMENTED
190              
191             Turn on cpan warnings. This checks various things, like directory permissions,
192             and tells you about problems you might have.
193              
194             =back
195              
196             =head2 Examples
197              
198             # print a help message
199             cpan -h
200              
201             # print the version numbers
202             cpan -v
203              
204             # create an autobundle
205             cpan -a
206              
207             # recompile modules
208             cpan -r
209              
210             # upgrade all installed modules
211             cpan -u
212              
213             # install modules ( sole -i is optional )
214             cpan -i Netscape::Booksmarks Business::ISBN
215              
216             # force install modules ( must use -i )
217             cpan -fi CGI::Minimal URI
218              
219              
220             =head2 Methods
221              
222             =over 4
223              
224             =cut
225              
226 4     4   5833 use autouse Carp => qw(carp croak cluck);
  4         6812  
  4         38  
227 4     4   8754 use CPAN ();
  4         1659113  
  4         515  
228 4     4   120 use Config;
  4         55  
  4         412  
229 4     4   46 use autouse Cwd => qw(cwd);
  4         22  
  4         59  
230 4     4   985 use autouse 'Data::Dumper' => qw(Dumper);
  4         12  
  4         38  
231 4     4   6015 use File::Spec::Functions;
  4         5201  
  4         477  
232 4     4   51 use File::Basename;
  4         13  
  4         438  
233 4     4   12398 use Getopt::Std;
  4         263  
  4         396  
234              
235             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
236             # Internal constants
237 4     4   26 use constant TRUE => 1;
  4         20  
  4         467  
238 4     4   33 use constant FALSE => 0;
  4         9  
  4         220  
239              
240              
241             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
242             # The return values
243 4     4   26 use constant HEY_IT_WORKED => 0;
  4         13  
  4         227  
244 4     4   27 use constant I_DONT_KNOW_WHAT_HAPPENED => 1; # 0b0000_0001
  4         13  
  4         186  
245 4     4   29 use constant ITS_NOT_MY_FAULT => 2;
  4         13  
  4         185  
246 4     4   24 use constant THE_PROGRAMMERS_AN_IDIOT => 4;
  4         8  
  4         290  
247 4     4   27 use constant A_MODULE_FAILED_TO_INSTALL => 8;
  4         10  
  4         257  
248              
249              
250             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
251             # set up the order of options that we layer over CPAN::Shell
252             BEGIN { # most of this should be in methods
253 4         3042 use vars qw( @META_OPTIONS $Default %CPAN_METHODS @CPAN_OPTIONS @option_order
254 4     4   33 %Method_table %Method_table_index );
  4         71  
255              
256 4     4   27 @META_OPTIONS = qw( h v V I g G M: C A D O l L a r p P j: J w T);
257              
258 4         8 $Default = 'default';
259              
260 4         109 %CPAN_METHODS = ( # map switches to method names in CPAN::Shell
261             $Default => 'install',
262             'c' => 'clean',
263             'f' => 'force',
264             'i' => 'install',
265             'm' => 'make',
266             't' => 'test',
267             'u' => 'upgrade',
268             );
269 4         46 @CPAN_OPTIONS = grep { $_ ne $Default } sort keys %CPAN_METHODS;
  28         62  
270              
271 4         25 @option_order = ( @META_OPTIONS, @CPAN_OPTIONS );
272              
273              
274             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
275             # map switches to the subroutines in this script, along with other information.
276             # use this stuff instead of hard-coded indices and values
277             sub NO_ARGS () { 0 }
278             sub ARGS () { 1 }
279             sub GOOD_EXIT () { 0 }
280              
281 4         307 %Method_table = (
282             # key => [ sub ref, takes args?, exit value, description ]
283              
284             # options that do their thing first, then exit
285             h => [ \&_print_help, NO_ARGS, GOOD_EXIT, 'Printing help' ],
286             v => [ \&_print_version, NO_ARGS, GOOD_EXIT, 'Printing version' ],
287             V => [ \&_print_details, NO_ARGS, GOOD_EXIT, 'Printing detailed version' ],
288              
289             # options that affect other options
290             j => [ \&_load_config, ARGS, GOOD_EXIT, 'Use specified config file' ],
291             J => [ \&_dump_config, NO_ARGS, GOOD_EXIT, 'Dump configuration to stdout' ],
292             F => [ \&_lock_lobotomy, NO_ARGS, GOOD_EXIT, 'Turn off CPAN.pm lock files' ],
293             I => [ \&_load_local_lib, NO_ARGS, GOOD_EXIT, 'Loading local::lib' ],
294             M => [ \&_use_these_mirrors, ARGS, GOOD_EXIT, 'Setting per session mirrors' ],
295             P => [ \&_find_good_mirrors, NO_ARGS, GOOD_EXIT, 'Finding good mirrors' ],
296             w => [ \&_turn_on_warnings, NO_ARGS, GOOD_EXIT, 'Turning on warnings' ],
297             T => [ \&_turn_off_testing, NO_ARGS, GOOD_EXIT, 'Turning off testing' ],
298              
299             # options that do their one thing
300             g => [ \&_download, NO_ARGS, GOOD_EXIT, 'Download the latest distro' ],
301             G => [ \&_gitify, NO_ARGS, GOOD_EXIT, 'Down and gitify the latest distro' ],
302              
303             C => [ \&_show_Changes, ARGS, GOOD_EXIT, 'Showing Changes file' ],
304             A => [ \&_show_Author, ARGS, GOOD_EXIT, 'Showing Author' ],
305             D => [ \&_show_Details, ARGS, GOOD_EXIT, 'Showing Details' ],
306             O => [ \&_show_out_of_date, NO_ARGS, GOOD_EXIT, 'Showing Out of date' ],
307             l => [ \&_list_all_mods, NO_ARGS, GOOD_EXIT, 'Listing all modules' ],
308              
309             L => [ \&_show_author_mods, ARGS, GOOD_EXIT, 'Showing author mods' ],
310             a => [ \&_create_autobundle, NO_ARGS, GOOD_EXIT, 'Creating autobundle' ],
311             p => [ \&_ping_mirrors, NO_ARGS, GOOD_EXIT, 'Pinging mirrors' ],
312              
313             r => [ \&_recompile, NO_ARGS, GOOD_EXIT, 'Recompiling' ],
314             u => [ \&_upgrade, NO_ARGS, GOOD_EXIT, 'Running `make test`' ],
315              
316             c => [ \&_default, ARGS, GOOD_EXIT, 'Running `make clean`' ],
317             f => [ \&_default, ARGS, GOOD_EXIT, 'Installing with force' ],
318             i => [ \&_default, ARGS, GOOD_EXIT, 'Running `make install`' ],
319             'm' => [ \&_default, ARGS, GOOD_EXIT, 'Running `make`' ],
320             t => [ \&_default, ARGS, GOOD_EXIT, 'Running `make test`' ],
321             );
322              
323 4         165 %Method_table_index = (
324             code => 0,
325             takes_args => 1,
326             exit_value => 2,
327             description => 3,
328             );
329             }
330              
331              
332             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
333             # finally, do some argument processing
334              
335             sub _stupid_interface_hack_for_non_rtfmers
336             {
337 4     4   35 no warnings 'uninitialized';
  4         8  
  4         2303  
338 4 100 100 4   3423 shift @ARGV if( $ARGV[0] eq 'install' and @ARGV > 1 )
339             }
340              
341             sub _process_options
342             {
343 1     1   17 my %options;
344              
345 1   50     12 push @ARGV, grep $_, split /\s+/, $ENV{CPAN_OPTS} || '';
346              
347             # if no arguments, just drop into the shell
348 1 50       5 if( 0 == @ARGV ) { CPAN::shell(); exit 0 }
  1         9  
  1         346  
349             else
350             {
351 0         0 Getopt::Std::getopts(
352             join( '', @option_order ), \%options );
353 0         0 \%options;
354             }
355             }
356              
357             sub _process_setup_options
358             {
359 0     0   0 my( $class, $options ) = @_;
360              
361 0 0       0 if( $options->{j} )
362             {
363 0         0 $Method_table{j}[ $Method_table_index{code} ]->( $options->{j} );
364 0         0 delete $options->{j};
365             }
366             else
367             {
368             # this is what CPAN.pm would do otherwise
369 0         0 local $CPAN::Be_Silent = 1;
370 0         0 CPAN::HandleConfig->load(
371             # be_silent => 1, deprecated
372             write_file => 0,
373             );
374             }
375              
376 0         0 foreach my $o ( qw(F I w T P M) )
377             {
378 0 0       0 next unless exists $options->{$o};
379 0         0 $Method_table{$o}[ $Method_table_index{code} ]->( $options->{$o} );
380 0         0 delete $options->{$o};
381             }
382              
383 0 0       0 if( $options->{o} )
384             {
385 0         0 my @pairs = map { [ split /=/, $_, 2 ] } split /,/, $options->{o};
  0         0  
386 0         0 foreach my $pair ( @pairs )
387             {
388 0         0 my( $setting, $value ) = @$pair;
389 0         0 $CPAN::Config->{$setting} = $value;
390             # $logger->debug( "Setting [$setting] to [$value]" );
391             }
392 0         0 delete $options->{o};
393             }
394              
395 0         0 my $option_count = grep { $options->{$_} } @option_order;
  0         0  
396 4     4   28 no warnings 'uninitialized';
  4         12  
  4         4756  
397 0         0 $option_count -= $options->{'f'}; # don't count force
398              
399             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
400             # if there are no options, set -i (this line fixes RT ticket 16915)
401 0 0       0 $options->{i}++ unless $option_count;
402             }
403              
404              
405             =item run()
406              
407             Just do it.
408              
409             The C method returns 0 on success and a postive number on
410             failure. See the section on EXIT CODES for details on the values.
411              
412             =cut
413              
414             my $logger;
415              
416             sub run
417             {
418 0     0 1 0 my $class = shift;
419              
420 0         0 my $return_value = HEY_IT_WORKED; # assume that things will work
421              
422 0         0 $logger = $class->_init_logger;
423 0         0 $logger->debug( "Using logger from @{[ref $logger]}" );
  0         0  
424              
425 0         0 $class->_hook_into_CPANpm_report;
426 0         0 $logger->debug( "Hooked into output" );
427              
428 0         0 $class->_stupid_interface_hack_for_non_rtfmers;
429 0         0 $logger->debug( "Patched cargo culting" );
430              
431 0         0 my $options = $class->_process_options;
432 0         0 $logger->debug( "Options are @{[Dumper($options)]}" );
  0         0  
433              
434 0         0 $class->_process_setup_options( $options );
435              
436 0         0 OPTION: foreach my $option ( @option_order )
437             {
438 0 0       0 next unless $options->{$option};
439              
440 0         0 my( $sub, $takes_args, $description ) =
441 0         0 map { $Method_table{$option}[ $Method_table_index{$_} ] }
442             qw( code takes_args description );
443              
444 0 0   0   0 unless( ref $sub eq ref sub {} )
  0         0  
445             {
446 0         0 $return_value = THE_PROGRAMMERS_AN_IDIOT;
447 0         0 last OPTION;
448             }
449              
450 0 0 0     0 $logger->info( "$description -- ignoring other arguments" )
451             if( @ARGV && ! $takes_args );
452              
453 0         0 $return_value = $sub->( \ @ARGV, $options );
454              
455 0         0 last;
456             }
457              
458 0         0 return $return_value;
459             }
460              
461             {
462             package
463             Local::Null::Logger; # hide from PAUSE
464              
465 2     2   11 sub new { bless \ my $x, $_[0] }
466 38     38   59 sub AUTOLOAD { 1 }
467 0     0   0 sub DESTROY { 1 }
468             }
469              
470             sub _init_logger
471             {
472 2     2   2212 my $log4perl_loaded = eval "require Log::Log4perl; 1";
473              
474 2 50       23 unless( $log4perl_loaded )
475             {
476 2         342 print "Loading internal null logger. Install Log::Log4perl for logging messages\n";
477 2         20 $logger = Local::Null::Logger->new;
478 2         86 return $logger;
479             }
480              
481 0   0     0 my $LEVEL = $ENV{CPANSCRIPT_LOGLEVEL} || 'INFO';
482              
483 0         0 Log::Log4perl::init( \ <<"HERE" );
484             log4perl.rootLogger=$LEVEL, A1
485             log4perl.appender.A1=Log::Log4perl::Appender::Screen
486             log4perl.appender.A1.layout=PatternLayout
487             log4perl.appender.A1.layout.ConversionPattern=%m%n
488             HERE
489              
490 0         0 $logger = Log::Log4perl->get_logger( 'App::Cpan' );
491             }
492              
493             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
494             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
495             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
496              
497             sub _default
498             {
499 0     0   0 my( $args, $options ) = @_;
500              
501 0         0 my $switch = '';
502              
503             # choose the option that we're going to use
504             # we'll deal with 'f' (force) later, so skip it
505 0         0 foreach my $option ( @CPAN_OPTIONS )
506             {
507 0 0       0 next if $option eq 'f';
508 0 0       0 next unless $options->{$option};
509 0         0 $switch = $option;
510 0         0 last;
511             }
512              
513             # 1. with no switches, but arguments, use the default switch (install)
514             # 2. with no switches and no args, start the shell
515             # 3. With a switch but no args, die! These switches need arguments.
516 0 0 0     0 if( not $switch and @$args ) { $switch = $Default; }
  0 0 0     0  
    0 0        
517 0         0 elsif( not $switch and not @$args ) { return CPAN::shell() }
518             elsif( $switch and not @$args )
519 0         0 { die "Nothing to $CPAN_METHODS{$switch}!\n"; }
520              
521             # Get and check the method from CPAN::Shell
522 0         0 my $method = $CPAN_METHODS{$switch};
523 0 0       0 die "CPAN.pm cannot $method!\n" unless CPAN::Shell->can( $method );
524              
525             # call the CPAN::Shell method, with force if specified
526 0         0 my $action = do {
527 0 0   0   0 if( $options->{f} ) { sub { CPAN::Shell->force( $method, @_ ) } }
  0         0  
  0         0  
528 0     0   0 else { sub { CPAN::Shell->$method( @_ ) } }
  0         0  
529             };
530              
531             # How do I handle exit codes for multiple arguments?
532 0         0 my @errors = ();
533              
534 0         0 foreach my $arg ( @$args )
535             {
536 0         0 _clear_cpanpm_output();
537 0         0 $action->( $arg );
538              
539 0         0 my $error = _cpanpm_output_indicates_failure();
540 0 0       0 push @errors, $error if $error;
541             }
542              
543 0         0 return do {
544 0 0       0 if( @errors ) { $errors[0] }
  0         0  
545 0         0 else { HEY_IT_WORKED }
546             };
547              
548             }
549              
550             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
551              
552             =for comment
553              
554             CPAN.pm sends all the good stuff either to STDOUT, or to a temp
555             file if $CPAN::Be_Silent is set. I have to intercept that output
556             so I can find out what happened.
557              
558             =cut
559              
560             BEGIN {
561 4     4   11 my $scalar = '';
562              
563             sub _hook_into_CPANpm_report
564             {
565 4     4   29 no warnings 'redefine';
  4         10  
  4         2183  
566              
567             *CPAN::Shell::myprint = sub {
568 18     18   1216 my($self,$what) = @_;
569 18         33 $scalar .= $what;
570 18   100     96 $self->print_ornamented($what,
571             $CPAN::Config->{colorize_print}||'bold blue on_white',
572             );
573 1     1   3070 };
574              
575             *CPAN::Shell::mywarn = sub {
576 14     14   3775 my($self,$what) = @_;
577 14         34 $scalar .= $what;
578 14   100     87 $self->print_ornamented($what,
579             $CPAN::Config->{colorize_warn}||'bold red on_white'
580             );
581 1         19 };
582              
583             }
584              
585 8     8   3362 sub _clear_cpanpm_output { $scalar = '' }
586              
587 11     11   52 sub _get_cpanpm_output { $scalar }
588              
589             # These are lines I don't care about in CPAN.pm output. If I can
590             # filter out the informational noise, I have a better chance to
591             # catch the error signal
592 4         768 my @skip_lines = (
593             qr/^\QWarning \(usually harmless\)/,
594             qr/\bwill not store persistent state\b/,
595             qr(//hint//),
596             qr/^\s+reports\s+/,
597             qr/^Try the command/,
598             qr/^\s+$/,
599             qr/^to find objects/,
600             qr/^\s*Database was generated on/,
601             qr/^Going to read/,
602             qr|^\s+i\s+/|, # the i /Foo::Whatever/ line when it doesn't know
603             );
604              
605             sub _get_cpanpm_last_line
606             {
607 1     1   7 open my($fh), "<", \ $scalar;
  1     29   2  
  1         9  
  29         362  
608              
609 29         1690 my @lines = <$fh>;
610              
611             # This is a bit ugly. Once we examine a line, we have to
612             # examine the line before it and go through all of the same
613             # regexes. I could do something fancy, but this works.
614             REGEXES: {
615 29         54 foreach my $regex ( @skip_lines )
  38         49  
616             {
617 314 100       1268 if( $lines[-1] =~ m/$regex/ )
618             {
619 9         11 pop @lines;
620 9         19 redo REGEXES; # we have to go through all of them for every line!
621             }
622             }
623             }
624              
625 29         173 $logger->debug( "Last interesting line of CPAN.pm output is:\n\t$lines[-1]" );
626              
627 29         168 $lines[-1];
628             }
629             }
630              
631             BEGIN {
632 4     4   5017 my $epic_fail_words = join '|',
633             qw( Error stop(?:ping)? problems force not unsupported
634             fail(?:ed)? Cannot\s+install );
635              
636             sub _cpanpm_output_indicates_failure
637             {
638 9     9   3166 my $last_line = _get_cpanpm_last_line();
639              
640 9         146 my $result = $last_line =~ /\b(?:$epic_fail_words)\b/i;
641 9 50       39 return A_MODULE_FAILED_TO_INSTALL if $last_line =~ /\b(?:Cannot\s+install)\b/i;
642              
643 9 100       43 $result || ();
644             }
645             }
646              
647             sub _cpanpm_output_indicates_success
648             {
649 9     9   2981 my $last_line = _get_cpanpm_last_line();
650              
651 9         50 my $result = $last_line =~ /\b(?:\s+-- OK|PASS)\b/;
652 9 100       56 $result || ();
653             }
654              
655             sub _cpanpm_output_is_vague
656             {
657 0 0 0 0   0 return FALSE if
658             _cpanpm_output_indicates_failure() ||
659             _cpanpm_output_indicates_success();
660              
661 0         0 return TRUE;
662             }
663              
664             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
665             sub _turn_on_warnings {
666 0     0   0 carp "Warnings are implemented yet";
667 0         0 return HEY_IT_WORKED;
668             }
669              
670             sub _turn_off_testing {
671 0     0   0 $logger->debug( 'Trusting test report history' );
672 0         0 $CPAN::Config->{trust_test_report_history} = 1;
673 0         0 return HEY_IT_WORKED;
674             }
675              
676             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
677             sub _print_help
678             {
679 0     0   0 $logger->info( "Use perldoc to read the documentation" );
680 0         0 exec "perldoc $0";
681             }
682              
683             sub _print_version # -v
684             {
685 0     0   0 $logger->info(
686             "$0 script version $VERSION, CPAN.pm version " . CPAN->VERSION );
687              
688 0         0 return HEY_IT_WORKED;
689             }
690              
691             sub _print_details # -V
692             {
693 0     0   0 _print_version();
694              
695 0         0 _check_install_dirs();
696              
697 0         0 $logger->info( '-' x 50 . "\nChecking configured mirrors..." );
698 0         0 foreach my $mirror ( @{ $CPAN::Config->{urllist} } ) {
  0         0  
699 0         0 _print_ping_report( $mirror );
700             }
701              
702 0         0 $logger->info( '-' x 50 . "\nChecking for faster mirrors..." );
703              
704             {
705 0         0 require CPAN::Mirrors;
  0         0  
706              
707 0 0       0 if ( $CPAN::Config->{connect_to_internet_ok} ) {
708 0         0 $CPAN::Frontend->myprint(qq{Trying to fetch a mirror list from the Internet\n});
709 0 0       0 eval { CPAN::FTP->localize('MIRRORED.BY',File::Spec->catfile($CPAN::Config->{keep_source_where},'MIRRORED.BY'),3,1) }
  0         0  
710             or $CPAN::Frontend->mywarn(<<'HERE');
711             We failed to get a copy of the mirror list from the Internet.
712             You will need to provide CPAN mirror URLs yourself.
713             HERE
714 0         0 $CPAN::Frontend->myprint("\n");
715             }
716              
717 0         0 my $mirrors = CPAN::Mirrors->new( );
718 0         0 $mirrors->parse_mirrored_by( File::Spec->catfile($CPAN::Config->{keep_source_where},'MIRRORED.BY') );
719 0         0 my @continents = $mirrors->find_best_continents;
720              
721 0         0 my @mirrors = $mirrors->get_mirrors_by_continents( $continents[0] );
722 0         0 my @timings = $mirrors->get_mirrors_timings( \@mirrors );
723              
724 0         0 foreach my $timing ( @timings ) {
725 0         0 $logger->info( sprintf "%s (%0.2f ms)",
726             $timing->hostname, $timing->rtt );
727             }
728             }
729              
730 0         0 return HEY_IT_WORKED;
731             }
732              
733             sub _check_install_dirs
734             {
735 0     0   0 my $makepl_arg = $CPAN::Config->{makepl_arg};
736 0         0 my $mbuildpl_arg = $CPAN::Config->{mbuildpl_arg};
737              
738 0         0 my @custom_dirs;
739             # PERL_MM_OPT
740 0         0 push @custom_dirs,
741             $makepl_arg =~ m/INSTALL_BASE\s*=\s*(\S+)/g,
742             $mbuildpl_arg =~ m/--install_base\s*=\s*(\S+)/g;
743              
744 0 0       0 if( @custom_dirs ) {
745 0         0 foreach my $dir ( @custom_dirs ) {
746 0         0 _print_inc_dir_report( $dir );
747             }
748             }
749              
750             # XXX: also need to check makepl_args, etc
751              
752 0         0 my @checks = (
753             [ 'core', [ grep $_, @Config{qw(installprivlib installarchlib)} ] ],
754             [ 'vendor', [ grep $_, @Config{qw(installvendorlib installvendorarch)} ] ],
755             [ 'site', [ grep $_, @Config{qw(installsitelib installsitearch)} ] ],
756             [ 'PERL5LIB', _split_paths( $ENV{PERL5LIB} ) ],
757             [ 'PERLLIB', _split_paths( $ENV{PERLLIB} ) ],
758             );
759              
760 0         0 $logger->info( '-' x 50 . "\nChecking install dirs..." );
761 0         0 foreach my $tuple ( @checks ) {
762 0         0 my( $label ) = $tuple->[0];
763              
764 0         0 $logger->info( "Checking $label" );
765 0 0       0 $logger->info( "\tno directories for $label" ) unless @{ $tuple->[1] };
  0         0  
766 0         0 foreach my $dir ( @{ $tuple->[1] } ) {
  0         0  
767 0         0 _print_inc_dir_report( $dir );
768             }
769             }
770              
771             }
772              
773             sub _split_paths
774             {
775 0   0 0   0 [ map { _expand_filename( $_ ) } split /$Config{path_sep}/, $_[0] || '' ];
  0         0  
776             }
777              
778              
779             =pod
780              
781             Stolen from File::Path::Expand
782              
783             =cut
784              
785             sub _expand_filename
786             {
787 9     9   10562 my( $path ) = @_;
788 4     4   39 no warnings 'uninitialized';
  4         15  
  4         6963  
789 9         61 $logger->debug( "Expanding path $path\n" );
790 9         34 $path =~ s{\A~([^/]+)?}{
791 3 50 66     36 _home_of( $1 || $> ) || "~$1"
792             }e;
793 9         66 return $path;
794             }
795              
796             sub _home_of
797             {
798 0     0     require User::pwent;
799 0           my( $user ) = @_;
800 0 0         my $ent = User::pwent::getpw($user) or return;
801 0           return $ent->dir;
802             }
803              
804             sub _get_default_inc
805             {
806 0     0     require Config;
807              
808 0           [ @Config::Config{ _vars() }, '.' ];
809             }
810              
811             sub _vars {
812 0     0     qw(
813             installarchlib
814             installprivlib
815             installsitearch
816             installsitelib
817             );
818             }
819              
820             sub _ping_mirrors {
821 0     0     my $urls = $CPAN::Config->{urllist};
822 0           require URI;
823              
824 0           foreach my $url ( @$urls ) {
825 0           my( $obj ) = URI->new( $url );
826 0 0         next unless _is_pingable_scheme( $obj );
827 0           my $host = $obj->host;
828 0           _print_ping_report( $obj );
829             }
830              
831             }
832              
833             sub _is_pingable_scheme {
834 0     0     my( $uri ) = @_;
835              
836 0           $uri->scheme eq 'file'
837             }
838              
839             sub _find_good_mirrors {
840 0     0     require CPAN::Mirrors;
841              
842 0           my $file = do {
843 0           my $file = 'MIRRORED.BY';
844 0           my $local_path = File::Spec->catfile(
845             $CPAN::Config->{keep_source_where}, $file );
846              
847 0 0         if( -e $local_path ) { $local_path }
  0            
848             else {
849 0           require CPAN::FTP;
850 0           CPAN::FTP->localize( $file, $local_path, 3, 1 );
851 0           $local_path;
852             }
853             };
854 0           my $mirrors = CPAN::Mirrors->new( $file );
855              
856 0           my @mirrors = $mirrors->best_mirrors(
857             how_many => 5,
858             verbose => 1,
859             );
860              
861 0           foreach my $mirror ( @mirrors ) {
862 0 0         next unless eval { $mirror->can( 'http' ) };
  0            
863 0           _print_ping_report( $mirror->http );
864             }
865              
866 0           $CPAN::Config->{urllist} = [
867 0           map { $_->http } @mirrors
868             ];
869             }
870              
871             sub _print_inc_dir_report
872             {
873 0     0     my( $dir ) = shift;
874              
875 0 0         my $writeable = -w $dir ? '+' : '!!! (not writeable)';
876 0           $logger->info( "\t$writeable $dir" );
877 0           return -w $dir;
878             }
879              
880             sub _print_ping_report
881             {
882 0     0     my( $mirror ) = @_;
883              
884 0           my $rtt = eval { _get_ping_report( $mirror ) };
  0            
885              
886 0 0         $logger->info(
887             sprintf "\t%s (%4d ms) %s", $rtt ? '+' : '!', $rtt * 1000, $mirror
888             );
889             }
890              
891             sub _get_ping_report
892             {
893 0     0     require URI;
894 0           my( $mirror ) = @_;
895 0 0         my( $url ) = ref $mirror ? $mirror : URI->new( $mirror ); #XXX
896 0           require Net::Ping;
897              
898 0           my $ping = Net::Ping->new( 'tcp', 1 );
899              
900 0 0         if( $url->scheme eq 'file' ) {
901 0           return -e $url->file;
902             }
903              
904 0           my( $port ) = $url->port;
905              
906 0 0         return unless $port;
907              
908 0 0         if ( $ping->can('port_number') ) {
909 0           $ping->port_number($port);
910             }
911             else {
912 0           $ping->{'port_num'} = $port;
913             }
914              
915 0 0         $ping->hires(1) if $ping->can( 'hires' );
916 0           my( $alive, $rtt ) = eval{ $ping->ping( $url->host ) };
  0            
917 0 0         $alive ? $rtt : undef;
918             }
919              
920             sub _load_local_lib # -I
921             {
922 0     0     $logger->debug( "Loading local::lib" );
923              
924 0           my $rc = eval { require local::lib; 1; };
  0            
  0            
925 0 0         unless( $rc ) {
926 0           $logger->die( "Could not load local::lib" );
927             }
928              
929 0           local::lib->import;
930              
931 0           return HEY_IT_WORKED;
932             }
933              
934             sub _use_these_mirrors # -M
935             {
936 0     0     $logger->debug( "Setting per session mirrors" );
937 0 0         unless( $_[0] ) {
938 0           $logger->die( "The -M switch requires a comma-separated list of mirrors" );
939             }
940              
941 0           $CPAN::Config->{urllist} = [ split /,/, $_[0] ];
942              
943 0           $logger->debug( "Mirrors are @{$CPAN::Config->{urllist}}" );
  0            
944              
945             }
946              
947             sub _create_autobundle
948             {
949 0     0     $logger->info(
950             "Creating autobundle in $CPAN::Config->{cpan_home}/Bundle" );
951              
952 0           CPAN::Shell->autobundle;
953              
954 0           return HEY_IT_WORKED;
955             }
956              
957             sub _recompile
958             {
959 0     0     $logger->info( "Recompiling dynamically-loaded extensions" );
960              
961 0           CPAN::Shell->recompile;
962              
963 0           return HEY_IT_WORKED;
964             }
965              
966             sub _upgrade
967             {
968 0     0     $logger->info( "Upgrading all modules" );
969              
970 0           CPAN::Shell->upgrade();
971              
972 0           return HEY_IT_WORKED;
973             }
974              
975             sub _load_config # -j
976             {
977 0   0 0     my $file = shift || '';
978              
979             # should I clear out any existing config here?
980 0           $CPAN::Config = {};
981 0           delete $INC{'CPAN/Config.pm'};
982 0 0         croak( "Config file [$file] does not exist!\n" ) unless -e $file;
983              
984 0           my $rc = eval "require '$file'";
985              
986             # CPAN::HandleConfig::require_myconfig_or_config looks for this
987 0           $INC{'CPAN/MyConfig.pm'} = 'fake out!';
988              
989             # CPAN::HandleConfig::load looks for this
990 0           $CPAN::Config_loaded = 'fake out';
991              
992 0 0         croak( "Could not load [$file]: $@\n") unless $rc;
993              
994 0           return HEY_IT_WORKED;
995             }
996              
997             sub _dump_config # -J
998             {
999 0     0     my $args = shift;
1000 0           require Data::Dumper;
1001              
1002 0   0       my $fh = $args->[0] || \*STDOUT;
1003              
1004 0           my $dd = Data::Dumper->new(
1005             [$CPAN::Config],
1006             ['$CPAN::Config']
1007             );
1008              
1009 0           print $fh $dd->Dump, "\n1;\n__END__\n";
1010              
1011 0           return HEY_IT_WORKED;
1012             }
1013              
1014             sub _lock_lobotomy # -F
1015             {
1016 4     4   39 no warnings 'redefine';
  4         11  
  4         12848  
1017              
1018 0     0     *CPAN::_flock = sub { 1 };
  0     0      
1019 0     0     *CPAN::checklock = sub { 1 };
  0            
1020              
1021 0           return HEY_IT_WORKED;
1022             }
1023              
1024             sub _download
1025             {
1026 0     0     my $args = shift;
1027              
1028 0           local $CPAN::DEBUG = 1;
1029              
1030 0           my %paths;
1031              
1032 0           foreach my $module ( @$args )
1033             {
1034 0           $logger->info( "Checking $module" );
1035 0           my $path = CPAN::Shell->expand( "Module", $module )->cpan_file;
1036              
1037 0           $logger->debug( "Inst file would be $path\n" );
1038              
1039 0           $paths{$module} = _get_file( _make_path( $path ) );
1040             }
1041              
1042 0           return \%paths;
1043             }
1044              
1045 0     0     sub _make_path { join "/", qw(authors id), $_[0] }
1046              
1047             sub _get_file
1048             {
1049 0     0     my $path = shift;
1050              
1051 0           my $loaded = eval "require LWP::Simple; 1;";
1052 0 0         croak "You need LWP::Simple to use features that fetch files from CPAN\n"
1053             unless $loaded;
1054              
1055 0           my $file = substr $path, rindex( $path, '/' ) + 1;
1056 0           my $store_path = catfile( cwd(), $file );
1057 0           $logger->debug( "Store path is $store_path" );
1058              
1059 0           foreach my $site ( @{ $CPAN::Config->{urllist} } )
  0            
1060             {
1061 0           my $fetch_path = join "/", $site, $path;
1062 0           $logger->debug( "Trying $fetch_path" );
1063 0 0         last if LWP::Simple::getstore( $fetch_path, $store_path );
1064             }
1065              
1066 0           return $store_path;
1067             }
1068              
1069             sub _gitify
1070             {
1071 0     0     my $args = shift;
1072              
1073 0           my $loaded = eval "require Archive::Extract; 1;";
1074 0 0         croak "You need Archive::Extract to use features that gitify distributions\n"
1075             unless $loaded;
1076              
1077 0           my $starting_dir = cwd();
1078              
1079 0           foreach my $module ( @$args )
1080             {
1081 0           $logger->info( "Checking $module" );
1082 0           my $path = CPAN::Shell->expand( "Module", $module )->cpan_file;
1083              
1084 0           my $store_paths = _download( [ $module ] );
1085 0           $logger->debug( "gitify Store path is $store_paths->{$module}" );
1086 0           my $dirname = dirname( $store_paths->{$module} );
1087              
1088 0           my $ae = Archive::Extract->new( archive => $store_paths->{$module} );
1089 0           $ae->extract( to => $dirname );
1090              
1091 0           chdir $ae->extract_path;
1092              
1093 0   0       my $git = $ENV{GIT_COMMAND} || '/usr/local/bin/git';
1094 0 0         croak "Could not find $git" unless -e $git;
1095 0 0         croak "$git is not executable" unless -x $git;
1096              
1097             # can we do this in Pure Perl?
1098 0           system( $git, 'init' );
1099 0           system( $git, qw( add . ) );
1100 0           system( $git, qw( commit -a -m ), 'initial import' );
1101             }
1102              
1103 0           chdir $starting_dir;
1104              
1105 0           return HEY_IT_WORKED;
1106             }
1107              
1108             sub _show_Changes
1109             {
1110 0     0     my $args = shift;
1111              
1112 0           foreach my $arg ( @$args )
1113             {
1114 0           $logger->info( "Checking $arg\n" );
1115              
1116 0           my $module = eval { CPAN::Shell->expand( "Module", $arg ) };
  0            
1117 0           my $out = _get_cpanpm_output();
1118              
1119 0 0         next unless eval { $module->inst_file };
  0            
1120             #next if $module->uptodate;
1121              
1122 0           ( my $id = $module->id() ) =~ s/::/\-/;
1123              
1124 0           my $url = "http://search.cpan.org/~" . lc( $module->userid ) . "/" .
1125             $id . "-" . $module->cpan_version() . "/";
1126              
1127             #print "URL: $url\n";
1128 0           _get_changes_file($url);
1129             }
1130              
1131 0           return HEY_IT_WORKED;
1132             }
1133              
1134             sub _get_changes_file
1135             {
1136 0 0   0     croak "Reading Changes files requires LWP::Simple and URI\n"
1137             unless eval "require LWP::Simple; require URI; 1";
1138              
1139 0           my $url = shift;
1140              
1141 0           my $content = LWP::Simple::get( $url );
1142 0 0         $logger->info( "Got $url ..." ) if defined $content;
1143             #print $content;
1144              
1145 0           my( $change_link ) = $content =~ m|Changes|gi;
1146              
1147 0           my $changes_url = URI->new_abs( $change_link, $url );
1148 0           $logger->debug( "Change link is: $changes_url" );
1149              
1150 0           my $changes = LWP::Simple::get( $changes_url );
1151              
1152 0           print $changes;
1153              
1154 0           return HEY_IT_WORKED;
1155             }
1156              
1157             sub _show_Author
1158             {
1159 0     0     my $args = shift;
1160              
1161 0           foreach my $arg ( @$args )
1162             {
1163 0           my $module = CPAN::Shell->expand( "Module", $arg );
1164 0 0         unless( $module )
1165             {
1166 0           $logger->info( "Didn't find a $arg module, so no author!" );
1167 0           next;
1168             }
1169              
1170 0           my $author = CPAN::Shell->expand( "Author", $module->userid );
1171              
1172 0 0         next unless $module->userid;
1173              
1174 0           printf "%-25s %-8s %-25s %s\n",
1175             $arg, $module->userid, $author->email, $author->name;
1176             }
1177              
1178 0           return HEY_IT_WORKED;
1179             }
1180              
1181             sub _show_Details
1182             {
1183 0     0     my $args = shift;
1184              
1185 0           foreach my $arg ( @$args )
1186             {
1187 0           my $module = CPAN::Shell->expand( "Module", $arg );
1188 0           my $author = CPAN::Shell->expand( "Author", $module->userid );
1189              
1190 0 0         next unless $module->userid;
1191              
1192 0           print "$arg\n", "-" x 73, "\n\t";
1193 0 0         print join "\n\t",
    0          
1194             $module->description ? $module->description : "(no description)",
1195             $module->cpan_file,
1196             $module->inst_file,
1197             'Installed: ' . $module->inst_version,
1198             'CPAN: ' . $module->cpan_version . ' ' .
1199             ($module->uptodate ? "" : "Not ") . "up to date",
1200             $author->fullname . " (" . $module->userid . ")",
1201             $author->email;
1202 0           print "\n\n";
1203              
1204             }
1205              
1206 0           return HEY_IT_WORKED;
1207             }
1208              
1209             sub _show_out_of_date
1210             {
1211 0     0     my @modules = CPAN::Shell->expand( "Module", "/./" );
1212              
1213 0           printf "%-40s %6s %6s\n", "Module Name", "Local", "CPAN";
1214 0           print "-" x 73, "\n";
1215              
1216 0           foreach my $module ( @modules )
1217             {
1218 0 0         next unless $module->inst_file;
1219 0 0         next if $module->uptodate;
1220 0 0         printf "%-40s %.4f %.4f\n",
1221             $module->id,
1222             $module->inst_version ? $module->inst_version : '',
1223             $module->cpan_version;
1224             }
1225              
1226 0           return HEY_IT_WORKED;
1227             }
1228              
1229             sub _show_author_mods
1230             {
1231 0     0     my $args = shift;
1232              
1233 0           my %hash = map { lc $_, 1 } @$args;
  0            
1234              
1235 0           my @modules = CPAN::Shell->expand( "Module", "/./" );
1236              
1237 0           foreach my $module ( @modules )
1238             {
1239 0 0         next unless exists $hash{ lc $module->userid };
1240 0           print $module->id, "\n";
1241             }
1242              
1243 0           return HEY_IT_WORKED;
1244             }
1245              
1246             sub _list_all_mods # -l
1247             {
1248 0     0     require File::Find;
1249              
1250 0           my $args = shift;
1251              
1252              
1253 0           my $fh = \*STDOUT;
1254              
1255 0           INC: foreach my $inc ( @INC )
1256             {
1257 0           my( $wanted, $reporter ) = _generator();
1258 0           File::Find::find( { wanted => $wanted }, $inc );
1259              
1260 0           my $count = 0;
1261 0           FILE: foreach my $file ( @{ $reporter->() } )
  0            
1262             {
1263 0           my $version = _parse_version_safely( $file );
1264              
1265 0           my $module_name = _path_to_module( $inc, $file );
1266 0 0         next FILE unless defined $module_name;
1267              
1268 0           print $fh "$module_name\t$version\n";
1269              
1270             #last if $count++ > 5;
1271             }
1272             }
1273              
1274 0           return HEY_IT_WORKED;
1275             }
1276              
1277             sub _generator
1278             {
1279 0     0     my @files = ();
1280              
1281 0 0   0     sub { push @files,
1282             File::Spec->canonpath( $File::Find::name )
1283             if m/\A\w+\.pm\z/ },
1284 0     0     sub { \@files },
1285 0           }
1286              
1287             sub _parse_version_safely # stolen from PAUSE's mldistwatch, but refactored
1288             {
1289 0     0     my( $file ) = @_;
1290              
1291 0           local $/ = "\n";
1292 0           local $_; # don't mess with the $_ in the map calling this
1293              
1294 0 0         return unless open FILE, "<$file";
1295              
1296 0           my $in_pod = 0;
1297 0           my $version;
1298 0           while( )
1299             {
1300 0           chomp;
1301 0 0         $in_pod = /^=(?!cut)/ ? 1 : /^=cut/ ? 0 : $in_pod;
    0          
1302 0 0 0       next if $in_pod || /^\s*#/;
1303              
1304 0 0         next unless /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/;
1305 0           my( $sigil, $var ) = ( $1, $2 );
1306              
1307 0           $version = _eval_version( $_, $sigil, $var );
1308 0           last;
1309             }
1310 0           close FILE;
1311              
1312 0 0         return 'undef' unless defined $version;
1313              
1314 0           return $version;
1315             }
1316              
1317             sub _eval_version
1318             {
1319 0     0     my( $line, $sigil, $var ) = @_;
1320              
1321             # split package line to hide from PAUSE
1322 0           my $eval = qq{
1323             package
1324             ExtUtils::MakeMaker::_version;
1325              
1326             local $sigil$var;
1327             \$$var=undef; do {
1328             $line
1329             }; \$$var
1330             };
1331              
1332 0           my $version = do {
1333 0           local $^W = 0;
1334 4     4   49 no strict;
  4         8  
  4         1198  
1335 0           eval( $eval );
1336             };
1337              
1338 0           return $version;
1339             }
1340              
1341             sub _path_to_module
1342             {
1343 0     0     my( $inc, $path ) = @_;
1344 0 0         return if length $path < length $inc;
1345              
1346 0           my $module_path = substr( $path, length $inc );
1347 0           $module_path =~ s/\.pm\z//;
1348              
1349             # XXX: this is cheating and doesn't handle everything right
1350 0           my @dirs = grep { ! /\W/ } File::Spec->splitdir( $module_path );
  0            
1351 0           shift @dirs;
1352              
1353 0           my $module_name = join "::", @dirs;
1354              
1355 0           return $module_name;
1356             }
1357              
1358             1;
1359              
1360             =back
1361              
1362             =head1 EXIT VALUES
1363              
1364             The script exits with zero if it thinks that everything worked, or a
1365             positive number if it thinks that something failed. Note, however, that
1366             in some cases it has to divine a failure by the output of things it does
1367             not control. For now, the exit codes are vague:
1368              
1369             1 An unknown error
1370              
1371             2 The was an external problem
1372              
1373             4 There was an internal problem with the script
1374              
1375             8 A module failed to install
1376              
1377             =head1 TO DO
1378              
1379             * There is initial support for Log4perl if it is available, but I
1380             haven't gone through everything to make the NullLogger work out
1381             correctly if Log4perl is not installed.
1382              
1383             * When I capture CPAN.pm output, I need to check for errors and
1384             report them to the user.
1385              
1386             * Warnings switch
1387              
1388             * Check then exit
1389              
1390             * no test option
1391              
1392             =head1 BUGS
1393              
1394             * none noted
1395              
1396             =head1 SEE ALSO
1397              
1398             Most behaviour, including environment variables and configuration,
1399             comes directly from CPAN.pm.
1400              
1401             =head1 SOURCE AVAILABILITY
1402              
1403             This code is in Github:
1404              
1405             git://github.com/briandfoy/cpan_script.git
1406              
1407             =head1 CREDITS
1408              
1409             Japheth Cleaver added the bits to allow a forced install (C<-f>).
1410              
1411             Jim Brandt suggest and provided the initial implementation for the
1412             up-to-date and Changes features.
1413              
1414             Adam Kennedy pointed out that C causes problems on Windows
1415             where this script ends up with a .bat extension
1416              
1417             David Golden helps integrate this into the C repos.
1418              
1419             =head1 AUTHOR
1420              
1421             brian d foy, C<< >>
1422              
1423             =head1 COPYRIGHT
1424              
1425             Copyright (c) 2001-2013, brian d foy, All Rights Reserved.
1426              
1427             You may redistribute this under the same terms as Perl itself.
1428              
1429             =cut