From 28daf97c04dbacdcc92f87e9928f9509e7a39d34 Mon Sep 17 00:00:00 2001 From: Adrian Pop <adrian.pop@liu.se> Date: Fri, 26 Feb 2021 15:30:36 +0100 Subject: [PATCH] add MLDB module for perl, used by testsuite/partest/runtests.pl --- tools/msys/usr/bin/site_perl/cpan | 349 ++ tools/msys/usr/bin/site_perl/cpan-mirrors | 170 + .../usr/lib/perl5/core_perl/perllocal.pod | 44 + .../lib/perl5/site_perl/auto/CPAN/.packlist | 60 + .../lib/perl5/site_perl/auto/MLDBM/.packlist | 5 + tools/msys/usr/share/man/man1/cpan-mirrors.1p | 188 + tools/msys/usr/share/man/man1/cpan.1p | 356 ++ tools/msys/usr/share/man/man3/App.Cpan.3pm | 384 ++ tools/msys/usr/share/man/man3/CPAN.3pm | 2556 +++++++++ .../usr/share/man/man3/CPAN.API.HOWTO.3pm | 117 + tools/msys/usr/share/man/man3/CPAN.Admin.3pm | 127 + tools/msys/usr/share/man/man3/CPAN.Debug.3pm | 84 + .../usr/share/man/man3/CPAN.Distroprefs.3pm | 222 + .../usr/share/man/man3/CPAN.FirstTime.3pm | 675 +++ .../usr/share/man/man3/CPAN.HandleConfig.3pm | 113 + .../msys/usr/share/man/man3/CPAN.Kwalify.3pm | 112 + .../msys/usr/share/man/man3/CPAN.Mirrors.3pm | 225 + tools/msys/usr/share/man/man3/CPAN.Nox.3pm | 100 + tools/msys/usr/share/man/man3/CPAN.Plugin.3pm | 123 + .../share/man/man3/CPAN.Plugin.Specfile.3pm | 127 + tools/msys/usr/share/man/man3/CPAN.Queue.3pm | 84 + tools/msys/usr/share/man/man3/CPAN.Tarzip.3pm | 84 + .../msys/usr/share/man/man3/CPAN.Version.3pm | 112 + tools/msys/usr/share/man/man3/MLDBM.3pm | 347 ++ .../usr/share/perl5/site_perl/App/Cpan.pm | 1719 ++++++ tools/msys/usr/share/perl5/site_perl/CPAN.pm | 4093 ++++++++++++++ .../share/perl5/site_perl/CPAN/API/HOWTO.pod | 44 + .../usr/share/perl5/site_perl/CPAN/Admin.pm | 230 + .../usr/share/perl5/site_perl/CPAN/Author.pm | 236 + .../usr/share/perl5/site_perl/CPAN/Bundle.pm | 306 ++ .../share/perl5/site_perl/CPAN/CacheMgr.pm | 249 + .../share/perl5/site_perl/CPAN/Complete.pm | 175 + .../usr/share/perl5/site_perl/CPAN/Debug.pm | 83 + .../perl5/site_perl/CPAN/DeferredCode.pm | 16 + .../perl5/site_perl/CPAN/Distribution.pm | 4862 +++++++++++++++++ .../share/perl5/site_perl/CPAN/Distroprefs.pm | 481 ++ .../perl5/site_perl/CPAN/Distrostatus.pm | 45 + .../CPAN/Exception/RecursiveDependency.pm | 113 + .../CPAN/Exception/blocked_urllist.pm | 46 + .../CPAN/Exception/yaml_not_installed.pm | 23 + .../CPAN/Exception/yaml_process_error.pm | 53 + .../usr/share/perl5/site_perl/CPAN/FTP.pm | 1153 ++++ .../share/perl5/site_perl/CPAN/FTP/netrc.pm | 62 + .../share/perl5/site_perl/CPAN/FirstTime.pm | 2186 ++++++++ .../share/perl5/site_perl/CPAN/HTTP/Client.pm | 254 + .../perl5/site_perl/CPAN/HTTP/Credentials.pm | 91 + .../perl5/site_perl/CPAN/HandleConfig.pm | 806 +++ .../usr/share/perl5/site_perl/CPAN/Index.pm | 626 +++ .../usr/share/perl5/site_perl/CPAN/InfoObj.pm | 224 + .../usr/share/perl5/site_perl/CPAN/Kwalify.pm | 136 + .../site_perl/CPAN/Kwalify/distroprefs.dd | 150 + .../site_perl/CPAN/Kwalify/distroprefs.yml | 92 + .../perl5/site_perl/CPAN/LWP/UserAgent.pm | 62 + .../usr/share/perl5/site_perl/CPAN/Mirrors.pm | 638 +++ .../usr/share/perl5/site_perl/CPAN/Module.pm | 702 +++ .../usr/share/perl5/site_perl/CPAN/Nox.pm | 52 + .../usr/share/perl5/site_perl/CPAN/Plugin.pm | 145 + .../perl5/site_perl/CPAN/Plugin/Specfile.pm | 263 + .../usr/share/perl5/site_perl/CPAN/Prompt.pm | 29 + .../usr/share/perl5/site_perl/CPAN/Queue.pm | 234 + .../usr/share/perl5/site_perl/CPAN/Shell.pm | 2072 +++++++ .../usr/share/perl5/site_perl/CPAN/Tarzip.pm | 479 ++ .../usr/share/perl5/site_perl/CPAN/URL.pm | 31 + .../usr/share/perl5/site_perl/CPAN/Version.pm | 177 + tools/msys/usr/share/perl5/site_perl/MLDBM.pm | 555 ++ .../site_perl/MLDBM/Serializer/Data/Dumper.pm | 65 + .../site_perl/MLDBM/Serializer/FreezeThaw.pm | 16 + .../site_perl/MLDBM/Serializer/Storable.pm | 41 + 68 files changed, 30879 insertions(+) create mode 100644 tools/msys/usr/bin/site_perl/cpan create mode 100644 tools/msys/usr/bin/site_perl/cpan-mirrors create mode 100644 tools/msys/usr/lib/perl5/core_perl/perllocal.pod create mode 100644 tools/msys/usr/lib/perl5/site_perl/auto/CPAN/.packlist create mode 100644 tools/msys/usr/lib/perl5/site_perl/auto/MLDBM/.packlist create mode 100644 tools/msys/usr/share/man/man1/cpan-mirrors.1p create mode 100644 tools/msys/usr/share/man/man1/cpan.1p create mode 100644 tools/msys/usr/share/man/man3/App.Cpan.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.API.HOWTO.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Admin.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Debug.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Distroprefs.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.FirstTime.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.HandleConfig.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Kwalify.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Mirrors.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Nox.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Plugin.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Plugin.Specfile.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Queue.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Tarzip.3pm create mode 100644 tools/msys/usr/share/man/man3/CPAN.Version.3pm create mode 100644 tools/msys/usr/share/man/man3/MLDBM.3pm create mode 100644 tools/msys/usr/share/perl5/site_perl/App/Cpan.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/API/HOWTO.pod create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Admin.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Author.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Bundle.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/CacheMgr.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Complete.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Debug.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/DeferredCode.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Distribution.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Distroprefs.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Distrostatus.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Exception/RecursiveDependency.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Exception/blocked_urllist.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Exception/yaml_not_installed.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Exception/yaml_process_error.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/FTP.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/FTP/netrc.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/FirstTime.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/HTTP/Client.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/HTTP/Credentials.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/HandleConfig.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Index.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/InfoObj.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.dd create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.yml create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/LWP/UserAgent.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Mirrors.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Module.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Nox.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Plugin.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Plugin/Specfile.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Prompt.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Queue.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Shell.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Tarzip.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/URL.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/CPAN/Version.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/MLDBM.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/Data/Dumper.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/FreezeThaw.pm create mode 100644 tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/Storable.pm diff --git a/tools/msys/usr/bin/site_perl/cpan b/tools/msys/usr/bin/site_perl/cpan new file mode 100644 index 0000000000..59badbdf8f --- /dev/null +++ b/tools/msys/usr/bin/site_perl/cpan @@ -0,0 +1,349 @@ +#!/usr/bin/perl + +BEGIN { pop @INC if $INC[-1] eq '.' } +use strict; +use vars qw($VERSION); + +use App::Cpan; +use CPAN::Version; +my $minver = '1.64'; +if ( CPAN::Version->vlt($App::Cpan::VERSION, $minver) ) { + warn "WARNING: your version of App::Cpan is $App::Cpan::VERSION while we would expect at least $minver"; +} +$VERSION = '1.64'; + +my $rc = App::Cpan->run( @ARGV ); + +# will this work under Strawberry Perl? +exit( $rc || 0 ); + +=head1 NAME + +cpan - easily interact with CPAN from the command line + +=head1 SYNOPSIS + + # with arguments and no switches, installs specified modules + cpan module_name [ module_name ... ] + + # with switches, installs modules with extra behavior + cpan [-cfFimtTw] module_name [ module_name ... ] + + # use local::lib + cpan -I module_name [ module_name ... ] + + # one time mirror override for faster mirrors + cpan -p ... + + # with just the dot, install from the distribution in the + # current directory + cpan . + + # without arguments, starts CPAN.pm shell + cpan + + # without arguments, but some switches + cpan [-ahpruvACDLOPX] + +=head1 DESCRIPTION + +This script provides a command interface (not a shell) to CPAN. At the +moment it uses CPAN.pm to do the work, but it is not a one-shot command +runner for CPAN.pm. + +=head2 Options + +=over 4 + +=item -a + +Creates a CPAN.pm autobundle with CPAN::Shell->autobundle. + +=item -A module [ module ... ] + +Shows the primary maintainers for the specified modules. + +=item -c module + +Runs a `make clean` in the specified module's directories. + +=item -C module [ module ... ] + +Show the F<Changes> files for the specified modules + +=item -D module [ module ... ] + +Show the module details. This prints one line for each out-of-date module +(meaning, modules locally installed but have newer versions on CPAN). +Each line has three columns: module name, local version, and CPAN +version. + +=item -f + +Force the specified action, when it normally would have failed. Use this +to install a module even if its tests fail. When you use this option, +-i is not optional for installing a module when you need to force it: + + % cpan -f -i Module::Foo + +=item -F + +Turn off CPAN.pm's attempts to lock anything. You should be careful with +this since you might end up with multiple scripts trying to muck in the +same directory. This isn't so much of a concern if you're loading a special +config with C<-j>, and that config sets up its own work directories. + +=item -g module [ module ... ] + +Downloads to the current directory the latest distribution of the module. + +=item -G module [ module ... ] + +UNIMPLEMENTED + +Download to the current directory the latest distribution of the +modules, unpack each distribution, and create a git repository for each +distribution. + +If you want this feature, check out Yanick Champoux's C<Git::CPAN::Patch> +distribution. + +=item -h + +Print a help message and exit. When you specify C<-h>, it ignores all +of the other options and arguments. + +=item -i module [ module ... ] + +Install the specified modules. With no other switches, this switch +is implied. + +=item -I + +Load C<local::lib> (think like C<-I> for loading lib paths). Too bad +C<-l> was already taken. + +=item -j Config.pm + +Load the file that has the CPAN configuration data. This should have the +same format as the standard F<CPAN/Config.pm> file, which defines +C<$CPAN::Config> as an anonymous hash. + +=item -J + +Dump the configuration in the same format that CPAN.pm uses. This is useful +for checking the configuration as well as using the dump as a starting point +for a new, custom configuration. + +=item -l + +List all installed modules with their versions + +=item -L author [ author ... ] + +List the modules by the specified authors. + +=item -m + +Make the specified modules. + +=item -M mirror1,mirror2,... + +A comma-separated list of mirrors to use for just this run. The C<-P> +option can find them for you automatically. + +=item -n + +Do a dry run, but don't actually install anything. (unimplemented) + +=item -O + +Show the out-of-date modules. + +=item -p + +Ping the configured mirrors and print a report + +=item -P + +Find the best mirrors you could be using and use them for the current +session. + +=item -r + +Recompiles dynamically loaded modules with CPAN::Shell->recompile. + +=item -s + +Drop in the CPAN.pm shell. This command does this automatically if you don't +specify any arguments. + +=item -t module [ module ... ] + +Run a `make test` on the specified modules. + +=item -T + +Do not test modules. Simply install them. + +=item -u + +Upgrade all installed modules. Blindly doing this can really break things, +so keep a backup. + +=item -v + +Print the script version and CPAN.pm version then exit. + +=item -V + +Print detailed information about the cpan client. + +=item -w + +UNIMPLEMENTED + +Turn on cpan warnings. This checks various things, like directory permissions, +and tells you about problems you might have. + +=item -x module [ module ... ] + +Find close matches to the named modules that you think you might have +mistyped. This requires the optional installation of Text::Levenshtein or +Text::Levenshtein::Damerau. + +=item -X + +Dump all the namespaces to standard output. + +=back + +=head2 Examples + + # print a help message + cpan -h + + # print the version numbers + cpan -v + + # create an autobundle + cpan -a + + # recompile modules + cpan -r + + # upgrade all installed modules + cpan -u + + # install modules ( sole -i is optional ) + cpan -i Netscape::Booksmarks Business::ISBN + + # force install modules ( must use -i ) + cpan -fi CGI::Minimal URI + + # install modules but without testing them + cpan -Ti CGI::Minimal URI + +=head2 Environment variables + +There are several components in CPAN.pm that use environment variables. +The build tools, L<ExtUtils::MakeMaker> and L<Module::Build> use some, +while others matter to the levels above them. Some of these are specified +by the Perl Toolchain Gang: + +Lancaster Concensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md> + +Oslo Concensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/oslo-consensus.md> + +=over 4 + +=item NONINTERACTIVE_TESTING + +Assume no one is paying attention and skips prompts for distributions +that do that correctly. C<cpan(1)> sets this to C<1> unless it already +has a value (even if that value is false). + +=item PERL_MM_USE_DEFAULT + +Use the default answer for a prompted questions. C<cpan(1)> sets this +to C<1> unless it already has a value (even if that value is false). + +=item CPAN_OPTS + +As with C<PERL5OPT>, a string of additional C<cpan(1)> options to +add to those you specify on the command line. + +=item CPANSCRIPT_LOGLEVEL + +The log level to use, with either the embedded, minimal logger or +L<Log::Log4perl> if it is installed. Possible values are the same as +the C<Log::Log4perl> levels: C<TRACE>, C<DEBUG>, C<INFO>, C<WARN>, +C<ERROR>, and C<FATAL>. The default is C<INFO>. + +=item GIT_COMMAND + +The path to the C<git> binary to use for the Git features. The default +is C</usr/local/bin/git>. + +=back + +=head1 EXIT VALUES + +The script exits with zero if it thinks that everything worked, or a +positive number if it thinks that something failed. Note, however, that +in some cases it has to divine a failure by the output of things it does +not control. For now, the exit codes are vague: + + 1 An unknown error + + 2 The was an external problem + + 4 There was an internal problem with the script + + 8 A module failed to install + +=head1 TO DO + +* one shot configuration values from the command line + +=head1 BUGS + +* none noted + +=head1 SEE ALSO + +Most behaviour, including environment variables and configuration, +comes directly from CPAN.pm. + +=head1 SOURCE AVAILABILITY + +This code is in Github in the CPAN.pm repository: + + https://github.com/andk/cpanpm + +The source used to be tracked separately in another GitHub repo, +but the canonical source is now in the above repo. + +=head1 CREDITS + +Japheth Cleaver added the bits to allow a forced install (-f). + +Jim Brandt suggest and provided the initial implementation for the +up-to-date and Changes features. + +Adam Kennedy pointed out that exit() causes problems on Windows +where this script ends up with a .bat extension + +=head1 AUTHOR + +brian d foy, C<< <bdfoy@cpan.org> >> + +=head1 COPYRIGHT + +Copyright (c) 2001-2015, brian d foy, All Rights Reserved. + +You may redistribute this under the same terms as Perl itself. + +=cut + +1; diff --git a/tools/msys/usr/bin/site_perl/cpan-mirrors b/tools/msys/usr/bin/site_perl/cpan-mirrors new file mode 100644 index 0000000000..fe0e29c368 --- /dev/null +++ b/tools/msys/usr/bin/site_perl/cpan-mirrors @@ -0,0 +1,170 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use CPAN::HandleConfig; +use CPAN; +use CPAN::Mirrors; +use File::Spec; + +my $file = 'MIRRORED.BY'; + +my $mirrored_by_file = File::Spec->catfile( + CPAN::HandleConfig::cpan_home(), + $file + ); + +unless( -e $mirrored_by_file ) { + eval { + CPAN::FTP->localize( + $file, + $mirrored_by_file, + 3,1) } or die "Could not fetch a $file: $@\n"; + } + +my $mirrors = CPAN::Mirrors->new( $mirrored_by_file ); + +my $seen = {}; +my $n = 5; + +my $best_continent = $mirrors->find_best_continents( n => $n, seen => $seen, verbose => 1 ); +print "Best continent is $best_continent\n"; + +my @mirrors = $mirrors->get_mirrors_by_continents( $best_continent ); + +my $callback = sub { + my( $m ) = @_; + printf "%s = %s ms\n", $m->hostname, 1000 * $m->rtt + }; +my @sorted_mirrors = $mirrors->get_mirrors_timings( \@mirrors, $seen, $callback ); + +print "Best mirrors are ", + join "\n\t", map( $_->hostname, @sorted_mirrors[0..$n-1] ), + "\n"; + +print "Best urls are\n\t", + join( "\n\t", map( $_->http, @sorted_mirrors[0..$n-1] ) ), + "\n"; + +=encoding utf8 + +=head1 NAME + +cpan-mirrors - Find the fatest CPAN mirrors nearby + +=head1 SYNOPSIS + + % cpan-mirrors + + Testing Africa + is.co.za -> 733.72 ms + wa.co.za -> 601.56 ms + ucu.ac.ug -> 407.99 ms + mirror.ac.za -> 614.05 ms + zol.co.zw -> 612.57 ms + -->median time: 612.57 ms + Testing Oceania + serversaustralia.com.au -> 38.62 ms + cpan.inspire.net.nz -> 67.81 ms + digitalpacific.com.au -> 37.89 ms + uber.com.au -> 44.47 ms + lagoon.nc -> 67.49 ms + -->median time: 44.47 ms + Testing North America + httpupdate40.cpanel.net -> 254.00 ms + httpupdate35.cpanel.net -> 409.75 ms + mirrors.sonic.net -> 368.56 ms + httpupdate8.cpanel.net -> 409.10 ms + cpan.cs.utah.edu -> 408.00 ms + -->median time: 408.00 ms + Testing Asia + vinahost.vn -> 146.10 ms + yazd.ac.ir -> 469.29 ms + mirrors-ru.go-parts.com -> 367.09 ms + sohu.com -> 399.76 ms + neolabs.kz -> 610.92 ms + -->median time: 399.76 ms + Testing South America + mmgdesigns.com.ar -> 407.96 ms + nbtelecom.com.br -> 401.96 ms + sunsite.dcc.uchile.cl -> 415.69 ms + linorg.usp.br -> 424.65 ms + unal.edu.co -> 407.60 ms + -->median time: 407.96 ms + Testing Europe + uk2.net -> 409.37 ms + ambiweb.de -> 710.65 ms + bibleonline.ru -> 471.49 ms + rol.ru -> 469.70 ms + rub.de -> 346.57 ms + -->median time: 469.70 ms + Median result by continent: + 44 ms Oceania + 400 ms Asia + 408 ms South America + 408 ms North America + 470 ms Europe + 613 ms Africa + Best continent is Oceania + cpan.catalyst.net.nz = 78.8369178771973 ms + cpan.inspire.net.nz = 69.4258213043213 ms + cpan.mirrors.ilisys.com.au = 50.2209663391113 ms + digitalpacific.com.au = 40.1251316070557 ms + lagoon.nc = 88.8760089874268 ms + nautile.nc = 65.7081604003906 ms + optusnet.com.au = 41.4729118347168 ms + serversaustralia.com.au = 46.1521148681641 ms + uber.com.au = 48.2730865478516 ms + waia.asn.au = 86.2929821014404 ms + webtastix.net = 70.5790519714355 ms + Best mirrors are digitalpacific.com.au + optusnet.com.au + serversaustralia.com.au + uber.com.au + cpan.mirrors.ilisys.com.au + + Best urls are + http://cpan.mirror.digitalpacific.com.au/ + http://mirror.optusnet.com.au/CPAN/ + http://cpan.mirror.serversaustralia.com.au/ + http://cpan.mirror.uber.com.au/ + http://cpan.mirrors.ilisys.com.au/ + +=head1 DESCRIPTION + +CPAN Mirrors come and go, and maybe you do too. The mirror that was +fast then might not be fast now, especially if you changed hemispheres. + +This program goes through the F<MIRRORED.BY> file and pings a selection +of mirrors on each continent to find the fastest mirrors. From the best +continent, it samples several mirrors to find the URL you should add +to your configuration. + +If you don't have a F<MIRRORED.BY> file, this program will download one +for you. It looks through your existing C<urllist> configuration and +then default locations. It stores it in C<~/.cpan> (or your configured +location to store files). + +=head1 TO DO + +=over 4 + +=item * Add an option to update the CPAN configuration + +=item * Add an option to give JSON output + +=back + +=head1 AUTHOR + +brian d foy C<< <bdfoy@cpan.org> >> + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +See L<http://www.perl.com/perl/misc/Artistic.html> + +=cut diff --git a/tools/msys/usr/lib/perl5/core_perl/perllocal.pod b/tools/msys/usr/lib/perl5/core_perl/perllocal.pod new file mode 100644 index 0000000000..910bd528d6 --- /dev/null +++ b/tools/msys/usr/lib/perl5/core_perl/perllocal.pod @@ -0,0 +1,44 @@ +=head2 Fri Feb 26 14:26:58 2021: C<Module> L<MLDBM|MLDBM> + +=over 4 + +=item * + +C<installed into: /usr/share/perl5/site_perl> + +=item * + +C<LINKTYPE: dynamic> + +=item * + +C<VERSION: 2.05> + +=item * + +C<EXE_FILES: > + +=back + +=head2 Fri Feb 26 14:28:27 2021: C<Module> L<CPAN|CPAN> + +=over 4 + +=item * + +C<installed into: /usr/share/perl5/site_perl> + +=item * + +C<LINKTYPE: dynamic> + +=item * + +C<VERSION: 2.28> + +=item * + +C<EXE_FILES: scripts/cpan scripts/cpan-mirrors> + +=back + diff --git a/tools/msys/usr/lib/perl5/site_perl/auto/CPAN/.packlist b/tools/msys/usr/lib/perl5/site_perl/auto/CPAN/.packlist new file mode 100644 index 0000000000..da452ba020 --- /dev/null +++ b/tools/msys/usr/lib/perl5/site_perl/auto/CPAN/.packlist @@ -0,0 +1,60 @@ +/usr/bin/site_perl/cpan +/usr/bin/site_perl/cpan-mirrors +/usr/share/man/man1/cpan-mirrors.1p +/usr/share/man/man1/cpan.1p +/usr/share/man/man3/App.Cpan.3pm +/usr/share/man/man3/CPAN.3pm +/usr/share/man/man3/CPAN.API.HOWTO.3pm +/usr/share/man/man3/CPAN.Admin.3pm +/usr/share/man/man3/CPAN.Debug.3pm +/usr/share/man/man3/CPAN.Distroprefs.3pm +/usr/share/man/man3/CPAN.FirstTime.3pm +/usr/share/man/man3/CPAN.HandleConfig.3pm +/usr/share/man/man3/CPAN.Kwalify.3pm +/usr/share/man/man3/CPAN.Mirrors.3pm +/usr/share/man/man3/CPAN.Nox.3pm +/usr/share/man/man3/CPAN.Plugin.3pm +/usr/share/man/man3/CPAN.Plugin.Specfile.3pm +/usr/share/man/man3/CPAN.Queue.3pm +/usr/share/man/man3/CPAN.Tarzip.3pm +/usr/share/man/man3/CPAN.Version.3pm +/usr/share/perl5/site_perl/App/Cpan.pm +/usr/share/perl5/site_perl/CPAN.pm +/usr/share/perl5/site_perl/CPAN/API/HOWTO.pod +/usr/share/perl5/site_perl/CPAN/Admin.pm +/usr/share/perl5/site_perl/CPAN/Author.pm +/usr/share/perl5/site_perl/CPAN/Bundle.pm +/usr/share/perl5/site_perl/CPAN/CacheMgr.pm +/usr/share/perl5/site_perl/CPAN/Complete.pm +/usr/share/perl5/site_perl/CPAN/Debug.pm +/usr/share/perl5/site_perl/CPAN/DeferredCode.pm +/usr/share/perl5/site_perl/CPAN/Distribution.pm +/usr/share/perl5/site_perl/CPAN/Distroprefs.pm +/usr/share/perl5/site_perl/CPAN/Distrostatus.pm +/usr/share/perl5/site_perl/CPAN/Exception/RecursiveDependency.pm +/usr/share/perl5/site_perl/CPAN/Exception/blocked_urllist.pm +/usr/share/perl5/site_perl/CPAN/Exception/yaml_not_installed.pm +/usr/share/perl5/site_perl/CPAN/Exception/yaml_process_error.pm +/usr/share/perl5/site_perl/CPAN/FTP.pm +/usr/share/perl5/site_perl/CPAN/FTP/netrc.pm +/usr/share/perl5/site_perl/CPAN/FirstTime.pm +/usr/share/perl5/site_perl/CPAN/HTTP/Client.pm +/usr/share/perl5/site_perl/CPAN/HTTP/Credentials.pm +/usr/share/perl5/site_perl/CPAN/HandleConfig.pm +/usr/share/perl5/site_perl/CPAN/Index.pm +/usr/share/perl5/site_perl/CPAN/InfoObj.pm +/usr/share/perl5/site_perl/CPAN/Kwalify.pm +/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.dd +/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.yml +/usr/share/perl5/site_perl/CPAN/LWP/UserAgent.pm +/usr/share/perl5/site_perl/CPAN/Mirrors.pm +/usr/share/perl5/site_perl/CPAN/Module.pm +/usr/share/perl5/site_perl/CPAN/Nox.pm +/usr/share/perl5/site_perl/CPAN/Plugin.pm +/usr/share/perl5/site_perl/CPAN/Plugin/Specfile.pm +/usr/share/perl5/site_perl/CPAN/Prompt.pm +/usr/share/perl5/site_perl/CPAN/Queue.pm +/usr/share/perl5/site_perl/CPAN/Shell.pm +/usr/share/perl5/site_perl/CPAN/Tarzip.pm +/usr/share/perl5/site_perl/CPAN/URL.pm +/usr/share/perl5/site_perl/CPAN/Version.pm diff --git a/tools/msys/usr/lib/perl5/site_perl/auto/MLDBM/.packlist b/tools/msys/usr/lib/perl5/site_perl/auto/MLDBM/.packlist new file mode 100644 index 0000000000..d0d4217a6f --- /dev/null +++ b/tools/msys/usr/lib/perl5/site_perl/auto/MLDBM/.packlist @@ -0,0 +1,5 @@ +/usr/share/man/man3/MLDBM.3pm +/usr/share/perl5/site_perl/MLDBM.pm +/usr/share/perl5/site_perl/MLDBM/Serializer/Data/Dumper.pm +/usr/share/perl5/site_perl/MLDBM/Serializer/FreezeThaw.pm +/usr/share/perl5/site_perl/MLDBM/Serializer/Storable.pm diff --git a/tools/msys/usr/share/man/man1/cpan-mirrors.1p b/tools/msys/usr/share/man/man1/cpan-mirrors.1p new file mode 100644 index 0000000000..2c1134e70a --- /dev/null +++ b/tools/msys/usr/share/man/man1/cpan-mirrors.1p @@ -0,0 +1,188 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN-MIRRORS 1" +.TH CPAN-MIRRORS 1 "2018-09-22" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +cpan\-mirrors \- Find the fatest CPAN mirrors nearby +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 1 +\& % cpan\-mirrors +\& +\& Testing Africa +\& is.co.za \-> 733.72 ms +\& wa.co.za \-> 601.56 ms +\& ucu.ac.ug \-> 407.99 ms +\& mirror.ac.za \-> 614.05 ms +\& zol.co.zw \-> 612.57 ms +\& \-\->median time: 612.57 ms +\& Testing Oceania +\& serversaustralia.com.au \-> 38.62 ms +\& cpan.inspire.net.nz \-> 67.81 ms +\& digitalpacific.com.au \-> 37.89 ms +\& uber.com.au \-> 44.47 ms +\& lagoon.nc \-> 67.49 ms +\& \-\->median time: 44.47 ms +\& Testing North America +\& httpupdate40.cpanel.net \-> 254.00 ms +\& httpupdate35.cpanel.net \-> 409.75 ms +\& mirrors.sonic.net \-> 368.56 ms +\& httpupdate8.cpanel.net \-> 409.10 ms +\& cpan.cs.utah.edu \-> 408.00 ms +\& \-\->median time: 408.00 ms +\& Testing Asia +\& vinahost.vn \-> 146.10 ms +\& yazd.ac.ir \-> 469.29 ms +\& mirrors\-ru.go\-parts.com \-> 367.09 ms +\& sohu.com \-> 399.76 ms +\& neolabs.kz \-> 610.92 ms +\& \-\->median time: 399.76 ms +\& Testing South America +\& mmgdesigns.com.ar \-> 407.96 ms +\& nbtelecom.com.br \-> 401.96 ms +\& sunsite.dcc.uchile.cl \-> 415.69 ms +\& linorg.usp.br \-> 424.65 ms +\& unal.edu.co \-> 407.60 ms +\& \-\->median time: 407.96 ms +\& Testing Europe +\& uk2.net \-> 409.37 ms +\& ambiweb.de \-> 710.65 ms +\& bibleonline.ru \-> 471.49 ms +\& rol.ru \-> 469.70 ms +\& rub.de \-> 346.57 ms +\& \-\->median time: 469.70 ms +\& Median result by continent: +\& 44 ms Oceania +\& 400 ms Asia +\& 408 ms South America +\& 408 ms North America +\& 470 ms Europe +\& 613 ms Africa +\& Best continent is Oceania +\& cpan.catalyst.net.nz = 78.8369178771973 ms +\& cpan.inspire.net.nz = 69.4258213043213 ms +\& cpan.mirrors.ilisys.com.au = 50.2209663391113 ms +\& digitalpacific.com.au = 40.1251316070557 ms +\& lagoon.nc = 88.8760089874268 ms +\& nautile.nc = 65.7081604003906 ms +\& optusnet.com.au = 41.4729118347168 ms +\& serversaustralia.com.au = 46.1521148681641 ms +\& uber.com.au = 48.2730865478516 ms +\& waia.asn.au = 86.2929821014404 ms +\& webtastix.net = 70.5790519714355 ms +\& Best mirrors are digitalpacific.com.au +\& optusnet.com.au +\& serversaustralia.com.au +\& uber.com.au +\& cpan.mirrors.ilisys.com.au +\& +\& Best urls are +\& http://cpan.mirror.digitalpacific.com.au/ +\& http://mirror.optusnet.com.au/CPAN/ +\& http://cpan.mirror.serversaustralia.com.au/ +\& http://cpan.mirror.uber.com.au/ +\& http://cpan.mirrors.ilisys.com.au/ +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +\&\s-1CPAN\s0 Mirrors come and go, and maybe you do too. The mirror that was +fast then might not be fast now, especially if you changed hemispheres. +.PP +This program goes through the \fI\s-1MIRRORED.BY\s0\fR file and pings a selection +of mirrors on each continent to find the fastest mirrors. From the best +continent, it samples several mirrors to find the \s-1URL\s0 you should add +to your configuration. +.PP +If you don't have a \fI\s-1MIRRORED.BY\s0\fR file, this program will download one +for you. It looks through your existing \f(CW\*(C`urllist\*(C'\fR configuration and +then default locations. It stores it in \f(CW\*(C`~/.cpan\*(C'\fR (or your configured +location to store files). +.SH "TO DO" +.IX Header "TO DO" +.IP "\(bu" 4 +Add an option to update the \s-1CPAN\s0 configuration +.IP "\(bu" 4 +Add an option to give \s-1JSON\s0 output +.SH "AUTHOR" +.IX Header "AUTHOR" +brian d foy \f(CW\*(C`<bdfoy@cpan.org>\*(C'\fR +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. +.PP +See <http://www.perl.com/perl/misc/Artistic.html> diff --git a/tools/msys/usr/share/man/man1/cpan.1p b/tools/msys/usr/share/man/man1/cpan.1p new file mode 100644 index 0000000000..cc9b3af27f --- /dev/null +++ b/tools/msys/usr/share/man/man1/cpan.1p @@ -0,0 +1,356 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN 1" +.TH CPAN 1 "2020-05-19" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +cpan \- easily interact with CPAN from the command line +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 2 +\& # with arguments and no switches, installs specified modules +\& cpan module_name [ module_name ... ] +\& +\& # with switches, installs modules with extra behavior +\& cpan [\-cfFimtTw] module_name [ module_name ... ] +\& +\& # use local::lib +\& cpan \-I module_name [ module_name ... ] +\& +\& # one time mirror override for faster mirrors +\& cpan \-p ... +\& +\& # with just the dot, install from the distribution in the +\& # current directory +\& cpan . +\& +\& # without arguments, starts CPAN.pm shell +\& cpan +\& +\& # without arguments, but some switches +\& cpan [\-ahpruvACDLOPX] +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +This script provides a command interface (not a shell) to \s-1CPAN.\s0 At the +moment it uses \s-1CPAN\s0.pm to do the work, but it is not a one-shot command +runner for \s-1CPAN\s0.pm. +.SS "Options" +.IX Subsection "Options" +.IP "\-a" 4 +.IX Item "-a" +Creates a \s-1CPAN\s0.pm autobundle with CPAN::Shell\->autobundle. +.IP "\-A module [ module ... ]" 4 +.IX Item "-A module [ module ... ]" +Shows the primary maintainers for the specified modules. +.IP "\-c module" 4 +.IX Item "-c module" +Runs a `make clean` in the specified module's directories. +.IP "\-C module [ module ... ]" 4 +.IX Item "-C module [ module ... ]" +Show the \fIChanges\fR files for the specified modules +.IP "\-D module [ module ... ]" 4 +.IX Item "-D module [ module ... ]" +Show the module details. This prints one line for each out-of-date module +(meaning, modules locally installed but have newer versions on \s-1CPAN\s0). +Each line has three columns: module name, local version, and \s-1CPAN\s0 +version. +.IP "\-f" 4 +.IX Item "-f" +Force the specified action, when it normally would have failed. Use this +to install a module even if its tests fail. When you use this option, +\&\-i is not optional for installing a module when you need to force it: +.Sp +.Vb 1 +\& % cpan \-f \-i Module::Foo +.Ve +.IP "\-F" 4 +.IX Item "-F" +Turn off \s-1CPAN\s0.pm's attempts to lock anything. You should be careful with +this since you might end up with multiple scripts trying to muck in the +same directory. This isn't so much of a concern if you're loading a special +config with \f(CW\*(C`\-j\*(C'\fR, and that config sets up its own work directories. +.IP "\-g module [ module ... ]" 4 +.IX Item "-g module [ module ... ]" +Downloads to the current directory the latest distribution of the module. +.IP "\-G module [ module ... ]" 4 +.IX Item "-G module [ module ... ]" +\&\s-1UNIMPLEMENTED\s0 +.Sp +Download to the current directory the latest distribution of the +modules, unpack each distribution, and create a git repository for each +distribution. +.Sp +If you want this feature, check out Yanick Champoux's \f(CW\*(C`Git::CPAN::Patch\*(C'\fR +distribution. +.IP "\-h" 4 +.IX Item "-h" +Print a help message and exit. When you specify \f(CW\*(C`\-h\*(C'\fR, it ignores all +of the other options and arguments. +.IP "\-i module [ module ... ]" 4 +.IX Item "-i module [ module ... ]" +Install the specified modules. With no other switches, this switch +is implied. +.IP "\-I" 4 +.IX Item "-I" +Load \f(CW\*(C`local::lib\*(C'\fR (think like \f(CW\*(C`\-I\*(C'\fR for loading lib paths). Too bad +\&\f(CW\*(C`\-l\*(C'\fR was already taken. +.IP "\-j Config.pm" 4 +.IX Item "-j Config.pm" +Load the file that has the \s-1CPAN\s0 configuration data. This should have the +same format as the standard \fICPAN/Config.pm\fR file, which defines +\&\f(CW$CPAN::Config\fR as an anonymous hash. +.IP "\-J" 4 +.IX Item "-J" +Dump the configuration in the same format that \s-1CPAN\s0.pm uses. This is useful +for checking the configuration as well as using the dump as a starting point +for a new, custom configuration. +.IP "\-l" 4 +.IX Item "-l" +List all installed modules with their versions +.IP "\-L author [ author ... ]" 4 +.IX Item "-L author [ author ... ]" +List the modules by the specified authors. +.IP "\-m" 4 +.IX Item "-m" +Make the specified modules. +.IP "\-M mirror1,mirror2,..." 4 +.IX Item "-M mirror1,mirror2,..." +A comma-separated list of mirrors to use for just this run. The \f(CW\*(C`\-P\*(C'\fR +option can find them for you automatically. +.IP "\-n" 4 +.IX Item "-n" +Do a dry run, but don't actually install anything. (unimplemented) +.IP "\-O" 4 +.IX Item "-O" +Show the out-of-date modules. +.IP "\-p" 4 +.IX Item "-p" +Ping the configured mirrors and print a report +.IP "\-P" 4 +.IX Item "-P" +Find the best mirrors you could be using and use them for the current +session. +.IP "\-r" 4 +.IX Item "-r" +Recompiles dynamically loaded modules with CPAN::Shell\->recompile. +.IP "\-s" 4 +.IX Item "-s" +Drop in the \s-1CPAN\s0.pm shell. This command does this automatically if you don't +specify any arguments. +.IP "\-t module [ module ... ]" 4 +.IX Item "-t module [ module ... ]" +Run a `make test` on the specified modules. +.IP "\-T" 4 +.IX Item "-T" +Do not test modules. Simply install them. +.IP "\-u" 4 +.IX Item "-u" +Upgrade all installed modules. Blindly doing this can really break things, +so keep a backup. +.IP "\-v" 4 +.IX Item "-v" +Print the script version and \s-1CPAN\s0.pm version then exit. +.IP "\-V" 4 +.IX Item "-V" +Print detailed information about the cpan client. +.IP "\-w" 4 +.IX Item "-w" +\&\s-1UNIMPLEMENTED\s0 +.Sp +Turn on cpan warnings. This checks various things, like directory permissions, +and tells you about problems you might have. +.IP "\-x module [ module ... ]" 4 +.IX Item "-x module [ module ... ]" +Find close matches to the named modules that you think you might have +mistyped. This requires the optional installation of Text::Levenshtein or +Text::Levenshtein::Damerau. +.IP "\-X" 4 +.IX Item "-X" +Dump all the namespaces to standard output. +.SS "Examples" +.IX Subsection "Examples" +.Vb 2 +\& # print a help message +\& cpan \-h +\& +\& # print the version numbers +\& cpan \-v +\& +\& # create an autobundle +\& cpan \-a +\& +\& # recompile modules +\& cpan \-r +\& +\& # upgrade all installed modules +\& cpan \-u +\& +\& # install modules ( sole \-i is optional ) +\& cpan \-i Netscape::Booksmarks Business::ISBN +\& +\& # force install modules ( must use \-i ) +\& cpan \-fi CGI::Minimal URI +\& +\& # install modules but without testing them +\& cpan \-Ti CGI::Minimal URI +.Ve +.SS "Environment variables" +.IX Subsection "Environment variables" +There are several components in \s-1CPAN\s0.pm that use environment variables. +The build tools, ExtUtils::MakeMaker and Module::Build use some, +while others matter to the levels above them. Some of these are specified +by the Perl Toolchain Gang: +.PP +Lancaster Concensus: <https://github.com/Perl\-Toolchain\-Gang/toolchain\-site/blob/master/lancaster\-consensus.md> +.PP +Oslo Concensus: <https://github.com/Perl\-Toolchain\-Gang/toolchain\-site/blob/master/oslo\-consensus.md> +.IP "\s-1NONINTERACTIVE_TESTING\s0" 4 +.IX Item "NONINTERACTIVE_TESTING" +Assume no one is paying attention and skips prompts for distributions +that do that correctly. \f(CWcpan(1)\fR sets this to \f(CW1\fR unless it already +has a value (even if that value is false). +.IP "\s-1PERL_MM_USE_DEFAULT\s0" 4 +.IX Item "PERL_MM_USE_DEFAULT" +Use the default answer for a prompted questions. \f(CWcpan(1)\fR sets this +to \f(CW1\fR unless it already has a value (even if that value is false). +.IP "\s-1CPAN_OPTS\s0" 4 +.IX Item "CPAN_OPTS" +As with \f(CW\*(C`PERL5OPT\*(C'\fR, a string of additional \f(CWcpan(1)\fR options to +add to those you specify on the command line. +.IP "\s-1CPANSCRIPT_LOGLEVEL\s0" 4 +.IX Item "CPANSCRIPT_LOGLEVEL" +The log level to use, with either the embedded, minimal logger or +Log::Log4perl if it is installed. Possible values are the same as +the \f(CW\*(C`Log::Log4perl\*(C'\fR levels: \f(CW\*(C`TRACE\*(C'\fR, \f(CW\*(C`DEBUG\*(C'\fR, \f(CW\*(C`INFO\*(C'\fR, \f(CW\*(C`WARN\*(C'\fR, +\&\f(CW\*(C`ERROR\*(C'\fR, and \f(CW\*(C`FATAL\*(C'\fR. The default is \f(CW\*(C`INFO\*(C'\fR. +.IP "\s-1GIT_COMMAND\s0" 4 +.IX Item "GIT_COMMAND" +The path to the \f(CW\*(C`git\*(C'\fR binary to use for the Git features. The default +is \f(CW\*(C`/usr/local/bin/git\*(C'\fR. +.SH "EXIT VALUES" +.IX Header "EXIT VALUES" +The script exits with zero if it thinks that everything worked, or a +positive number if it thinks that something failed. Note, however, that +in some cases it has to divine a failure by the output of things it does +not control. For now, the exit codes are vague: +.PP +.Vb 1 +\& 1 An unknown error +\& +\& 2 The was an external problem +\& +\& 4 There was an internal problem with the script +\& +\& 8 A module failed to install +.Ve +.SH "TO DO" +.IX Header "TO DO" +* one shot configuration values from the command line +.SH "BUGS" +.IX Header "BUGS" +* none noted +.SH "SEE ALSO" +.IX Header "SEE ALSO" +Most behaviour, including environment variables and configuration, +comes directly from \s-1CPAN\s0.pm. +.SH "SOURCE AVAILABILITY" +.IX Header "SOURCE AVAILABILITY" +This code is in Github in the \s-1CPAN\s0.pm repository: +.PP +.Vb 1 +\& https://github.com/andk/cpanpm +.Ve +.PP +The source used to be tracked separately in another GitHub repo, +but the canonical source is now in the above repo. +.SH "CREDITS" +.IX Header "CREDITS" +Japheth Cleaver added the bits to allow a forced install (\-f). +.PP +Jim Brandt suggest and provided the initial implementation for the +up-to-date and Changes features. +.PP +Adam Kennedy pointed out that \fBexit()\fR causes problems on Windows +where this script ends up with a .bat extension +.SH "AUTHOR" +.IX Header "AUTHOR" +brian d foy, \f(CW\*(C`<bdfoy@cpan.org>\*(C'\fR +.SH "COPYRIGHT" +.IX Header "COPYRIGHT" +Copyright (c) 2001\-2015, brian d foy, All Rights Reserved. +.PP +You may redistribute this under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/App.Cpan.3pm b/tools/msys/usr/share/man/man3/App.Cpan.3pm new file mode 100644 index 0000000000..218c57afbe --- /dev/null +++ b/tools/msys/usr/share/man/man3/App.Cpan.3pm @@ -0,0 +1,384 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "App::Cpan 3" +.TH App::Cpan 3 "2020-06-13" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +App::Cpan \- easily interact with CPAN from the command line +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 2 +\& # with arguments and no switches, installs specified modules +\& cpan module_name [ module_name ... ] +\& +\& # with switches, installs modules with extra behavior +\& cpan [\-cfFimtTw] module_name [ module_name ... ] +\& +\& # use local::lib +\& cpan \-I module_name [ module_name ... ] +\& +\& # one time mirror override for faster mirrors +\& cpan \-p ... +\& +\& # with just the dot, install from the distribution in the +\& # current directory +\& cpan . +\& +\& # without arguments, starts CPAN.pm shell +\& cpan +\& +\& # without arguments, but some switches +\& cpan [\-ahpruvACDLOPX] +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +This script provides a command interface (not a shell) to \s-1CPAN.\s0 At the +moment it uses \s-1CPAN\s0.pm to do the work, but it is not a one-shot command +runner for \s-1CPAN\s0.pm. +.SS "Options" +.IX Subsection "Options" +.IP "\-a" 4 +.IX Item "-a" +Creates a \s-1CPAN\s0.pm autobundle with CPAN::Shell\->autobundle. +.IP "\-A module [ module ... ]" 4 +.IX Item "-A module [ module ... ]" +Shows the primary maintainers for the specified modules. +.IP "\-c module" 4 +.IX Item "-c module" +Runs a `make clean` in the specified module's directories. +.IP "\-C module [ module ... ]" 4 +.IX Item "-C module [ module ... ]" +Show the \fIChanges\fR files for the specified modules +.IP "\-D module [ module ... ]" 4 +.IX Item "-D module [ module ... ]" +Show the module details. This prints one line for each out-of-date module +(meaning, modules locally installed but have newer versions on \s-1CPAN\s0). +Each line has three columns: module name, local version, and \s-1CPAN\s0 +version. +.IP "\-f" 4 +.IX Item "-f" +Force the specified action, when it normally would have failed. Use this +to install a module even if its tests fail. When you use this option, +\&\-i is not optional for installing a module when you need to force it: +.Sp +.Vb 1 +\& % cpan \-f \-i Module::Foo +.Ve +.IP "\-F" 4 +.IX Item "-F" +Turn off \s-1CPAN\s0.pm's attempts to lock anything. You should be careful with +this since you might end up with multiple scripts trying to muck in the +same directory. This isn't so much of a concern if you're loading a special +config with \f(CW\*(C`\-j\*(C'\fR, and that config sets up its own work directories. +.IP "\-g module [ module ... ]" 4 +.IX Item "-g module [ module ... ]" +Downloads to the current directory the latest distribution of the module. +.IP "\-G module [ module ... ]" 4 +.IX Item "-G module [ module ... ]" +\&\s-1UNIMPLEMENTED\s0 +.Sp +Download to the current directory the latest distribution of the +modules, unpack each distribution, and create a git repository for each +distribution. +.Sp +If you want this feature, check out Yanick Champoux's \f(CW\*(C`Git::CPAN::Patch\*(C'\fR +distribution. +.IP "\-h" 4 +.IX Item "-h" +Print a help message and exit. When you specify \f(CW\*(C`\-h\*(C'\fR, it ignores all +of the other options and arguments. +.IP "\-i module [ module ... ]" 4 +.IX Item "-i module [ module ... ]" +Install the specified modules. With no other switches, this switch +is implied. +.IP "\-I" 4 +.IX Item "-I" +Load \f(CW\*(C`local::lib\*(C'\fR (think like \f(CW\*(C`\-I\*(C'\fR for loading lib paths). Too bad +\&\f(CW\*(C`\-l\*(C'\fR was already taken. +.IP "\-j Config.pm" 4 +.IX Item "-j Config.pm" +Load the file that has the \s-1CPAN\s0 configuration data. This should have the +same format as the standard \fICPAN/Config.pm\fR file, which defines +\&\f(CW$CPAN::Config\fR as an anonymous hash. +.Sp +If the file does not exist, \f(CW\*(C`cpan\*(C'\fR dies. +.IP "\-J" 4 +.IX Item "-J" +Dump the configuration in the same format that \s-1CPAN\s0.pm uses. This is useful +for checking the configuration as well as using the dump as a starting point +for a new, custom configuration. +.IP "\-l" 4 +.IX Item "-l" +List all installed modules with their versions +.IP "\-L author [ author ... ]" 4 +.IX Item "-L author [ author ... ]" +List the modules by the specified authors. +.IP "\-m" 4 +.IX Item "-m" +Make the specified modules. +.IP "\-M mirror1,mirror2,..." 4 +.IX Item "-M mirror1,mirror2,..." +A comma-separated list of mirrors to use for just this run. The \f(CW\*(C`\-P\*(C'\fR +option can find them for you automatically. +.IP "\-n" 4 +.IX Item "-n" +Do a dry run, but don't actually install anything. (unimplemented) +.IP "\-O" 4 +.IX Item "-O" +Show the out-of-date modules. +.IP "\-p" 4 +.IX Item "-p" +Ping the configured mirrors and print a report +.IP "\-P" 4 +.IX Item "-P" +Find the best mirrors you could be using and use them for the current +session. +.IP "\-r" 4 +.IX Item "-r" +Recompiles dynamically loaded modules with CPAN::Shell\->recompile. +.IP "\-s" 4 +.IX Item "-s" +Drop in the \s-1CPAN\s0.pm shell. This command does this automatically if you don't +specify any arguments. +.IP "\-t module [ module ... ]" 4 +.IX Item "-t module [ module ... ]" +Run a `make test` on the specified modules. +.IP "\-T" 4 +.IX Item "-T" +Do not test modules. Simply install them. +.IP "\-u" 4 +.IX Item "-u" +Upgrade all installed modules. Blindly doing this can really break things, +so keep a backup. +.IP "\-v" 4 +.IX Item "-v" +Print the script version and \s-1CPAN\s0.pm version then exit. +.IP "\-V" 4 +.IX Item "-V" +Print detailed information about the cpan client. +.IP "\-w" 4 +.IX Item "-w" +\&\s-1UNIMPLEMENTED\s0 +.Sp +Turn on cpan warnings. This checks various things, like directory permissions, +and tells you about problems you might have. +.IP "\-x module [ module ... ]" 4 +.IX Item "-x module [ module ... ]" +Find close matches to the named modules that you think you might have +mistyped. This requires the optional installation of Text::Levenshtein or +Text::Levenshtein::Damerau. +.IP "\-X" 4 +.IX Item "-X" +Dump all the namespaces to standard output. +.SS "Examples" +.IX Subsection "Examples" +.Vb 2 +\& # print a help message +\& cpan \-h +\& +\& # print the version numbers +\& cpan \-v +\& +\& # create an autobundle +\& cpan \-a +\& +\& # recompile modules +\& cpan \-r +\& +\& # upgrade all installed modules +\& cpan \-u +\& +\& # install modules ( sole \-i is optional ) +\& cpan \-i Netscape::Booksmarks Business::ISBN +\& +\& # force install modules ( must use \-i ) +\& cpan \-fi CGI::Minimal URI +\& +\& # install modules but without testing them +\& cpan \-Ti CGI::Minimal URI +.Ve +.SS "Environment variables" +.IX Subsection "Environment variables" +There are several components in \s-1CPAN\s0.pm that use environment variables. +The build tools, ExtUtils::MakeMaker and Module::Build use some, +while others matter to the levels above them. Some of these are specified +by the Perl Toolchain Gang: +.PP +Lancaster Concensus: <https://github.com/Perl\-Toolchain\-Gang/toolchain\-site/blob/master/lancaster\-consensus.md> +.PP +Oslo Concensus: <https://github.com/Perl\-Toolchain\-Gang/toolchain\-site/blob/master/oslo\-consensus.md> +.IP "\s-1NONINTERACTIVE_TESTING\s0" 4 +.IX Item "NONINTERACTIVE_TESTING" +Assume no one is paying attention and skips prompts for distributions +that do that correctly. \f(CWcpan(1)\fR sets this to \f(CW1\fR unless it already +has a value (even if that value is false). +.IP "\s-1PERL_MM_USE_DEFAULT\s0" 4 +.IX Item "PERL_MM_USE_DEFAULT" +Use the default answer for a prompted questions. \f(CWcpan(1)\fR sets this +to \f(CW1\fR unless it already has a value (even if that value is false). +.IP "\s-1CPAN_OPTS\s0" 4 +.IX Item "CPAN_OPTS" +As with \f(CW\*(C`PERL5OPT\*(C'\fR, a string of additional \f(CWcpan(1)\fR options to +add to those you specify on the command line. +.IP "\s-1CPANSCRIPT_LOGLEVEL\s0" 4 +.IX Item "CPANSCRIPT_LOGLEVEL" +The log level to use, with either the embedded, minimal logger or +Log::Log4perl if it is installed. Possible values are the same as +the \f(CW\*(C`Log::Log4perl\*(C'\fR levels: \f(CW\*(C`TRACE\*(C'\fR, \f(CW\*(C`DEBUG\*(C'\fR, \f(CW\*(C`INFO\*(C'\fR, \f(CW\*(C`WARN\*(C'\fR, +\&\f(CW\*(C`ERROR\*(C'\fR, and \f(CW\*(C`FATAL\*(C'\fR. The default is \f(CW\*(C`INFO\*(C'\fR. +.IP "\s-1GIT_COMMAND\s0" 4 +.IX Item "GIT_COMMAND" +The path to the \f(CW\*(C`git\*(C'\fR binary to use for the Git features. The default +is \f(CW\*(C`/usr/local/bin/git\*(C'\fR. +.SS "Methods" +.IX Subsection "Methods" +.IP "\fBrun()\fR" 4 +.IX Item "run()" +Just do it. +.Sp +The \f(CW\*(C`run\*(C'\fR method returns 0 on success and a positive number on +failure. See the section on \s-1EXIT CODES\s0 for details on the values. +.Sp +\&\s-1CPAN\s0.pm sends all the good stuff either to \s-1STDOUT,\s0 or to a temp +file if \f(CW$CPAN::Be_Silent\fR is set. I have to intercept that output +so I can find out what happened. +.Sp +Stolen from File::Path::Expand +.SH "EXIT VALUES" +.IX Header "EXIT VALUES" +The script exits with zero if it thinks that everything worked, or a +positive number if it thinks that something failed. Note, however, that +in some cases it has to divine a failure by the output of things it does +not control. For now, the exit codes are vague: +.PP +.Vb 1 +\& 1 An unknown error +\& +\& 2 The was an external problem +\& +\& 4 There was an internal problem with the script +\& +\& 8 A module failed to install +.Ve +.SH "TO DO" +.IX Header "TO DO" +* There is initial support for Log4perl if it is available, but I +haven't gone through everything to make the NullLogger work out +correctly if Log4perl is not installed. +.PP +* When I capture \s-1CPAN\s0.pm output, I need to check for errors and +report them to the user. +.PP +* Warnings switch +.PP +* Check then exit +.SH "BUGS" +.IX Header "BUGS" +* none noted +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\s-1CPAN\s0, App::cpanminus +.SH "SOURCE AVAILABILITY" +.IX Header "SOURCE AVAILABILITY" +This code is in Github in the \s-1CPAN\s0.pm repository: +.PP +.Vb 1 +\& https://github.com/andk/cpanpm +.Ve +.PP +The source used to be tracked separately in another GitHub repo, +but the canonical source is now in the above repo. +.SH "CREDITS" +.IX Header "CREDITS" +Japheth Cleaver added the bits to allow a forced install (\f(CW\*(C`\-f\*(C'\fR). +.PP +Jim Brandt suggest and provided the initial implementation for the +up-to-date and Changes features. +.PP +Adam Kennedy pointed out that \f(CW\*(C`exit()\*(C'\fR causes problems on Windows +where this script ends up with a .bat extension +.PP +David Golden helps integrate this into the \f(CW\*(C`CPAN.pm\*(C'\fR repos. +.PP +Jim Keenan fixed up various issues with _download +.SH "AUTHOR" +.IX Header "AUTHOR" +brian d foy, \f(CW\*(C`<bdfoy@cpan.org>\*(C'\fR +.SH "COPYRIGHT" +.IX Header "COPYRIGHT" +Copyright (c) 2001\-2018, brian d foy, All Rights Reserved. +.PP +You may redistribute this under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/CPAN.3pm b/tools/msys/usr/share/man/man3/CPAN.3pm new file mode 100644 index 0000000000..0915ec3877 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.3pm @@ -0,0 +1,2556 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN 3" +.TH CPAN 3 "2020-06-13" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN \- query, download and build perl modules from CPAN sites +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +Interactive mode: +.PP +.Vb 1 +\& perl \-MCPAN \-e shell +.Ve +.PP +\&\-\-or\*(-- +.PP +.Vb 1 +\& cpan +.Ve +.PP +Basic commands: +.PP +.Vb 1 +\& # Modules: +\& +\& cpan> install Acme::Meta # in the shell +\& +\& CPAN::Shell\->install("Acme::Meta"); # in perl +\& +\& # Distributions: +\& +\& cpan> install NWCLARK/Acme\-Meta\-0.02.tar.gz # in the shell +\& +\& CPAN::Shell\-> +\& install("NWCLARK/Acme\-Meta\-0.02.tar.gz"); # in perl +\& +\& # module objects: +\& +\& $mo = CPAN::Shell\->expandany($mod); +\& $mo = CPAN::Shell\->expand("Module",$mod); # same thing +\& +\& # distribution objects: +\& +\& $do = CPAN::Shell\->expand("Module",$mod)\->distribution; +\& $do = CPAN::Shell\->expandany($distro); # same thing +\& $do = CPAN::Shell\->expand("Distribution", +\& $distro); # same thing +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +The \s-1CPAN\s0 module automates or at least simplifies the make and install +of perl modules and extensions. It includes some primitive searching +capabilities and knows how to use \s-1LWP,\s0 HTTP::Tiny, Net::FTP and certain +external download clients to fetch distributions from the net. +.PP +These are fetched from one or more mirrored \s-1CPAN\s0 (Comprehensive +Perl Archive Network) sites and unpacked in a dedicated directory. +.PP +The \s-1CPAN\s0 module also supports named and versioned +\&\fIbundles\fR of modules. Bundles simplify handling of sets of +related modules. See Bundles below. +.PP +The package contains a session manager and a cache manager. The +session manager keeps track of what has been fetched, built, and +installed in the current session. The cache manager keeps track of the +disk space occupied by the make processes and deletes excess space +using a simple \s-1FIFO\s0 mechanism. +.PP +All methods provided are accessible in a programmer style and in an +interactive shell style. +.ie n .SS "CPAN::shell([$prompt, $command]) Starting Interactive Mode" +.el .SS "CPAN::shell([$prompt, \f(CW$command\fP]) Starting Interactive Mode" +.IX Subsection "CPAN::shell([$prompt, $command]) Starting Interactive Mode" +Enter interactive mode by running +.PP +.Vb 1 +\& perl \-MCPAN \-e shell +.Ve +.PP +or +.PP +.Vb 1 +\& cpan +.Ve +.PP +which puts you into a readline interface. If \f(CW\*(C`Term::ReadKey\*(C'\fR and +either of \f(CW\*(C`Term::ReadLine::Perl\*(C'\fR or \f(CW\*(C`Term::ReadLine::Gnu\*(C'\fR are installed, +history and command completion are supported. +.PP +Once at the command line, type \f(CW\*(C`h\*(C'\fR for one-page help +screen; the rest should be self-explanatory. +.PP +The function call \f(CW\*(C`shell\*(C'\fR takes two optional arguments: one the +prompt, the second the default initial command line (the latter +only works if a real ReadLine interface module is installed). +.PP +The most common uses of the interactive modes are +.IP "Searching for authors, bundles, distribution files and modules" 2 +.IX Item "Searching for authors, bundles, distribution files and modules" +There are corresponding one-letter commands \f(CW\*(C`a\*(C'\fR, \f(CW\*(C`b\*(C'\fR, \f(CW\*(C`d\*(C'\fR, and \f(CW\*(C`m\*(C'\fR +for each of the four categories and another, \f(CW\*(C`i\*(C'\fR for any of the +mentioned four. Each of the four entities is implemented as a class +with slightly differing methods for displaying an object. +.Sp +Arguments to these commands are either strings exactly matching +the identification string of an object, or regular expressions +matched case-insensitively against various attributes of the +objects. The parser only recognizes a regular expression when you +enclose it with slashes. +.Sp +The principle is that the number of objects found influences how an +item is displayed. If the search finds one item, the result is +displayed with the rather verbose method \f(CW\*(C`as_string\*(C'\fR, but if +more than one is found, each object is displayed with the terse method +\&\f(CW\*(C`as_glimpse\*(C'\fR. +.Sp +Examples: +.Sp +.Vb 10 +\& cpan> m Acme::MetaSyntactic +\& Module id = Acme::MetaSyntactic +\& CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) +\& CPAN_VERSION 0.99 +\& CPAN_FILE B/BO/BOOK/Acme\-MetaSyntactic\-0.99.tar.gz +\& UPLOAD_DATE 2006\-11\-06 +\& MANPAGE Acme::MetaSyntactic \- Themed metasyntactic variables names +\& INST_FILE /usr/local/lib/perl/5.10.0/Acme/MetaSyntactic.pm +\& INST_VERSION 0.99 +\& cpan> a BOOK +\& Author id = BOOK +\& EMAIL [...] +\& FULLNAME Philippe Bruhat (BooK) +\& cpan> d BOOK/Acme\-MetaSyntactic\-0.99.tar.gz +\& Distribution id = B/BO/BOOK/Acme\-MetaSyntactic\-0.99.tar.gz +\& CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) +\& CONTAINSMODS Acme::MetaSyntactic Acme::MetaSyntactic::Alias [...] +\& UPLOAD_DATE 2006\-11\-06 +\& cpan> m /lorem/ +\& Module = Acme::MetaSyntactic::loremipsum (BOOK/Acme\-MetaSyntactic\-0.99.tar.gz) +\& Module Text::Lorem (ADEOLA/Text\-Lorem\-0.3.tar.gz) +\& Module Text::Lorem::More (RKRIMEN/Text\-Lorem\-More\-0.12.tar.gz) +\& Module Text::Lorem::More::Source (RKRIMEN/Text\-Lorem\-More\-0.12.tar.gz) +\& cpan> i /berlin/ +\& Distribution BEATNIK/Filter\-NumberLines\-0.02.tar.gz +\& Module = DateTime::TimeZone::Europe::Berlin (DROLSKY/DateTime\-TimeZone\-0.7904.tar.gz) +\& Module Filter::NumberLines (BEATNIK/Filter\-NumberLines\-0.02.tar.gz) +\& Author [...] +.Ve +.Sp +The examples illustrate several aspects: the first three queries +target modules, authors, or distros directly and yield exactly one +result. The last two use regular expressions and yield several +results. The last one targets all of bundles, modules, authors, and +distros simultaneously. When more than one result is available, they +are printed in one-line format. +.ie n .IP """get"", ""make"", ""test"", ""install"", ""clean"" modules or distributions" 2 +.el .IP "\f(CWget\fR, \f(CWmake\fR, \f(CWtest\fR, \f(CWinstall\fR, \f(CWclean\fR modules or distributions" 2 +.IX Item "get, make, test, install, clean modules or distributions" +These commands take any number of arguments and investigate what is +necessary to perform the action. Argument processing is as follows: +.Sp +.Vb 5 +\& known module name in format Foo/Bar.pm module +\& other embedded slash distribution +\& \- with trailing slash dot directory +\& enclosing slashes regexp +\& known module name in format Foo::Bar module +.Ve +.Sp +If the argument is a distribution file name (recognized by embedded +slashes), it is processed. If it is a module, \s-1CPAN\s0 determines the +distribution file in which this module is included and processes that, +following any dependencies named in the module's \s-1META\s0.yml or +Makefile.PL (this behavior is controlled by the configuration +parameter \f(CW\*(C`prerequisites_policy\*(C'\fR). If an argument is enclosed in +slashes it is treated as a regular expression: it is expanded and if +the result is a single object (distribution, bundle or module), this +object is processed. +.Sp +Example: +.Sp +.Vb 3 +\& install Dummy::Perl # installs the module +\& install AUXXX/Dummy\-Perl\-3.14.tar.gz # installs that distribution +\& install /Dummy\-Perl\-3.14/ # same if the regexp is unambiguous +.Ve +.Sp +\&\f(CW\*(C`get\*(C'\fR downloads a distribution file and untars or unzips it, \f(CW\*(C`make\*(C'\fR +builds it, \f(CW\*(C`test\*(C'\fR runs the test suite, and \f(CW\*(C`install\*(C'\fR installs it. +.Sp +Any \f(CW\*(C`make\*(C'\fR or \f(CW\*(C`test\*(C'\fR is run unconditionally. An +.Sp +.Vb 1 +\& install <distribution_file> +.Ve +.Sp +is also run unconditionally. But for +.Sp +.Vb 1 +\& install <module> +.Ve +.Sp +\&\s-1CPAN\s0 checks whether an install is needed and prints +\&\fImodule up to date\fR if the distribution file containing +the module doesn't need updating. +.Sp +\&\s-1CPAN\s0 also keeps track of what it has done within the current session +and doesn't try to build a package a second time regardless of whether it +succeeded or not. It does not repeat a test run if the test +has been run successfully before. Same for install runs. +.Sp +The \f(CW\*(C`force\*(C'\fR pragma may precede another command (currently: \f(CW\*(C`get\*(C'\fR, +\&\f(CW\*(C`make\*(C'\fR, \f(CW\*(C`test\*(C'\fR, or \f(CW\*(C`install\*(C'\fR) to execute the command from scratch +and attempt to continue past certain errors. See the section below on +the \f(CW\*(C`force\*(C'\fR and the \f(CW\*(C`fforce\*(C'\fR pragma. +.Sp +The \f(CW\*(C`notest\*(C'\fR pragma skips the test part in the build +process. +.Sp +Example: +.Sp +.Vb 1 +\& cpan> notest install Tk +.Ve +.Sp +A \f(CW\*(C`clean\*(C'\fR command results in a +.Sp +.Vb 1 +\& make clean +.Ve +.Sp +being executed within the distribution file's working directory. +.ie n .IP """readme"", ""perldoc"", ""look"" module or distribution" 2 +.el .IP "\f(CWreadme\fR, \f(CWperldoc\fR, \f(CWlook\fR module or distribution" 2 +.IX Item "readme, perldoc, look module or distribution" +\&\f(CW\*(C`readme\*(C'\fR displays the \s-1README\s0 file of the associated distribution. +\&\f(CW\*(C`Look\*(C'\fR gets and untars (if not yet done) the distribution file, +changes to the appropriate directory and opens a subshell process in +that directory. \f(CW\*(C`perldoc\*(C'\fR displays the module's pod documentation +in html or plain text format. +.ie n .IP """ls"" author" 2 +.el .IP "\f(CWls\fR author" 2 +.IX Item "ls author" +.PD 0 +.ie n .IP """ls"" globbing_expression" 2 +.el .IP "\f(CWls\fR globbing_expression" 2 +.IX Item "ls globbing_expression" +.PD +The first form lists all distribution files in and below an author's +\&\s-1CPAN\s0 directory as stored in the \s-1CHECKSUMS\s0 files distributed on +\&\s-1CPAN.\s0 The listing recurses into subdirectories. +.Sp +The second form limits or expands the output with shell +globbing as in the following examples: +.Sp +.Vb 3 +\& ls JV/make* +\& ls GSAR/*make* +\& ls */*make* +.Ve +.Sp +The last example is very slow and outputs extra progress indicators +that break the alignment of the result. +.Sp +Note that globbing only lists directories explicitly asked for, for +example FOO/* will not list FOO/bar/Acme\-Sthg\-n.nn.tar.gz. This may be +regarded as a bug that may be changed in some future version. +.ie n .IP """failed""" 2 +.el .IP "\f(CWfailed\fR" 2 +.IX Item "failed" +The \f(CW\*(C`failed\*(C'\fR command reports all distributions that failed on one of +\&\f(CW\*(C`make\*(C'\fR, \f(CW\*(C`test\*(C'\fR or \f(CW\*(C`install\*(C'\fR for some reason in the currently +running shell session. +.IP "Persistence between sessions" 2 +.IX Item "Persistence between sessions" +If the \f(CW\*(C`YAML\*(C'\fR or the \f(CW\*(C`YAML::Syck\*(C'\fR module is installed a record of +the internal state of all modules is written to disk after each step. +The files contain a signature of the currently running perl version +for later perusal. +.Sp +If the configurations variable \f(CW\*(C`build_dir_reuse\*(C'\fR is set to a true +value, then \s-1CPAN\s0.pm reads the collected \s-1YAML\s0 files. If the stored +signature matches the currently running perl, the stored state is +loaded into memory such that persistence between sessions +is effectively established. +.ie n .IP "The ""force"" and the ""fforce"" pragma" 2 +.el .IP "The \f(CWforce\fR and the \f(CWfforce\fR pragma" 2 +.IX Item "The force and the fforce pragma" +To speed things up in complex installation scenarios, \s-1CPAN\s0.pm keeps +track of what it has already done and refuses to do some things a +second time. A \f(CW\*(C`get\*(C'\fR, a \f(CW\*(C`make\*(C'\fR, and an \f(CW\*(C`install\*(C'\fR are not repeated. +A \f(CW\*(C`test\*(C'\fR is repeated only if the previous test was unsuccessful. The +diagnostic message when \s-1CPAN\s0.pm refuses to do something a second time +is one of \fIHas already been \fR\f(CW\*(C`unwrapped|made|tested successfully\*(C'\fR or +something similar. Another situation where \s-1CPAN\s0 refuses to act is an +\&\f(CW\*(C`install\*(C'\fR if the corresponding \f(CW\*(C`test\*(C'\fR was not successful. +.Sp +In all these cases, the user can override this stubborn behaviour by +prepending the command with the word force, for example: +.Sp +.Vb 4 +\& cpan> force get Foo +\& cpan> force make AUTHOR/Bar\-3.14.tar.gz +\& cpan> force test Baz +\& cpan> force install Acme::Meta +.Ve +.Sp +Each \fIforced\fR command is executed with the corresponding part of its +memory erased. +.Sp +The \f(CW\*(C`fforce\*(C'\fR pragma is a variant that emulates a \f(CW\*(C`force get\*(C'\fR which +erases the entire memory followed by the action specified, effectively +restarting the whole get/make/test/install procedure from scratch. +.IP "Lockfile" 2 +.IX Item "Lockfile" +Interactive sessions maintain a lockfile, by default \f(CW\*(C`~/.cpan/.lock\*(C'\fR. +Batch jobs can run without a lockfile and not disturb each other. +.Sp +The shell offers to run in \fIdowngraded mode\fR when another process is +holding the lockfile. This is an experimental feature that is not yet +tested very well. This second shell then does not write the history +file, does not use the metadata file, and has a different prompt. +.IP "Signals" 2 +.IX Item "Signals" +\&\s-1CPAN\s0.pm installs signal handlers for \s-1SIGINT\s0 and \s-1SIGTERM.\s0 While you are +in the cpan-shell, it is intended that you can press \f(CW\*(C`^C\*(C'\fR anytime and +return to the cpan-shell prompt. A \s-1SIGTERM\s0 will cause the cpan-shell +to clean up and leave the shell loop. You can emulate the effect of a +\&\s-1SIGTERM\s0 by sending two consecutive SIGINTs, which usually means by +pressing \f(CW\*(C`^C\*(C'\fR twice. +.Sp +\&\s-1CPAN\s0.pm ignores \s-1SIGPIPE.\s0 If the user sets \f(CW\*(C`inactivity_timeout\*(C'\fR, a +\&\s-1SIGALRM\s0 is used during the run of the \f(CW\*(C`perl Makefile.PL\*(C'\fR or \f(CW\*(C`perl +Build.PL\*(C'\fR subprocess. A \s-1SIGALRM\s0 is also used during module version +parsing, and is controlled by \f(CW\*(C`version_timeout\*(C'\fR. +.SS "CPAN::Shell" +.IX Subsection "CPAN::Shell" +The commands available in the shell interface are methods in +the package CPAN::Shell. If you enter the shell command, your +input is split by the \fBText::ParseWords::shellwords()\fR routine, which +acts like most shells do. The first word is interpreted as the +method to be invoked, and the rest of the words are treated as the method's arguments. +Continuation lines are supported by ending a line with a +literal backslash. +.SS "autobundle" +.IX Subsection "autobundle" +\&\f(CW\*(C`autobundle\*(C'\fR writes a bundle file into the +\&\f(CW\*(C`$CPAN::Config\->{cpan_home}/Bundle\*(C'\fR directory. The file contains +a list of all modules that are both available from \s-1CPAN\s0 and currently +installed within \f(CW@INC\fR. Duplicates of each distribution are suppressed. +The name of the bundle file is based on the current date and a +counter, e.g. \fIBundle/Snapshot_2012_05_21_00.pm\fR. This is installed +again by running \f(CW\*(C`cpan Bundle::Snapshot_2012_05_21_00\*(C'\fR, or installing +\&\f(CW\*(C`Bundle::Snapshot_2012_05_21_00\*(C'\fR from the \s-1CPAN\s0 shell. +.PP +Return value: path to the written file. +.SS "hosts" +.IX Subsection "hosts" +Note: this feature is still in alpha state and may change in future +versions of \s-1CPAN\s0.pm +.PP +This commands provides a statistical overview over recent download +activities. The data for this is collected in the \s-1YAML\s0 file +\&\f(CW\*(C`FTPstats.yml\*(C'\fR in your \f(CW\*(C`cpan_home\*(C'\fR directory. If no \s-1YAML\s0 module is +configured or \s-1YAML\s0 not installed, no stats are provided. +.IP "install_tested" 4 +.IX Item "install_tested" +Install all distributions that have been tested successfully but have +not yet been installed. See also \f(CW\*(C`is_tested\*(C'\fR. +.IP "is_tested" 4 +.IX Item "is_tested" +List all build directories of distributions that have been tested +successfully but have not yet been installed. See also +\&\f(CW\*(C`install_tested\*(C'\fR. +.SS "mkmyconfig" +.IX Subsection "mkmyconfig" +\&\fBmkmyconfig()\fR writes your own CPAN::MyConfig file into your \f(CW\*(C`~/.cpan/\*(C'\fR +directory so that you can save your own preferences instead of the +system-wide ones. +.SS "r [Module|/Regexp/]..." +.IX Subsection "r [Module|/Regexp/]..." +scans current perl installation for modules that have a newer version +available on \s-1CPAN\s0 and provides a list of them. If called without +argument, all potential upgrades are listed; if called with arguments +the list is filtered to the modules and regexps given as arguments. +.PP +The listing looks something like this: +.PP +.Vb 10 +\& Package namespace installed latest in CPAN file +\& CPAN 1.94_64 1.9600 ANDK/CPAN\-1.9600.tar.gz +\& CPAN::Reporter 1.1801 1.1902 DAGOLDEN/CPAN\-Reporter\-1.1902.tar.gz +\& YAML 0.70 0.73 INGY/YAML\-0.73.tar.gz +\& YAML::Syck 1.14 1.17 AVAR/YAML\-Syck\-1.17.tar.gz +\& YAML::Tiny 1.44 1.50 ADAMK/YAML\-Tiny\-1.50.tar.gz +\& CGI 3.43 3.55 MARKSTOS/CGI.pm\-3.55.tar.gz +\& Module::Build::YAML 1.40 1.41 DAGOLDEN/Module\-Build\-0.3800.tar.gz +\& TAP::Parser::Result::YAML 3.22 3.23 ANDYA/Test\-Harness\-3.23.tar.gz +\& YAML::XS 0.34 0.35 INGY/YAML\-LibYAML\-0.35.tar.gz +.Ve +.PP +It suppresses duplicates in the column \f(CW\*(C`in CPAN file\*(C'\fR such that +distributions with many upgradeable modules are listed only once. +.PP +Note that the list is not sorted. +.SS "recent ***EXPERIMENTAL COMMAND***" +.IX Subsection "recent ***EXPERIMENTAL COMMAND***" +The \f(CW\*(C`recent\*(C'\fR command downloads a list of recent uploads to \s-1CPAN\s0 and +displays them \fIslowly\fR. While the command is running, a \f(CW$SIG\fR{\s-1INT\s0} +exits the loop after displaying the current item. +.PP +\&\fBNote\fR: This command requires XML::LibXML installed. +.PP +\&\fBNote\fR: This whole command currently is just a hack and will +probably change in future versions of \s-1CPAN\s0.pm, but the general +approach will likely remain. +.PP +\&\fBNote\fR: See also smoke +.SS "recompile" +.IX Subsection "recompile" +\&\fBrecompile()\fR is a special command that takes no argument and +runs the make/test/install cycle with brute force over all installed +dynamically loadable extensions (a.k.a. \s-1XS\s0 modules) with 'force' in +effect. The primary purpose of this command is to finish a network +installation. Imagine you have a common source tree for two different +architectures. You decide to do a completely independent fresh +installation. You start on one architecture with the help of a Bundle +file produced earlier. \s-1CPAN\s0 installs the whole Bundle for you, but +when you try to repeat the job on the second architecture, \s-1CPAN\s0 +responds with a \f(CW"Foo up to date"\fR message for all modules. So you +invoke \s-1CPAN\s0's recompile on the second architecture and you're done. +.PP +Another popular use for \f(CW\*(C`recompile\*(C'\fR is to act as a rescue in case your +perl breaks binary compatibility. If one of the modules that \s-1CPAN\s0 uses +is in turn depending on binary compatibility (so you cannot run \s-1CPAN\s0 +commands), then you should try the CPAN::Nox module for recovery. +.SS "report Bundle|Distribution|Module" +.IX Subsection "report Bundle|Distribution|Module" +The \f(CW\*(C`report\*(C'\fR command temporarily turns on the \f(CW\*(C`test_report\*(C'\fR config +variable, then runs the \f(CW\*(C`force test\*(C'\fR command with the given +arguments. The \f(CW\*(C`force\*(C'\fR pragma reruns the tests and repeats +every step that might have failed before. +.SS "smoke ***EXPERIMENTAL COMMAND***" +.IX Subsection "smoke ***EXPERIMENTAL COMMAND***" +\&\fB*** \s-1WARNING:\s0 this command downloads and executes software from \s-1CPAN\s0 to +your computer of completely unknown status. You should never do +this with your normal account and better have a dedicated well +separated and secured machine to do this. ***\fR +.PP +The \f(CW\*(C`smoke\*(C'\fR command takes the list of recent uploads to \s-1CPAN\s0 as +provided by the \f(CW\*(C`recent\*(C'\fR command and tests them all. While the +command is running \f(CW$SIG\fR{\s-1INT\s0} is defined to mean that the current item +shall be skipped. +.PP +\&\fBNote\fR: This whole command currently is just a hack and will +probably change in future versions of \s-1CPAN\s0.pm, but the general +approach will likely remain. +.PP +\&\fBNote\fR: See also recent +.SS "upgrade [Module|/Regexp/]..." +.IX Subsection "upgrade [Module|/Regexp/]..." +The \f(CW\*(C`upgrade\*(C'\fR command first runs an \f(CW\*(C`r\*(C'\fR command with the given +arguments and then installs the newest versions of all modules that +were listed by that. +.ie n .SS "The four ""CPAN::*"" Classes: Author, Bundle, Module, Distribution" +.el .SS "The four \f(CWCPAN::*\fP Classes: Author, Bundle, Module, Distribution" +.IX Subsection "The four CPAN::* Classes: Author, Bundle, Module, Distribution" +Although it may be considered internal, the class hierarchy does matter +for both users and programmer. \s-1CPAN\s0.pm deals with the four +classes mentioned above, and those classes all share a set of methods. Classical +single polymorphism is in effect. A metaclass object registers all +objects of all kinds and indexes them with a string. The strings +referencing objects have a separated namespace (well, not completely +separated): +.PP +.Vb 1 +\& Namespace Class +\& +\& words containing a "/" (slash) Distribution +\& words starting with Bundle:: Bundle +\& everything else Module or Author +.Ve +.PP +Modules know their associated Distribution objects. They always refer +to the most recent official release. Developers may mark their releases +as unstable development versions (by inserting an underscore into the +module version number which will also be reflected in the distribution +name when you run 'make dist'), so the really hottest and newest +distribution is not always the default. If a module Foo circulates +on \s-1CPAN\s0 in both version 1.23 and 1.23_90, \s-1CPAN\s0.pm offers a convenient +way to install version 1.23 by saying +.PP +.Vb 1 +\& install Foo +.Ve +.PP +This would install the complete distribution file (say +BAR/Foo\-1.23.tar.gz) with all accompanying material. But if you would +like to install version 1.23_90, you need to know where the +distribution file resides on \s-1CPAN\s0 relative to the authors/id/ +directory. If the author is \s-1BAR,\s0 this might be BAR/Foo\-1.23_90.tar.gz; +so you would have to say +.PP +.Vb 1 +\& install BAR/Foo\-1.23_90.tar.gz +.Ve +.PP +The first example will be driven by an object of the class +CPAN::Module, the second by an object of class CPAN::Distribution. +.SS "Integrating local directories" +.IX Subsection "Integrating local directories" +Note: this feature is still in alpha state and may change in future +versions of \s-1CPAN\s0.pm +.PP +Distribution objects are normally distributions from the \s-1CPAN,\s0 but +there is a slightly degenerate case for Distribution objects, too, of +projects held on the local disk. These distribution objects have the +same name as the local directory and end with a dot. A dot by itself +is also allowed for the current directory at the time \s-1CPAN\s0.pm was +used. All actions such as \f(CW\*(C`make\*(C'\fR, \f(CW\*(C`test\*(C'\fR, and \f(CW\*(C`install\*(C'\fR are applied +directly to that directory. This gives the command \f(CW\*(C`cpan .\*(C'\fR an +interesting touch: while the normal mantra of installing a \s-1CPAN\s0 module +without \s-1CPAN\s0.pm is one of +.PP +.Vb 5 +\& perl Makefile.PL perl Build.PL +\& ( go and get prerequisites ) +\& make ./Build +\& make test ./Build test +\& make install ./Build install +.Ve +.PP +the command \f(CW\*(C`cpan .\*(C'\fR does all of this at once. It figures out which +of the two mantras is appropriate, fetches and installs all +prerequisites, takes care of them recursively, and finally finishes the +installation of the module in the current directory, be it a \s-1CPAN\s0 +module or not. +.PP +The typical usage case is for private modules or working copies of +projects from remote repositories on the local disk. +.SS "Redirection" +.IX Subsection "Redirection" +The usual shell redirection symbols \f(CW\*(C` | \*(C'\fR and \f(CW\*(C`>\*(C'\fR are recognized +by the cpan shell \fBonly when surrounded by whitespace\fR. So piping to +pager or redirecting output into a file works somewhat as in a normal +shell, with the stipulation that you must type extra spaces. +.SS "Plugin support ***EXPERIMENTAL***" +.IX Subsection "Plugin support ***EXPERIMENTAL***" +Plugins are objects that implement any of currently eight methods: +.PP +.Vb 8 +\& pre_get +\& post_get +\& pre_make +\& post_make +\& pre_test +\& post_test +\& pre_install +\& post_install +.Ve +.PP +The \f(CW\*(C`plugin_list\*(C'\fR configuration parameter holds a list of strings of +the form +.PP +.Vb 1 +\& Modulename=arg0,arg1,arg2,arg3,... +.Ve +.PP +eg: +.PP +.Vb 1 +\& CPAN::Plugin::Flurb=dir,/opt/pkgs/flurb/raw,verbose,1 +.Ve +.PP +At run time, each listed plugin is instantiated as a singleton object +by running the equivalent of this pseudo code: +.PP +.Vb 3 +\& my $plugin = <string representation from config>; +\& <generate Modulename and arguments from $plugin>; +\& my $p = $instance{$plugin} ||= Modulename\->new($arg0,$arg1,...); +.Ve +.PP +The generated singletons are kept around from instantiation until the +end of the shell session. <plugin_list> can be reconfigured at any +time at run time. While the cpan shell is running, it checks all +activated plugins at each of the 8 reference points listed above and +runs the respective method if it is implemented for that object. The +method is called with the active CPAN::Distribution object passed in +as an argument. +.SH "CONFIGURATION" +.IX Header "CONFIGURATION" +When the \s-1CPAN\s0 module is used for the first time, a configuration +dialogue tries to determine a couple of site specific options. The +result of the dialog is stored in a hash reference \f(CW $CPAN::Config \fR +in a file CPAN/Config.pm. +.PP +Default values defined in the CPAN/Config.pm file can be +overridden in a user specific file: CPAN/MyConfig.pm. Such a file is +best placed in \f(CW\*(C`$HOME/.cpan/CPAN/MyConfig.pm\*(C'\fR, because \f(CW\*(C`$HOME/.cpan\*(C'\fR is +added to the search path of the \s-1CPAN\s0 module before the \fBuse()\fR or +\&\fBrequire()\fR statements. The mkmyconfig command writes this file for you. +.PP +The \f(CW\*(C`o conf\*(C'\fR command has various bells and whistles: +.IP "completion support" 4 +.IX Item "completion support" +If you have a ReadLine module installed, you can hit \s-1TAB\s0 at any point +of the commandline and \f(CW\*(C`o conf\*(C'\fR will offer you completion for the +built-in subcommands and/or config variable names. +.IP "displaying some help: o conf help" 4 +.IX Item "displaying some help: o conf help" +Displays a short help +.IP "displaying current values: o conf [\s-1KEY\s0]" 4 +.IX Item "displaying current values: o conf [KEY]" +Displays the current value(s) for this config variable. Without \s-1KEY,\s0 +displays all subcommands and config variables. +.Sp +Example: +.Sp +.Vb 1 +\& o conf shell +.Ve +.Sp +If \s-1KEY\s0 starts and ends with a slash, the string in between is +treated as a regular expression and only keys matching this regexp +are displayed +.Sp +Example: +.Sp +.Vb 1 +\& o conf /color/ +.Ve +.IP "changing of scalar values: o conf \s-1KEY VALUE\s0" 4 +.IX Item "changing of scalar values: o conf KEY VALUE" +Sets the config variable \s-1KEY\s0 to \s-1VALUE.\s0 The empty string can be +specified as usual in shells, with \f(CW\*(Aq\*(Aq\fR or \f(CW""\fR +.Sp +Example: +.Sp +.Vb 1 +\& o conf wget /usr/bin/wget +.Ve +.IP "changing of list values: o conf \s-1KEY\s0 SHIFT|UNSHIFT|PUSH|POP|SPLICE|LIST" 4 +.IX Item "changing of list values: o conf KEY SHIFT|UNSHIFT|PUSH|POP|SPLICE|LIST" +If a config variable name ends with \f(CW\*(C`list\*(C'\fR, it is a list. \f(CW\*(C`o conf +KEY shift\*(C'\fR removes the first element of the list, \f(CW\*(C`o conf KEY pop\*(C'\fR +removes the last element of the list. \f(CW\*(C`o conf KEYS unshift LIST\*(C'\fR +prepends a list of values to the list, \f(CW\*(C`o conf KEYS push LIST\*(C'\fR +appends a list of valued to the list. +.Sp +Likewise, \f(CW\*(C`o conf KEY splice LIST\*(C'\fR passes the \s-1LIST\s0 to the corresponding +splice command. +.Sp +Finally, any other list of arguments is taken as a new list value for +the \s-1KEY\s0 variable discarding the previous value. +.Sp +Examples: +.Sp +.Vb 3 +\& o conf urllist unshift http://cpan.dev.local/CPAN +\& o conf urllist splice 3 1 +\& o conf urllist http://cpan1.local http://cpan2.local ftp://ftp.perl.org +.Ve +.IP "reverting to saved: o conf defaults" 4 +.IX Item "reverting to saved: o conf defaults" +Reverts all config variables to the state in the saved config file. +.IP "saving the config: o conf commit" 4 +.IX Item "saving the config: o conf commit" +Saves all config variables to the current config file (CPAN/Config.pm +or CPAN/MyConfig.pm that was loaded at start). +.PP +The configuration dialog can be started any time later again by +issuing the command \f(CW\*(C` o conf init \*(C'\fR in the \s-1CPAN\s0 shell. A subset of +the configuration dialog can be run by issuing \f(CW\*(C`o conf init WORD\*(C'\fR +where \s-1WORD\s0 is any valid config variable or a regular expression. +.SS "Config Variables" +.IX Subsection "Config Variables" +The following keys in the hash reference \f(CW$CPAN::Config\fR are +currently defined: +.PP +.Vb 10 +\& allow_installing_module_downgrades +\& allow or disallow installing module downgrades +\& allow_installing_outdated_dists +\& allow or disallow installing modules that are +\& indexed in the cpan index pointing to a distro +\& with a higher distro\-version number +\& applypatch path to external prg +\& auto_commit commit all changes to config variables to disk +\& build_cache size of cache for directories to build modules +\& build_dir locally accessible directory to build modules +\& build_dir_reuse boolean if distros in build_dir are persistent +\& build_requires_install_policy +\& to install or not to install when a module is +\& only needed for building. yes|no|ask/yes|ask/no +\& bzip2 path to external prg +\& cache_metadata use serializer to cache metadata +\& check_sigs if signatures should be verified +\& cleanup_after_install +\& remove build directory immediately after a +\& successful install and remember that for the +\& duration of the session +\& colorize_debug Term::ANSIColor attributes for debugging output +\& colorize_output boolean if Term::ANSIColor should colorize output +\& colorize_print Term::ANSIColor attributes for normal output +\& colorize_warn Term::ANSIColor attributes for warnings +\& commandnumber_in_prompt +\& boolean if you want to see current command number +\& commands_quote preferred character to use for quoting external +\& commands when running them. Defaults to double +\& quote on Windows, single tick everywhere else; +\& can be set to space to disable quoting +\& connect_to_internet_ok +\& whether to ask if opening a connection is ok before +\& urllist is specified +\& cpan_home local directory reserved for this package +\& curl path to external prg +\& dontload_hash DEPRECATED +\& dontload_list arrayref: modules in the list will not be +\& loaded by the CPAN::has_inst() routine +\& ftp path to external prg +\& ftp_passive if set, the environment variable FTP_PASSIVE is set +\& for downloads +\& ftp_proxy proxy host for ftp requests +\& ftpstats_period max number of days to keep download statistics +\& ftpstats_size max number of items to keep in the download statistics +\& getcwd see below +\& gpg path to external prg +\& gzip location of external program gzip +\& halt_on_failure stop processing after the first failure of queued +\& items or dependencies +\& histfile file to maintain history between sessions +\& histsize maximum number of lines to keep in histfile +\& http_proxy proxy host for http requests +\& inactivity_timeout breaks interactive Makefile.PLs or Build.PLs +\& after this many seconds inactivity. Set to 0 to +\& disable timeouts. +\& index_expire refetch index files after this many days +\& inhibit_startup_message +\& if true, suppress the startup message +\& keep_source_where directory in which to keep the source (if we do) +\& load_module_verbosity +\& report loading of optional modules used by CPAN.pm +\& lynx path to external prg +\& make location of external make program +\& make_arg arguments that should always be passed to \*(Aqmake\*(Aq +\& make_install_make_command +\& the make command for running \*(Aqmake install\*(Aq, for +\& example \*(Aqsudo make\*(Aq +\& make_install_arg same as make_arg for \*(Aqmake install\*(Aq +\& makepl_arg arguments passed to \*(Aqperl Makefile.PL\*(Aq +\& mbuild_arg arguments passed to \*(Aq./Build\*(Aq +\& mbuild_install_arg arguments passed to \*(Aq./Build install\*(Aq +\& mbuild_install_build_command +\& command to use instead of \*(Aq./Build\*(Aq when we are +\& in the install stage, for example \*(Aqsudo ./Build\*(Aq +\& mbuildpl_arg arguments passed to \*(Aqperl Build.PL\*(Aq +\& ncftp path to external prg +\& ncftpget path to external prg +\& no_proxy don\*(Aqt proxy to these hosts/domains (comma separated list) +\& pager location of external program more (or any pager) +\& password your password if you CPAN server wants one +\& patch path to external prg +\& patches_dir local directory containing patch files +\& perl5lib_verbosity verbosity level for PERL5LIB additions +\& plugin_list list of active hooks (see Plugin support above +\& and the CPAN::Plugin module) +\& prefer_external_tar +\& per default all untar operations are done with +\& Archive::Tar; by setting this variable to true +\& the external tar command is used if available +\& prefer_installer legal values are MB and EUMM: if a module comes +\& with both a Makefile.PL and a Build.PL, use the +\& former (EUMM) or the latter (MB); if the module +\& comes with only one of the two, that one will be +\& used no matter the setting +\& prerequisites_policy +\& what to do if you are missing module prerequisites +\& (\*(Aqfollow\*(Aq automatically, \*(Aqask\*(Aq me, or \*(Aqignore\*(Aq) +\& For \*(Aqfollow\*(Aq, also sets PERL_AUTOINSTALL and +\& PERL_EXTUTILS_AUTOINSTALL for "\-\-defaultdeps" if +\& not already set +\& prefs_dir local directory to store per\-distro build options +\& proxy_user username for accessing an authenticating proxy +\& proxy_pass password for accessing an authenticating proxy +\& randomize_urllist add some randomness to the sequence of the urllist +\& recommends_policy whether recommended prerequisites should be included +\& scan_cache controls scanning of cache (\*(Aqatstart\*(Aq, \*(Aqatexit\*(Aq or \*(Aqnever\*(Aq) +\& shell your favorite shell +\& show_unparsable_versions +\& boolean if r command tells which modules are versionless +\& show_upload_date boolean if commands should try to determine upload date +\& show_zero_versions boolean if r command tells for which modules $version==0 +\& suggests_policy whether suggested prerequisites should be included +\& tar location of external program tar +\& tar_verbosity verbosity level for the tar command +\& term_is_latin deprecated: if true Unicode is translated to ISO\-8859\-1 +\& (and nonsense for characters outside latin range) +\& term_ornaments boolean to turn ReadLine ornamenting on/off +\& test_report email test reports (if CPAN::Reporter is installed) +\& trust_test_report_history +\& skip testing when previously tested ok (according to +\& CPAN::Reporter history) +\& unzip location of external program unzip +\& urllist arrayref to nearby CPAN sites (or equivalent locations) +\& urllist_ping_external +\& use external ping command when autoselecting mirrors +\& urllist_ping_verbose +\& increase verbosity when autoselecting mirrors +\& use_prompt_default set PERL_MM_USE_DEFAULT for configure/make/test/install +\& use_sqlite use CPAN::SQLite for metadata storage (fast and lean) +\& username your username if you CPAN server wants one +\& version_timeout stops version parsing after this many seconds. +\& Default is 15 secs. Set to 0 to disable. +\& wait_list arrayref to a wait server to try (See CPAN::WAIT) +\& wget path to external prg +\& yaml_load_code enable YAML code deserialisation via CPAN::DeferredCode +\& yaml_module which module to use to read/write YAML files +.Ve +.PP +You can set and query each of these options interactively in the cpan +shell with the \f(CW\*(C`o conf\*(C'\fR or the \f(CW\*(C`o conf init\*(C'\fR command as specified below. +.ie n .IP """o conf <scalar option>""" 2 +.el .IP "\f(CWo conf <scalar option>\fR" 2 +.IX Item "o conf <scalar option>" +prints the current value of the \fIscalar option\fR +.ie n .IP """o conf <scalar option> <value>""" 2 +.el .IP "\f(CWo conf <scalar option> <value>\fR" 2 +.IX Item "o conf <scalar option> <value>" +Sets the value of the \fIscalar option\fR to \fIvalue\fR +.ie n .IP """o conf <list option>""" 2 +.el .IP "\f(CWo conf <list option>\fR" 2 +.IX Item "o conf <list option>" +prints the current value of the \fIlist option\fR in MakeMaker's +neatvalue format. +.ie n .IP """o conf <list option> [shift|pop]""" 2 +.el .IP "\f(CWo conf <list option> [shift|pop]\fR" 2 +.IX Item "o conf <list option> [shift|pop]" +shifts or pops the array in the \fIlist option\fR variable +.ie n .IP """o conf <list option> [unshift|push|splice] <list>""" 2 +.el .IP "\f(CWo conf <list option> [unshift|push|splice] <list>\fR" 2 +.IX Item "o conf <list option> [unshift|push|splice] <list>" +works like the corresponding perl commands. +.IP "interactive editing: o conf init [MATCH|LIST]" 2 +.IX Item "interactive editing: o conf init [MATCH|LIST]" +Runs an interactive configuration dialog for matching variables. +Without argument runs the dialog over all supported config variables. +To specify a \s-1MATCH\s0 the argument must be enclosed by slashes. +.Sp +Examples: +.Sp +.Vb 2 +\& o conf init ftp_passive ftp_proxy +\& o conf init /color/ +.Ve +.Sp +Note: this method of setting config variables often provides more +explanation about the functioning of a variable than the manpage. +.SS "CPAN::anycwd($path): Note on config variable getcwd" +.IX Subsection "CPAN::anycwd($path): Note on config variable getcwd" +\&\s-1CPAN\s0.pm changes the current working directory often and needs to +determine its own current working directory. By default it uses +Cwd::cwd, but if for some reason this doesn't work on your system, +configure alternatives according to the following table: +.IP "cwd" 4 +.IX Item "cwd" +Calls Cwd::cwd +.IP "getcwd" 4 +.IX Item "getcwd" +Calls Cwd::getcwd +.IP "fastcwd" 4 +.IX Item "fastcwd" +Calls Cwd::fastcwd +.IP "getdcwd" 4 +.IX Item "getdcwd" +Calls Cwd::getdcwd +.IP "backtickcwd" 4 +.IX Item "backtickcwd" +Calls the external command cwd. +.SS "Note on the format of the urllist parameter" +.IX Subsection "Note on the format of the urllist parameter" +urllist parameters are URLs according to \s-1RFC 1738.\s0 We do a little +guessing if your \s-1URL\s0 is not compliant, but if you have problems with +\&\f(CW\*(C`file\*(C'\fR URLs, please try the correct format. Either: +.PP +.Vb 1 +\& file://localhost/whatever/ftp/pub/CPAN/ +.Ve +.PP +or +.PP +.Vb 1 +\& file:///home/ftp/pub/CPAN/ +.Ve +.SS "The urllist parameter has CD-ROM support" +.IX Subsection "The urllist parameter has CD-ROM support" +The \f(CW\*(C`urllist\*(C'\fR parameter of the configuration table contains a list of +URLs used for downloading. If the list contains any +\&\f(CW\*(C`file\*(C'\fR URLs, \s-1CPAN\s0 always tries there first. This +feature is disabled for index files. So the recommendation for the +owner of a CD-ROM with \s-1CPAN\s0 contents is: include your local, possibly +outdated CD-ROM as a \f(CW\*(C`file\*(C'\fR \s-1URL\s0 at the end of urllist, e.g. +.PP +.Vb 1 +\& o conf urllist push file://localhost/CDROM/CPAN +.Ve +.PP +\&\s-1CPAN\s0.pm will then fetch the index files from one of the \s-1CPAN\s0 sites +that come at the beginning of urllist. It will later check for each +module to see whether there is a local copy of the most recent version. +.PP +Another peculiarity of urllist is that the site that we could +successfully fetch the last file from automatically gets a preference +token and is tried as the first site for the next request. So if you +add a new site at runtime it may happen that the previously preferred +site will be tried another time. This means that if you want to disallow +a site for the next transfer, it must be explicitly removed from +urllist. +.SS "Maintaining the urllist parameter" +.IX Subsection "Maintaining the urllist parameter" +If you have \s-1YAML\s0.pm (or some other \s-1YAML\s0 module configured in +\&\f(CW\*(C`yaml_module\*(C'\fR) installed, \s-1CPAN\s0.pm collects a few statistical data +about recent downloads. You can view the statistics with the \f(CW\*(C`hosts\*(C'\fR +command or inspect them directly by looking into the \f(CW\*(C`FTPstats.yml\*(C'\fR +file in your \f(CW\*(C`cpan_home\*(C'\fR directory. +.PP +To get some interesting statistics, it is recommended that +\&\f(CW\*(C`randomize_urllist\*(C'\fR be set; this introduces some amount of +randomness into the \s-1URL\s0 selection. +.ie n .SS "The ""requires"" and ""build_requires"" dependency declarations" +.el .SS "The \f(CWrequires\fP and \f(CWbuild_requires\fP dependency declarations" +.IX Subsection "The requires and build_requires dependency declarations" +Since \s-1CPAN\s0.pm version 1.88_51 modules declared as \f(CW\*(C`build_requires\*(C'\fR by +a distribution are treated differently depending on the config +variable \f(CW\*(C`build_requires_install_policy\*(C'\fR. By setting +\&\f(CW\*(C`build_requires_install_policy\*(C'\fR to \f(CW\*(C`no\*(C'\fR, such a module is not +installed. It is only built and tested, and then kept in the list of +tested but uninstalled modules. As such, it is available during the +build of the dependent module by integrating the path to the +\&\f(CW\*(C`blib/arch\*(C'\fR and \f(CW\*(C`blib/lib\*(C'\fR directories in the environment variable +\&\s-1PERL5LIB.\s0 If \f(CW\*(C`build_requires_install_policy\*(C'\fR is set to \f(CW\*(C`yes\*(C'\fR, then +both modules declared as \f(CW\*(C`requires\*(C'\fR and those declared as +\&\f(CW\*(C`build_requires\*(C'\fR are treated alike. By setting to \f(CW\*(C`ask/yes\*(C'\fR or +\&\f(CW\*(C`ask/no\*(C'\fR, \s-1CPAN\s0.pm asks the user and sets the default accordingly. +.SS "Configuration of the allow_installing_* parameters" +.IX Subsection "Configuration of the allow_installing_* parameters" +The \f(CW\*(C`allow_installing_*\*(C'\fR parameters are evaluated during +the \f(CW\*(C`make\*(C'\fR phase. If set to \f(CW\*(C`yes\*(C'\fR, they allow the testing and the installation of +the current distro and otherwise have no effect. If set to \f(CW\*(C`no\*(C'\fR, they +may abort the build (preventing testing and installing), depending on the contents of the +\&\f(CW\*(C`blib/\*(C'\fR directory. The \f(CW\*(C`blib/\*(C'\fR directory is the directory that holds +all the files that would usually be installed in the \f(CW\*(C`install\*(C'\fR phase. +.PP +\&\f(CW\*(C`allow_installing_outdated_dists\*(C'\fR compares the \f(CW\*(C`blib/\*(C'\fR directory with the \s-1CPAN\s0 index. +If it finds something there that belongs, according to the index, to a different +dist, it aborts the current build. +.PP +\&\f(CW\*(C`allow_installing_module_downgrades\*(C'\fR compares the \f(CW\*(C`blib/\*(C'\fR directory +with already installed modules, actually their version numbers, as +determined by ExtUtils::MakeMaker or equivalent. If a to-be-installed +module would downgrade an already installed module, the current build +is aborted. +.PP +An interesting twist occurs when a distroprefs document demands the +installation of an outdated dist via goto while +\&\f(CW\*(C`allow_installing_outdated_dists\*(C'\fR forbids it. Without additional +provisions, this would let the \f(CW\*(C`allow_installing_outdated_dists\*(C'\fR +win and the distroprefs lose. So the proper arrangement in such a case +is to write a second distroprefs document for the distro that \f(CW\*(C`goto\*(C'\fR +points to and overrule the \f(CW\*(C`cpanconfig\*(C'\fR there. E.g.: +.PP +.Vb 9 +\& \-\-\- +\& match: +\& distribution: "^MAUKE/Keyword\-Simple\-0.04.tar.gz" +\& goto: "MAUKE/Keyword\-Simple\-0.03.tar.gz" +\& \-\-\- +\& match: +\& distribution: "^MAUKE/Keyword\-Simple\-0.03.tar.gz" +\& cpanconfig: +\& allow_installing_outdated_dists: yes +.Ve +.SS "Configuration for individual distributions (\fIDistroprefs\fP)" +.IX Subsection "Configuration for individual distributions (Distroprefs)" +(\fBNote:\fR This feature has been introduced in \s-1CPAN\s0.pm 1.8854) +.PP +Distributions on \s-1CPAN\s0 usually behave according to what we call the +\&\s-1CPAN\s0 mantra. Or since the advent of Module::Build we should talk about +two mantras: +.PP +.Vb 4 +\& perl Makefile.PL perl Build.PL +\& make ./Build +\& make test ./Build test +\& make install ./Build install +.Ve +.PP +But some modules cannot be built with this mantra. They try to get +some extra data from the user via the environment, extra arguments, or +interactively\*(--thus disturbing the installation of large bundles like +Phalanx100 or modules with many dependencies like Plagger. +.PP +The distroprefs system of \f(CW\*(C`CPAN.pm\*(C'\fR addresses this problem by +allowing the user to specify extra informations and recipes in \s-1YAML\s0 +files to either +.IP "\(bu" 4 +pass additional arguments to one of the four commands, +.IP "\(bu" 4 +set environment variables +.IP "\(bu" 4 +instantiate an Expect object that reads from the console, waits for +some regular expressions and enters some answers +.IP "\(bu" 4 +temporarily override assorted \f(CW\*(C`CPAN.pm\*(C'\fR configuration variables +.IP "\(bu" 4 +specify dependencies the original maintainer forgot +.IP "\(bu" 4 +disable the installation of an object altogether +.PP +See the \s-1YAML\s0 and Data::Dumper files that come with the \f(CW\*(C`CPAN.pm\*(C'\fR +distribution in the \f(CW\*(C`distroprefs/\*(C'\fR directory for examples. +.SS "Filenames" +.IX Subsection "Filenames" +The \s-1YAML\s0 files themselves must have the \f(CW\*(C`.yml\*(C'\fR extension; all other +files are ignored (for two exceptions see \fIFallback Data::Dumper and +Storable\fR below). The containing directory can be specified in +\&\f(CW\*(C`CPAN.pm\*(C'\fR in the \f(CW\*(C`prefs_dir\*(C'\fR config variable. Try \f(CW\*(C`o conf init +prefs_dir\*(C'\fR in the \s-1CPAN\s0 shell to set and activate the distroprefs +system. +.PP +Every \s-1YAML\s0 file may contain arbitrary documents according to the \s-1YAML\s0 +specification, and every document is treated as an entity that +can specify the treatment of a single distribution. +.PP +Filenames can be picked arbitrarily; \f(CW\*(C`CPAN.pm\*(C'\fR always reads +all files (in alphabetical order) and takes the key \f(CW\*(C`match\*(C'\fR (see +below in \fILanguage Specs\fR) as a hashref containing match criteria +that determine if the current distribution matches the \s-1YAML\s0 document +or not. +.SS "Fallback Data::Dumper and Storable" +.IX Subsection "Fallback Data::Dumper and Storable" +If neither your configured \f(CW\*(C`yaml_module\*(C'\fR nor \s-1YAML\s0.pm is installed, +\&\s-1CPAN\s0.pm falls back to using Data::Dumper and Storable and looks for +files with the extensions \f(CW\*(C`.dd\*(C'\fR or \f(CW\*(C`.st\*(C'\fR in the \f(CW\*(C`prefs_dir\*(C'\fR +directory. These files are expected to contain one or more hashrefs. +For Data::Dumper generated files, this is expected to be done with by +defining \f(CW$VAR1\fR, \f(CW$VAR2\fR, etc. The \s-1YAML\s0 shell would produce these +with the command +.PP +.Vb 1 +\& ysh < somefile.yml > somefile.dd +.Ve +.PP +For Storable files the rule is that they must be constructed such that +\&\f(CW\*(C`Storable::retrieve(file)\*(C'\fR returns an array reference and the array +elements represent one distropref object each. The conversion from +\&\s-1YAML\s0 would look like so: +.PP +.Vb 3 +\& perl \-MYAML=LoadFile \-MStorable=nstore \-e \*(Aq +\& @y=LoadFile(shift); +\& nstore(\e@y, shift)\*(Aq somefile.yml somefile.st +.Ve +.PP +In bootstrapping situations it is usually sufficient to translate only +a few \s-1YAML\s0 files to Data::Dumper for crucial modules like +\&\f(CW\*(C`YAML::Syck\*(C'\fR, \f(CW\*(C`YAML.pm\*(C'\fR and \f(CW\*(C`Expect.pm\*(C'\fR. If you prefer Storable +over Data::Dumper, remember to pull out a Storable version that writes +an older format than all the other Storable versions that will need to +read them. +.SS "Blueprint" +.IX Subsection "Blueprint" +The following example contains all supported keywords and structures +with the exception of \f(CW\*(C`eexpect\*(C'\fR which can be used instead of +\&\f(CW\*(C`expect\*(C'\fR. +.PP +.Vb 10 +\& \-\-\- +\& comment: "Demo" +\& match: +\& module: "Dancing::Queen" +\& distribution: "^CHACHACHA/Dancing\-" +\& not_distribution: "\e.zip$" +\& perl: "/usr/local/cariba\-perl/bin/perl" +\& perlconfig: +\& archname: "freebsd" +\& not_cc: "gcc" +\& env: +\& DANCING_FLOOR: "Shubiduh" +\& disabled: 1 +\& cpanconfig: +\& make: gmake +\& pl: +\& args: +\& \- "\-\-somearg=specialcase" +\& +\& env: {} +\& +\& expect: +\& \- "Which is your favorite fruit" +\& \- "apple\en" +\& +\& make: +\& args: +\& \- all +\& \- extra\-all +\& +\& env: {} +\& +\& expect: [] +\& +\& commandline: "echo SKIPPING make" +\& +\& test: +\& args: [] +\& +\& env: {} +\& +\& expect: [] +\& +\& install: +\& args: [] +\& +\& env: +\& WANT_TO_INSTALL: YES +\& +\& expect: +\& \- "Do you really want to install" +\& \- "y\en" +\& +\& patches: +\& \- "ABCDE/Fedcba\-3.14\-ABCDE\-01.patch" +\& +\& depends: +\& configure_requires: +\& LWP: 5.8 +\& build_requires: +\& Test::Exception: 0.25 +\& requires: +\& Spiffy: 0.30 +.Ve +.SS "Language Specs" +.IX Subsection "Language Specs" +Every \s-1YAML\s0 document represents a single hash reference. The valid keys +in this hash are as follows: +.IP "comment [scalar]" 4 +.IX Item "comment [scalar]" +A comment +.IP "cpanconfig [hash]" 4 +.IX Item "cpanconfig [hash]" +Temporarily override assorted \f(CW\*(C`CPAN.pm\*(C'\fR configuration variables. +.Sp +Supported are: \f(CW\*(C`build_requires_install_policy\*(C'\fR, \f(CW\*(C`check_sigs\*(C'\fR, +\&\f(CW\*(C`make\*(C'\fR, \f(CW\*(C`make_install_make_command\*(C'\fR, \f(CW\*(C`prefer_installer\*(C'\fR, +\&\f(CW\*(C`test_report\*(C'\fR. Please report as a bug when you need another one +supported. +.IP "depends [hash] *** \s-1EXPERIMENTAL FEATURE\s0 ***" 4 +.IX Item "depends [hash] *** EXPERIMENTAL FEATURE ***" +All three types, namely \f(CW\*(C`configure_requires\*(C'\fR, \f(CW\*(C`build_requires\*(C'\fR, and +\&\f(CW\*(C`requires\*(C'\fR are supported in the way specified in the \s-1META\s0.yml +specification. The current implementation \fImerges\fR the specified +dependencies with those declared by the package maintainer. In a +future implementation this may be changed to override the original +declaration. +.IP "disabled [boolean]" 4 +.IX Item "disabled [boolean]" +Specifies that this distribution shall not be processed at all. +.IP "features [array] *** \s-1EXPERIMENTAL FEATURE\s0 ***" 4 +.IX Item "features [array] *** EXPERIMENTAL FEATURE ***" +Experimental implementation to deal with optional_features from +\&\s-1META\s0.yml. Still needs coordination with installer software and +currently works only for \s-1META\s0.yml declaring \f(CW\*(C`dynamic_config=0\*(C'\fR. Use +with caution. +.IP "goto [string]" 4 +.IX Item "goto [string]" +The canonical name of a delegate distribution to install +instead. Useful when a new version, although it tests \s-1OK\s0 itself, +breaks something else or a developer release or a fork is already +uploaded that is better than the last released version. +.IP "install [hash]" 4 +.IX Item "install [hash]" +Processing instructions for the \f(CW\*(C`make install\*(C'\fR or \f(CW\*(C`./Build install\*(C'\fR +phase of the \s-1CPAN\s0 mantra. See below under \fIProcessing Instructions\fR. +.IP "make [hash]" 4 +.IX Item "make [hash]" +Processing instructions for the \f(CW\*(C`make\*(C'\fR or \f(CW\*(C`./Build\*(C'\fR phase of the +\&\s-1CPAN\s0 mantra. See below under \fIProcessing Instructions\fR. +.IP "match [hash]" 4 +.IX Item "match [hash]" +A hashref with one or more of the keys \f(CW\*(C`distribution\*(C'\fR, \f(CW\*(C`module\*(C'\fR, +\&\f(CW\*(C`perl\*(C'\fR, \f(CW\*(C`perlconfig\*(C'\fR, and \f(CW\*(C`env\*(C'\fR that specify whether a document is +targeted at a specific \s-1CPAN\s0 distribution or installation. +Keys prefixed with \f(CW\*(C`not_\*(C'\fR negates the corresponding match. +.Sp +The corresponding values are interpreted as regular expressions. The +\&\f(CW\*(C`distribution\*(C'\fR related one will be matched against the canonical +distribution name, e.g. \*(L"AUTHOR/Foo\-Bar\-3.14.tar.gz\*(R". +.Sp +The \f(CW\*(C`module\*(C'\fR related one will be matched against \fIall\fR modules +contained in the distribution until one module matches. +.Sp +The \f(CW\*(C`perl\*(C'\fR related one will be matched against \f(CW$^X\fR (but with the +absolute path). +.Sp +The value associated with \f(CW\*(C`perlconfig\*(C'\fR is itself a hashref that is +matched against corresponding values in the \f(CW%Config::Config\fR hash +living in the \f(CW\*(C`Config.pm\*(C'\fR module. +Keys prefixed with \f(CW\*(C`not_\*(C'\fR negates the corresponding match. +.Sp +The value associated with \f(CW\*(C`env\*(C'\fR is itself a hashref that is +matched against corresponding values in the \f(CW%ENV\fR hash. +Keys prefixed with \f(CW\*(C`not_\*(C'\fR negates the corresponding match. +.Sp +If more than one restriction of \f(CW\*(C`module\*(C'\fR, \f(CW\*(C`distribution\*(C'\fR, etc. is +specified, the results of the separately computed match values must +all match. If so, the hashref represented by the +\&\s-1YAML\s0 document is returned as the preference structure for the current +distribution. +.IP "patches [array]" 4 +.IX Item "patches [array]" +An array of patches on \s-1CPAN\s0 or on the local disk to be applied in +order via an external patch program. If the value for the \f(CW\*(C`\-p\*(C'\fR +parameter is \f(CW0\fR or \f(CW1\fR is determined by reading the patch +beforehand. The path to each patch is either an absolute path on the +local filesystem or relative to a patch directory specified in the +\&\f(CW\*(C`patches_dir\*(C'\fR configuration variable or in the format of a canonical +distro name. For examples please consult the distroprefs/ directory in +the \s-1CPAN\s0.pm distribution (these examples are not installed by +default). +.Sp +Note: if the \f(CW\*(C`applypatch\*(C'\fR program is installed and \f(CW\*(C`CPAN::Config\*(C'\fR +knows about it \fBand\fR a patch is written by the \f(CW\*(C`makepatch\*(C'\fR program, +then \f(CW\*(C`CPAN.pm\*(C'\fR lets \f(CW\*(C`applypatch\*(C'\fR apply the patch. Both \f(CW\*(C`makepatch\*(C'\fR +and \f(CW\*(C`applypatch\*(C'\fR are available from \s-1CPAN\s0 in the \f(CW\*(C`JV/makepatch\-*\*(C'\fR +distribution. +.IP "pl [hash]" 4 +.IX Item "pl [hash]" +Processing instructions for the \f(CW\*(C`perl Makefile.PL\*(C'\fR or \f(CW\*(C`perl +Build.PL\*(C'\fR phase of the \s-1CPAN\s0 mantra. See below under \fIProcessing +Instructions\fR. +.IP "test [hash]" 4 +.IX Item "test [hash]" +Processing instructions for the \f(CW\*(C`make test\*(C'\fR or \f(CW\*(C`./Build test\*(C'\fR phase +of the \s-1CPAN\s0 mantra. See below under \fIProcessing Instructions\fR. +.SS "Processing Instructions" +.IX Subsection "Processing Instructions" +.IP "args [array]" 4 +.IX Item "args [array]" +Arguments to be added to the command line +.IP "commandline" 4 +.IX Item "commandline" +A full commandline to run via \f(CW\*(C`system()\*(C'\fR. +During execution, the environment variable \s-1PERL\s0 is set +to $^X (but with an absolute path). If \f(CW\*(C`commandline\*(C'\fR is specified, +\&\f(CW\*(C`args\*(C'\fR is not used. +.IP "eexpect [hash]" 4 +.IX Item "eexpect [hash]" +Extended \f(CW\*(C`expect\*(C'\fR. This is a hash reference with four allowed keys, +\&\f(CW\*(C`mode\*(C'\fR, \f(CW\*(C`timeout\*(C'\fR, \f(CW\*(C`reuse\*(C'\fR, and \f(CW\*(C`talk\*(C'\fR. +.Sp +You must install the \f(CW\*(C`Expect\*(C'\fR module to use \f(CW\*(C`eexpect\*(C'\fR. \s-1CPAN\s0.pm +does not install it for you. +.Sp +\&\f(CW\*(C`mode\*(C'\fR may have the values \f(CW\*(C`deterministic\*(C'\fR for the case where all +questions come in the order written down and \f(CW\*(C`anyorder\*(C'\fR for the case +where the questions may come in any order. The default mode is +\&\f(CW\*(C`deterministic\*(C'\fR. +.Sp +\&\f(CW\*(C`timeout\*(C'\fR denotes a timeout in seconds. Floating-point timeouts are +\&\s-1OK.\s0 With \f(CW\*(C`mode=deterministic\*(C'\fR, the timeout denotes the +timeout per question; with \f(CW\*(C`mode=anyorder\*(C'\fR it denotes the +timeout per byte received from the stream or questions. +.Sp +\&\f(CW\*(C`talk\*(C'\fR is a reference to an array that contains alternating questions +and answers. Questions are regular expressions and answers are literal +strings. The Expect module watches the stream from the +execution of the external program (\f(CW\*(C`perl Makefile.PL\*(C'\fR, \f(CW\*(C`perl +Build.PL\*(C'\fR, \f(CW\*(C`make\*(C'\fR, etc.). +.Sp +For \f(CW\*(C`mode=deterministic\*(C'\fR, the \s-1CPAN\s0.pm injects the +corresponding answer as soon as the stream matches the regular expression. +.Sp +For \f(CW\*(C`mode=anyorder\*(C'\fR \s-1CPAN\s0.pm answers a question as soon +as the timeout is reached for the next byte in the input stream. In +this mode you can use the \f(CW\*(C`reuse\*(C'\fR parameter to decide what will +happen with a question-answer pair after it has been used. In the +default case (reuse=0) it is removed from the array, avoiding being +used again accidentally. If you want to answer the +question \f(CW\*(C`Do you really want to do that\*(C'\fR several times, then it must +be included in the array at least as often as you want this answer to +be given. Setting the parameter \f(CW\*(C`reuse\*(C'\fR to 1 makes this repetition +unnecessary. +.IP "env [hash]" 4 +.IX Item "env [hash]" +Environment variables to be set during the command +.IP "expect [array]" 4 +.IX Item "expect [array]" +You must install the \f(CW\*(C`Expect\*(C'\fR module to use \f(CW\*(C`expect\*(C'\fR. \s-1CPAN\s0.pm +does not install it for you. +.Sp +\&\f(CW\*(C`expect: <array>\*(C'\fR is a short notation for this \f(CW\*(C`eexpect\*(C'\fR: +.Sp +.Vb 4 +\& eexpect: +\& mode: deterministic +\& timeout: 15 +\& talk: <array> +.Ve +.ie n .SS "Schema verification with ""Kwalify""" +.el .SS "Schema verification with \f(CWKwalify\fP" +.IX Subsection "Schema verification with Kwalify" +If you have the \f(CW\*(C`Kwalify\*(C'\fR module installed (which is part of the +Bundle::CPANxxl), then all your distroprefs files are checked for +syntactic correctness. +.SS "Example Distroprefs Files" +.IX Subsection "Example Distroprefs Files" +\&\f(CW\*(C`CPAN.pm\*(C'\fR comes with a collection of example \s-1YAML\s0 files. Note that these +are really just examples and should not be used without care because +they cannot fit everybody's purpose. After all, the authors of the +packages that ask questions had a need to ask, so you should watch +their questions and adjust the examples to your environment and your +needs. You have been warned:\-) +.SH "PROGRAMMER'S INTERFACE" +.IX Header "PROGRAMMER'S INTERFACE" +If you do not enter the shell, shell commands are +available both as methods (\f(CW\*(C`CPAN::Shell\->install(...)\*(C'\fR) and as +functions in the calling package (\f(CW\*(C`install(...)\*(C'\fR). Before calling low-level +commands, it makes sense to initialize components of \s-1CPAN\s0 you need, e.g.: +.PP +.Vb 3 +\& CPAN::HandleConfig\->load; +\& CPAN::Shell::setup_output; +\& CPAN::Index\->reload; +.Ve +.PP +High-level commands do such initializations automatically. +.PP +There's currently only one class that has a stable interface \- +CPAN::Shell. All commands that are available in the \s-1CPAN\s0 shell are +methods of the class CPAN::Shell. The arguments on the commandline are +passed as arguments to the method. +.PP +So if you take for example the shell command +.PP +.Vb 1 +\& notest install A B C +.Ve +.PP +the actually executed command is +.PP +.Vb 1 +\& CPAN::Shell\->notest("install","A","B","C"); +.Ve +.PP +Each of the commands that produce listings of modules (\f(CW\*(C`r\*(C'\fR, +\&\f(CW\*(C`autobundle\*(C'\fR, \f(CW\*(C`u\*(C'\fR) also return a list of the IDs of all modules +within the list. +.IP "expand($type,@things)" 2 +.IX Item "expand($type,@things)" +The IDs of all objects available within a program are strings that can +be expanded to the corresponding real objects with the +\&\f(CW\*(C`CPAN::Shell\->expand("Module",@things)\*(C'\fR method. Expand returns a +list of CPAN::Module objects according to the \f(CW@things\fR arguments +given. In scalar context, it returns only the first element of the +list. +.IP "expandany(@things)" 2 +.IX Item "expandany(@things)" +Like expand, but returns objects of the appropriate type, i.e. +CPAN::Bundle objects for bundles, CPAN::Module objects for modules, and +CPAN::Distribution objects for distributions. Note: it does not expand +to CPAN::Author objects. +.IP "Programming Examples" 2 +.IX Item "Programming Examples" +This enables the programmer to do operations that combine +functionalities that are available in the shell. +.Sp +.Vb 2 +\& # install everything that is outdated on my disk: +\& perl \-MCPAN \-e \*(AqCPAN::Shell\->install(CPAN::Shell\->r)\*(Aq +\& +\& # install my favorite programs if necessary: +\& for $mod (qw(Net::FTP Digest::SHA Data::Dumper)) { +\& CPAN::Shell\->install($mod); +\& } +\& +\& # list all modules on my disk that have no VERSION number +\& for $mod (CPAN::Shell\->expand("Module","/./")) { +\& next unless $mod\->inst_file; +\& # MakeMaker convention for undefined $VERSION: +\& next unless $mod\->inst_version eq "undef"; +\& print "No VERSION in ", $mod\->id, "\en"; +\& } +\& +\& # find out which distribution on CPAN contains a module: +\& print CPAN::Shell\->expand("Module","Apache::Constants")\->cpan_file +.Ve +.Sp +Or if you want to schedule a \fIcron\fR job to watch \s-1CPAN,\s0 you could list +all modules that need updating. First a quick and dirty way: +.Sp +.Vb 1 +\& perl \-e \*(Aquse CPAN; CPAN::Shell\->r;\*(Aq +.Ve +.Sp +If you don't want any output should all modules be +up to date, parse the output of above command for the regular +expression \f(CW\*(C`/modules are up to date/\*(C'\fR and decide to mail the output +only if it doesn't match. +.Sp +If you prefer to do it more in a programmerish style in one single +process, something like this may better suit you: +.Sp +.Vb 7 +\& # list all modules on my disk that have newer versions on CPAN +\& for $mod (CPAN::Shell\->expand("Module","/./")) { +\& next unless $mod\->inst_file; +\& next if $mod\->uptodate; +\& printf "Module %s is installed as %s, could be updated to %s from CPAN\en", +\& $mod\->id, $mod\->inst_version, $mod\->cpan_version; +\& } +.Ve +.Sp +If that gives too much output every day, you may want to +watch only for three modules. You can write +.Sp +.Vb 1 +\& for $mod (CPAN::Shell\->expand("Module","/Apache|LWP|CGI/")) { +.Ve +.Sp +as the first line instead. Or you can combine some of the above +tricks: +.Sp +.Vb 5 +\& # watch only for a new mod_perl module +\& $mod = CPAN::Shell\->expand("Module","mod_perl"); +\& exit if $mod\->uptodate; +\& # new mod_perl arrived, let me know all update recommendations +\& CPAN::Shell\->r; +.Ve +.SS "Methods in the other Classes" +.IX Subsection "Methods in the other Classes" +.IP "\fBCPAN::Author::as_glimpse()\fR" 4 +.IX Item "CPAN::Author::as_glimpse()" +Returns a one-line description of the author +.IP "\fBCPAN::Author::as_string()\fR" 4 +.IX Item "CPAN::Author::as_string()" +Returns a multi-line description of the author +.IP "\fBCPAN::Author::email()\fR" 4 +.IX Item "CPAN::Author::email()" +Returns the author's email address +.IP "\fBCPAN::Author::fullname()\fR" 4 +.IX Item "CPAN::Author::fullname()" +Returns the author's name +.IP "\fBCPAN::Author::name()\fR" 4 +.IX Item "CPAN::Author::name()" +An alias for fullname +.IP "\fBCPAN::Bundle::as_glimpse()\fR" 4 +.IX Item "CPAN::Bundle::as_glimpse()" +Returns a one-line description of the bundle +.IP "\fBCPAN::Bundle::as_string()\fR" 4 +.IX Item "CPAN::Bundle::as_string()" +Returns a multi-line description of the bundle +.IP "\fBCPAN::Bundle::clean()\fR" 4 +.IX Item "CPAN::Bundle::clean()" +Recursively runs the \f(CW\*(C`clean\*(C'\fR method on all items contained in the bundle. +.IP "\fBCPAN::Bundle::contains()\fR" 4 +.IX Item "CPAN::Bundle::contains()" +Returns a list of objects' IDs contained in a bundle. The associated +objects may be bundles, modules or distributions. +.IP "CPAN::Bundle::force($method,@args)" 4 +.IX Item "CPAN::Bundle::force($method,@args)" +Forces \s-1CPAN\s0 to perform a task that it normally would have refused to +do. Force takes as arguments a method name to be called and any number +of additional arguments that should be passed to the called method. +The internals of the object get the needed changes so that \s-1CPAN\s0.pm +does not refuse to take the action. The \f(CW\*(C`force\*(C'\fR is passed recursively +to all contained objects. See also the section above on the \f(CW\*(C`force\*(C'\fR +and the \f(CW\*(C`fforce\*(C'\fR pragma. +.IP "\fBCPAN::Bundle::get()\fR" 4 +.IX Item "CPAN::Bundle::get()" +Recursively runs the \f(CW\*(C`get\*(C'\fR method on all items contained in the bundle +.IP "\fBCPAN::Bundle::inst_file()\fR" 4 +.IX Item "CPAN::Bundle::inst_file()" +Returns the highest installed version of the bundle in either \f(CW@INC\fR or +\&\f(CW\*(C`$CPAN::Config\->{cpan_home}\*(C'\fR. Note that this is different from +CPAN::Module::inst_file. +.IP "\fBCPAN::Bundle::inst_version()\fR" 4 +.IX Item "CPAN::Bundle::inst_version()" +Like CPAN::Bundle::inst_file, but returns the \f(CW$VERSION\fR +.IP "\fBCPAN::Bundle::uptodate()\fR" 4 +.IX Item "CPAN::Bundle::uptodate()" +Returns 1 if the bundle itself and all its members are up-to-date. +.IP "\fBCPAN::Bundle::install()\fR" 4 +.IX Item "CPAN::Bundle::install()" +Recursively runs the \f(CW\*(C`install\*(C'\fR method on all items contained in the bundle +.IP "\fBCPAN::Bundle::make()\fR" 4 +.IX Item "CPAN::Bundle::make()" +Recursively runs the \f(CW\*(C`make\*(C'\fR method on all items contained in the bundle +.IP "\fBCPAN::Bundle::readme()\fR" 4 +.IX Item "CPAN::Bundle::readme()" +Recursively runs the \f(CW\*(C`readme\*(C'\fR method on all items contained in the bundle +.IP "\fBCPAN::Bundle::test()\fR" 4 +.IX Item "CPAN::Bundle::test()" +Recursively runs the \f(CW\*(C`test\*(C'\fR method on all items contained in the bundle +.IP "\fBCPAN::Distribution::as_glimpse()\fR" 4 +.IX Item "CPAN::Distribution::as_glimpse()" +Returns a one-line description of the distribution +.IP "\fBCPAN::Distribution::as_string()\fR" 4 +.IX Item "CPAN::Distribution::as_string()" +Returns a multi-line description of the distribution +.IP "CPAN::Distribution::author" 4 +.IX Item "CPAN::Distribution::author" +Returns the CPAN::Author object of the maintainer who uploaded this +distribution +.IP "\fBCPAN::Distribution::pretty_id()\fR" 4 +.IX Item "CPAN::Distribution::pretty_id()" +Returns a string of the form \*(L"\s-1AUTHORID/TARBALL\*(R",\s0 where \s-1AUTHORID\s0 is the +author's \s-1PAUSE ID\s0 and \s-1TARBALL\s0 is the distribution filename. +.IP "\fBCPAN::Distribution::base_id()\fR" 4 +.IX Item "CPAN::Distribution::base_id()" +Returns the distribution filename without any archive suffix. E.g +\&\*(L"Foo\-Bar\-0.01\*(R" +.IP "\fBCPAN::Distribution::clean()\fR" 4 +.IX Item "CPAN::Distribution::clean()" +Changes to the directory where the distribution has been unpacked and +runs \f(CW\*(C`make clean\*(C'\fR there. +.IP "\fBCPAN::Distribution::containsmods()\fR" 4 +.IX Item "CPAN::Distribution::containsmods()" +Returns a list of IDs of modules contained in a distribution file. +Works only for distributions listed in the 02packages.details.txt.gz +file. This typically means that just most recent version of a +distribution is covered. +.IP "\fBCPAN::Distribution::cvs_import()\fR" 4 +.IX Item "CPAN::Distribution::cvs_import()" +Changes to the directory where the distribution has been unpacked and +runs something like +.Sp +.Vb 1 +\& cvs \-d $cvs_root import \-m $cvs_log $cvs_dir $userid v$version +.Ve +.Sp +there. +.IP "\fBCPAN::Distribution::dir()\fR" 4 +.IX Item "CPAN::Distribution::dir()" +Returns the directory into which this distribution has been unpacked. +.IP "CPAN::Distribution::force($method,@args)" 4 +.IX Item "CPAN::Distribution::force($method,@args)" +Forces \s-1CPAN\s0 to perform a task that it normally would have refused to +do. Force takes as arguments a method name to be called and any number +of additional arguments that should be passed to the called method. +The internals of the object get the needed changes so that \s-1CPAN\s0.pm +does not refuse to take the action. See also the section above on the +\&\f(CW\*(C`force\*(C'\fR and the \f(CW\*(C`fforce\*(C'\fR pragma. +.IP "\fBCPAN::Distribution::get()\fR" 4 +.IX Item "CPAN::Distribution::get()" +Downloads the distribution from \s-1CPAN\s0 and unpacks it. Does nothing if +the distribution has already been downloaded and unpacked within the +current session. +.IP "\fBCPAN::Distribution::install()\fR" 4 +.IX Item "CPAN::Distribution::install()" +Changes to the directory where the distribution has been unpacked and +runs the external command \f(CW\*(C`make install\*(C'\fR there. If \f(CW\*(C`make\*(C'\fR has not +yet been run, it will be run first. A \f(CW\*(C`make test\*(C'\fR is issued in +any case and if this fails, the install is cancelled. The +cancellation can be avoided by letting \f(CW\*(C`force\*(C'\fR run the \f(CW\*(C`install\*(C'\fR for +you. +.Sp +This install method only has the power to install the distribution if +there are no dependencies in the way. To install an object along with all +its dependencies, use CPAN::Shell\->install. +.Sp +Note that \fBinstall()\fR gives no meaningful return value. See \fBuptodate()\fR. +.IP "\fBCPAN::Distribution::isa_perl()\fR" 4 +.IX Item "CPAN::Distribution::isa_perl()" +Returns 1 if this distribution file seems to be a perl distribution. +Normally this is derived from the file name only, but the index from +\&\s-1CPAN\s0 can contain a hint to achieve a return value of true for other +filenames too. +.IP "\fBCPAN::Distribution::look()\fR" 4 +.IX Item "CPAN::Distribution::look()" +Changes to the directory where the distribution has been unpacked and +opens a subshell there. Exiting the subshell returns. +.IP "\fBCPAN::Distribution::make()\fR" 4 +.IX Item "CPAN::Distribution::make()" +First runs the \f(CW\*(C`get\*(C'\fR method to make sure the distribution is +downloaded and unpacked. Changes to the directory where the +distribution has been unpacked and runs the external commands \f(CW\*(C`perl +Makefile.PL\*(C'\fR or \f(CW\*(C`perl Build.PL\*(C'\fR and \f(CW\*(C`make\*(C'\fR there. +.IP "\fBCPAN::Distribution::perldoc()\fR" 4 +.IX Item "CPAN::Distribution::perldoc()" +Downloads the pod documentation of the file associated with a +distribution (in \s-1HTML\s0 format) and runs it through the external +command \fIlynx\fR specified in \f(CW\*(C`$CPAN::Config\->{lynx}\*(C'\fR. If \fIlynx\fR +isn't available, it converts it to plain text with the external +command \fIhtml2text\fR and runs it through the pager specified +in \f(CW\*(C`$CPAN::Config\->{pager}\*(C'\fR. +.IP "\fBCPAN::Distribution::prefs()\fR" 4 +.IX Item "CPAN::Distribution::prefs()" +Returns the hash reference from the first matching \s-1YAML\s0 file that the +user has deposited in the \f(CW\*(C`prefs_dir/\*(C'\fR directory. The first +succeeding match wins. The files in the \f(CW\*(C`prefs_dir/\*(C'\fR are processed +alphabetically, and the canonical distro name (e.g. +AUTHOR/Foo\-Bar\-3.14.tar.gz) is matched against the regular expressions +stored in the \f(CW$root\fR\->{match}{distribution} attribute value. +Additionally all module names contained in a distribution are matched +against the regular expressions in the \f(CW$root\fR\->{match}{module} attribute +value. The two match values are ANDed together. Each of the two +attributes are optional. +.IP "\fBCPAN::Distribution::prereq_pm()\fR" 4 +.IX Item "CPAN::Distribution::prereq_pm()" +Returns the hash reference that has been announced by a distribution +as the \f(CW\*(C`requires\*(C'\fR and \f(CW\*(C`build_requires\*(C'\fR elements. These can be +declared either by the \f(CW\*(C`META.yml\*(C'\fR (if authoritative) or can be +deposited after the run of \f(CW\*(C`Build.PL\*(C'\fR in the file \f(CW\*(C`./_build/prereqs\*(C'\fR +or after the run of \f(CW\*(C`Makfile.PL\*(C'\fR written as the \f(CW\*(C`PREREQ_PM\*(C'\fR hash in +a comment in the produced \f(CW\*(C`Makefile\*(C'\fR. \fINote\fR: this method only works +after an attempt has been made to \f(CW\*(C`make\*(C'\fR the distribution. Returns +undef otherwise. +.IP "\fBCPAN::Distribution::readme()\fR" 4 +.IX Item "CPAN::Distribution::readme()" +Downloads the \s-1README\s0 file associated with a distribution and runs it +through the pager specified in \f(CW\*(C`$CPAN::Config\->{pager}\*(C'\fR. +.IP "\fBCPAN::Distribution::reports()\fR" 4 +.IX Item "CPAN::Distribution::reports()" +Downloads report data for this distribution from www.cpantesters.org +and displays a subset of them. +.IP "\fBCPAN::Distribution::read_yaml()\fR" 4 +.IX Item "CPAN::Distribution::read_yaml()" +Returns the content of the \s-1META\s0.yml of this distro as a hashref. Note: +works only after an attempt has been made to \f(CW\*(C`make\*(C'\fR the distribution. +Returns undef otherwise. Also returns undef if the content of \s-1META\s0.yml +is not authoritative. (The rules about what exactly makes the content +authoritative are still in flux.) +.IP "\fBCPAN::Distribution::test()\fR" 4 +.IX Item "CPAN::Distribution::test()" +Changes to the directory where the distribution has been unpacked and +runs \f(CW\*(C`make test\*(C'\fR there. +.IP "\fBCPAN::Distribution::uptodate()\fR" 4 +.IX Item "CPAN::Distribution::uptodate()" +Returns 1 if all the modules contained in the distribution are +up-to-date. Relies on containsmods. +.IP "\fBCPAN::Index::force_reload()\fR" 4 +.IX Item "CPAN::Index::force_reload()" +Forces a reload of all indices. +.IP "\fBCPAN::Index::reload()\fR" 4 +.IX Item "CPAN::Index::reload()" +Reloads all indices if they have not been read for more than +\&\f(CW\*(C`$CPAN::Config\->{index_expire}\*(C'\fR days. +.IP "\fBCPAN::InfoObj::dump()\fR" 4 +.IX Item "CPAN::InfoObj::dump()" +CPAN::Author, CPAN::Bundle, CPAN::Module, and CPAN::Distribution +inherit this method. It prints the data structure associated with an +object. Useful for debugging. Note: the data structure is considered +internal and thus subject to change without notice. +.IP "\fBCPAN::Module::as_glimpse()\fR" 4 +.IX Item "CPAN::Module::as_glimpse()" +Returns a one-line description of the module in four columns: The +first column contains the word \f(CW\*(C`Module\*(C'\fR, the second column consists +of one character: an equals sign if this module is already installed +and up-to-date, a less-than sign if this module is installed but can be +upgraded, and a space if the module is not installed. The third column +is the name of the module and the fourth column gives maintainer or +distribution information. +.IP "\fBCPAN::Module::as_string()\fR" 4 +.IX Item "CPAN::Module::as_string()" +Returns a multi-line description of the module +.IP "\fBCPAN::Module::clean()\fR" 4 +.IX Item "CPAN::Module::clean()" +Runs a clean on the distribution associated with this module. +.IP "\fBCPAN::Module::cpan_file()\fR" 4 +.IX Item "CPAN::Module::cpan_file()" +Returns the filename on \s-1CPAN\s0 that is associated with the module. +.IP "\fBCPAN::Module::cpan_version()\fR" 4 +.IX Item "CPAN::Module::cpan_version()" +Returns the latest version of this module available on \s-1CPAN.\s0 +.IP "\fBCPAN::Module::cvs_import()\fR" 4 +.IX Item "CPAN::Module::cvs_import()" +Runs a cvs_import on the distribution associated with this module. +.IP "\fBCPAN::Module::description()\fR" 4 +.IX Item "CPAN::Module::description()" +Returns a 44 character description of this module. Only available for +modules listed in The Module List (CPAN/modules/00modlist.long.html +or 00modlist.long.txt.gz) +.IP "\fBCPAN::Module::distribution()\fR" 4 +.IX Item "CPAN::Module::distribution()" +Returns the CPAN::Distribution object that contains the current +version of this module. +.IP "\fBCPAN::Module::dslip_status()\fR" 4 +.IX Item "CPAN::Module::dslip_status()" +Returns a hash reference. The keys of the hash are the letters \f(CW\*(C`D\*(C'\fR, +\&\f(CW\*(C`S\*(C'\fR, \f(CW\*(C`L\*(C'\fR, \f(CW\*(C`I\*(C'\fR, and <P>, for development status, support level, +language, interface and public licence respectively. The data for the +\&\s-1DSLIP\s0 status are collected by pause.perl.org when authors register +their namespaces. The values of the 5 hash elements are one-character +words whose meaning is described in the table below. There are also 5 +hash elements \f(CW\*(C`DV\*(C'\fR, \f(CW\*(C`SV\*(C'\fR, \f(CW\*(C`LV\*(C'\fR, \f(CW\*(C`IV\*(C'\fR, and <\s-1PV\s0> that carry a more +verbose value of the 5 status variables. +.Sp +Where the '\s-1DSLIP\s0' characters have the following meanings: +.Sp +.Vb 7 +\& D \- Development Stage (Note: *NO IMPLIED TIMESCALES*): +\& i \- Idea, listed to gain consensus or as a placeholder +\& c \- under construction but pre\-alpha (not yet released) +\& a/b \- Alpha/Beta testing +\& R \- Released +\& M \- Mature (no rigorous definition) +\& S \- Standard, supplied with Perl 5 +\& +\& S \- Support Level: +\& m \- Mailing\-list +\& d \- Developer +\& u \- Usenet newsgroup comp.lang.perl.modules +\& n \- None known, try comp.lang.perl.modules +\& a \- abandoned; volunteers welcome to take over maintenance +\& +\& L \- Language Used: +\& p \- Perl\-only, no compiler needed, should be platform independent +\& c \- C and perl, a C compiler will be needed +\& h \- Hybrid, written in perl with optional C code, no compiler needed +\& + \- C++ and perl, a C++ compiler will be needed +\& o \- perl and another language other than C or C++ +\& +\& I \- Interface Style +\& f \- plain Functions, no references used +\& h \- hybrid, object and function interfaces available +\& n \- no interface at all (huh?) +\& r \- some use of unblessed References or ties +\& O \- Object oriented using blessed references and/or inheritance +\& +\& P \- Public License +\& p \- Standard\-Perl: user may choose between GPL and Artistic +\& g \- GPL: GNU General Public License +\& l \- LGPL: "GNU Lesser General Public License" (previously known as +\& "GNU Library General Public License") +\& b \- BSD: The BSD License +\& a \- Artistic license alone +\& 2 \- Artistic license 2.0 or later +\& o \- open source: approved by www.opensource.org +\& d \- allows distribution without restrictions +\& r \- restricted distribution +\& n \- no license at all +.Ve +.IP "CPAN::Module::force($method,@args)" 4 +.IX Item "CPAN::Module::force($method,@args)" +Forces \s-1CPAN\s0 to perform a task it would normally refuse to +do. Force takes as arguments a method name to be invoked and any number +of additional arguments to pass that method. +The internals of the object get the needed changes so that \s-1CPAN\s0.pm +does not refuse to take the action. See also the section above on the +\&\f(CW\*(C`force\*(C'\fR and the \f(CW\*(C`fforce\*(C'\fR pragma. +.IP "\fBCPAN::Module::get()\fR" 4 +.IX Item "CPAN::Module::get()" +Runs a get on the distribution associated with this module. +.IP "\fBCPAN::Module::inst_file()\fR" 4 +.IX Item "CPAN::Module::inst_file()" +Returns the filename of the module found in \f(CW@INC\fR. The first file found +is reported, just as perl itself stops searching \f(CW@INC\fR once it finds a +module. +.IP "\fBCPAN::Module::available_file()\fR" 4 +.IX Item "CPAN::Module::available_file()" +Returns the filename of the module found in \s-1PERL5LIB\s0 or \f(CW@INC\fR. The +first file found is reported. The advantage of this method over +\&\f(CW\*(C`inst_file\*(C'\fR is that modules that have been tested but not yet +installed are included because \s-1PERL5LIB\s0 keeps track of tested modules. +.IP "\fBCPAN::Module::inst_version()\fR" 4 +.IX Item "CPAN::Module::inst_version()" +Returns the version number of the installed module in readable format. +.IP "\fBCPAN::Module::available_version()\fR" 4 +.IX Item "CPAN::Module::available_version()" +Returns the version number of the available module in readable format. +.IP "\fBCPAN::Module::install()\fR" 4 +.IX Item "CPAN::Module::install()" +Runs an \f(CW\*(C`install\*(C'\fR on the distribution associated with this module. +.IP "\fBCPAN::Module::look()\fR" 4 +.IX Item "CPAN::Module::look()" +Changes to the directory where the distribution associated with this +module has been unpacked and opens a subshell there. Exiting the +subshell returns. +.IP "\fBCPAN::Module::make()\fR" 4 +.IX Item "CPAN::Module::make()" +Runs a \f(CW\*(C`make\*(C'\fR on the distribution associated with this module. +.IP "\fBCPAN::Module::manpage_headline()\fR" 4 +.IX Item "CPAN::Module::manpage_headline()" +If module is installed, peeks into the module's manpage, reads the +headline, and returns it. Moreover, if the module has been downloaded +within this session, does the equivalent on the downloaded module even +if it hasn't been installed yet. +.IP "\fBCPAN::Module::perldoc()\fR" 4 +.IX Item "CPAN::Module::perldoc()" +Runs a \f(CW\*(C`perldoc\*(C'\fR on this module. +.IP "\fBCPAN::Module::readme()\fR" 4 +.IX Item "CPAN::Module::readme()" +Runs a \f(CW\*(C`readme\*(C'\fR on the distribution associated with this module. +.IP "\fBCPAN::Module::reports()\fR" 4 +.IX Item "CPAN::Module::reports()" +Calls the \fBreports()\fR method on the associated distribution object. +.IP "\fBCPAN::Module::test()\fR" 4 +.IX Item "CPAN::Module::test()" +Runs a \f(CW\*(C`test\*(C'\fR on the distribution associated with this module. +.IP "\fBCPAN::Module::uptodate()\fR" 4 +.IX Item "CPAN::Module::uptodate()" +Returns 1 if the module is installed and up-to-date. +.IP "\fBCPAN::Module::userid()\fR" 4 +.IX Item "CPAN::Module::userid()" +Returns the author's \s-1ID\s0 of the module. +.SS "Cache Manager" +.IX Subsection "Cache Manager" +Currently the cache manager only keeps track of the build directory +($CPAN::Config\->{build_dir}). It is a simple \s-1FIFO\s0 mechanism that +deletes complete directories below \f(CW\*(C`build_dir\*(C'\fR as soon as the size of +all directories there gets bigger than \f(CW$CPAN::Config\fR\->{build_cache} +(in \s-1MB\s0). The contents of this cache may be used for later +re-installations that you intend to do manually, but will never be +trusted by \s-1CPAN\s0 itself. This is due to the fact that the user might +use these directories for building modules on different architectures. +.PP +There is another directory ($CPAN::Config\->{keep_source_where}) where +the original distribution files are kept. This directory is not +covered by the cache manager and must be controlled by the user. If +you choose to have the same directory as build_dir and as +keep_source_where directory, then your sources will be deleted with +the same fifo mechanism. +.SS "Bundles" +.IX Subsection "Bundles" +A bundle is just a perl module in the namespace Bundle:: that does not +define any functions or methods. It usually only contains documentation. +.PP +It starts like a perl module with a package declaration and a \f(CW$VERSION\fR +variable. After that the pod section looks like any other pod with the +only difference being that \fIone special pod section\fR exists starting with +(verbatim): +.PP +.Vb 1 +\& =head1 CONTENTS +.Ve +.PP +In this pod section each line obeys the format +.PP +.Vb 1 +\& Module_Name [Version_String] [\- optional text] +.Ve +.PP +The only required part is the first field, the name of a module +(e.g. Foo::Bar, i.e. \fInot\fR the name of the distribution file). The rest +of the line is optional. The comment part is delimited by a dash just +as in the man page header. +.PP +The distribution of a bundle should follow the same convention as +other distributions. +.PP +Bundles are treated specially in the \s-1CPAN\s0 package. If you say 'install +Bundle::Tkkit' (assuming such a bundle exists), \s-1CPAN\s0 will install all +the modules in the \s-1CONTENTS\s0 section of the pod. You can install your +own Bundles locally by placing a conformant Bundle file somewhere into +your \f(CW@INC\fR path. The \fBautobundle()\fR command which is available in the +shell interface does that for you by including all currently installed +modules in a snapshot bundle file. +.SH "PREREQUISITES" +.IX Header "PREREQUISITES" +The \s-1CPAN\s0 program is trying to depend on as little as possible so the +user can use it in hostile environment. It works better the more goodies +the environment provides. For example if you try in the \s-1CPAN\s0 shell +.PP +.Vb 1 +\& install Bundle::CPAN +.Ve +.PP +or +.PP +.Vb 1 +\& install Bundle::CPANxxl +.Ve +.PP +you will find the shell more convenient than the bare shell before. +.PP +If you have a local mirror of \s-1CPAN\s0 and can access all files with +\&\*(L"file:\*(R" URLs, then you only need a perl later than perl5.003 to run +this module. Otherwise Net::FTP is strongly recommended. \s-1LWP\s0 may be +required for non-UNIX systems, or if your nearest \s-1CPAN\s0 site is +associated with a \s-1URL\s0 that is not \f(CW\*(C`ftp:\*(C'\fR. +.PP +If you have neither Net::FTP nor \s-1LWP,\s0 there is a fallback mechanism +implemented for an external ftp command or for an external lynx +command. +.SH "UTILITIES" +.IX Header "UTILITIES" +.SS "Finding packages and \s-1VERSION\s0" +.IX Subsection "Finding packages and VERSION" +This module presumes that all packages on \s-1CPAN\s0 +.IP "\(bu" 2 +declare their \f(CW$VERSION\fR variable in an easy to parse manner. This +prerequisite can hardly be relaxed because it consumes far too much +memory to load all packages into the running program just to determine +the \f(CW$VERSION\fR variable. Currently all programs that are dealing with +version use something like this +.Sp +.Vb 2 +\& perl \-MExtUtils::MakeMaker \-le \e +\& \*(Aqprint MM\->parse_version(shift)\*(Aq filename +.Ve +.Sp +If you are author of a package and wonder if your \f(CW$VERSION\fR can be +parsed, please try the above method. +.IP "\(bu" 2 +come as compressed or gzipped tarfiles or as zip files and contain a +\&\f(CW\*(C`Makefile.PL\*(C'\fR or \f(CW\*(C`Build.PL\*(C'\fR (well, we try to handle a bit more, but +with little enthusiasm). +.SS "Debugging" +.IX Subsection "Debugging" +Debugging this module is more than a bit complex due to interference from +the software producing the indices on \s-1CPAN,\s0 the mirroring process on \s-1CPAN,\s0 +packaging, configuration, synchronicity, and even (gasp!) due to bugs +within the \s-1CPAN\s0.pm module itself. +.PP +For debugging the code of \s-1CPAN\s0.pm itself in interactive mode, some +debugging aid can be turned on for most packages within +\&\s-1CPAN\s0.pm with one of +.IP "o debug package..." 2 +.IX Item "o debug package..." +sets debug mode for packages. +.IP "o debug \-package..." 2 +.IX Item "o debug -package..." +unsets debug mode for packages. +.IP "o debug all" 2 +.IX Item "o debug all" +turns debugging on for all packages. +.IP "o debug number" 2 +.IX Item "o debug number" +.PP +which sets the debugging packages directly. Note that \f(CW\*(C`o debug 0\*(C'\fR +turns debugging off. +.PP +What seems a successful strategy is the combination of \f(CW\*(C`reload +cpan\*(C'\fR and the debugging switches. Add a new debug statement while +running in the shell and then issue a \f(CW\*(C`reload cpan\*(C'\fR and see the new +debugging messages immediately without losing the current context. +.PP +\&\f(CW\*(C`o debug\*(C'\fR without an argument lists the valid package names and the +current set of packages in debugging mode. \f(CW\*(C`o debug\*(C'\fR has built-in +completion support. +.PP +For debugging of \s-1CPAN\s0 data there is the \f(CW\*(C`dump\*(C'\fR command which takes +the same arguments as make/test/install and outputs each object's +Data::Dumper dump. If an argument looks like a perl variable and +contains one of \f(CW\*(C`$\*(C'\fR, \f(CW\*(C`@\*(C'\fR or \f(CW\*(C`%\*(C'\fR, it is \fBeval()\fRed and fed to +Data::Dumper directly. +.SS "Floppy, Zip, Offline Mode" +.IX Subsection "Floppy, Zip, Offline Mode" +\&\s-1CPAN\s0.pm works nicely without network access, too. If you maintain machines +that are not networked at all, you should consider working with \f(CW\*(C`file:\*(C'\fR +URLs. You'll have to collect your modules somewhere first. So +you might use \s-1CPAN\s0.pm to put together all you need on a networked +machine. Then copy the \f(CW$CPAN::Config\fR\->{keep_source_where} (but not +\&\f(CW$CPAN::Config\fR\->{build_dir}) directory on a floppy. This floppy is kind +of a personal \s-1CPAN. CPAN\s0.pm on the non-networked machines works nicely +with this floppy. See also below the paragraph about CD-ROM support. +.SS "Basic Utilities for Programmers" +.IX Subsection "Basic Utilities for Programmers" +.IP "has_inst($module)" 2 +.IX Item "has_inst($module)" +Returns true if the module is installed. Used to load all modules into +the running \s-1CPAN\s0.pm that are considered optional. The config variable +\&\f(CW\*(C`dontload_list\*(C'\fR intercepts the \f(CW\*(C`has_inst()\*(C'\fR call such +that an optional module is not loaded despite being available. For +example, the following command will prevent \f(CW\*(C`YAML.pm\*(C'\fR from being +loaded: +.Sp +.Vb 1 +\& cpan> o conf dontload_list push YAML +.Ve +.Sp +See the source for details. +.IP "use_inst($module)" 2 +.IX Item "use_inst($module)" +Similary to \fBhas_inst()\fR tries to load optional library but also dies if +library is not available +.IP "has_usable($module)" 2 +.IX Item "has_usable($module)" +Returns true if the module is installed and in a usable state. Only +useful for a handful of modules that are used internally. See the +source for details. +.IP "instance($module)" 2 +.IX Item "instance($module)" +The constructor for all the singletons used to represent modules, +distributions, authors, and bundles. If the object already exists, this +method returns the object; otherwise, it calls the constructor. +.IP "\fBfrontend()\fR" 2 +.IX Item "frontend()" +.PD 0 +.IP "frontend($new_frontend)" 2 +.IX Item "frontend($new_frontend)" +.PD +Getter/setter for frontend object. Method just allows to subclass \s-1CPAN\s0.pm. +.SH "SECURITY" +.IX Header "SECURITY" +There's no strong security layer in \s-1CPAN\s0.pm. \s-1CPAN\s0.pm helps you to +install foreign, unmasked, unsigned code on your machine. We compare +to a checksum that comes from the net just as the distribution file +itself. But we try to make it easy to add security on demand: +.SS "Cryptographically signed modules" +.IX Subsection "Cryptographically signed modules" +Since release 1.77, \s-1CPAN\s0.pm has been able to verify cryptographically +signed module distributions using Module::Signature. The \s-1CPAN\s0 modules +can be signed by their authors, thus giving more security. The simple +unsigned \s-1MD5\s0 checksums that were used before by \s-1CPAN\s0 protect mainly +against accidental file corruption. +.PP +You will need to have Module::Signature installed, which in turn +requires that you have at least one of Crypt::OpenPGP module or the +command-line \fIgpg\fR tool installed. +.PP +You will also need to be able to connect over the Internet to the public +key servers, like pgp.mit.edu, and their port 11731 (the \s-1HKP\s0 protocol). +.PP +The configuration parameter check_sigs is there to turn signature +checking on or off. +.SH "EXPORT" +.IX Header "EXPORT" +Most functions in package \s-1CPAN\s0 are exported by default. The reason +for this is that the primary use is intended for the cpan shell or for +one-liners. +.SH "ENVIRONMENT" +.IX Header "ENVIRONMENT" +When the \s-1CPAN\s0 shell enters a subshell via the look command, it sets +the environment \s-1CPAN_SHELL_LEVEL\s0 to 1, or increments that variable if it is +already set. +.PP +When \s-1CPAN\s0 runs, it sets the environment variable \s-1PERL5_CPAN_IS_RUNNING\s0 +to the \s-1ID\s0 of the running process. It also sets +\&\s-1PERL5_CPANPLUS_IS_RUNNING\s0 to prevent runaway processes which could +happen with older versions of Module::Install. +.PP +When running \f(CW\*(C`perl Makefile.PL\*(C'\fR, the environment variable +\&\f(CW\*(C`PERL5_CPAN_IS_EXECUTING\*(C'\fR is set to the full path of the +\&\f(CW\*(C`Makefile.PL\*(C'\fR that is being executed. This prevents runaway processes +with newer versions of Module::Install. +.PP +When the config variable ftp_passive is set, all downloads will be run +with the environment variable \s-1FTP_PASSIVE\s0 set to this value. This is +in general a good idea as it influences both Net::FTP and \s-1LWP\s0 based +connections. The same effect can be achieved by starting the cpan +shell with this environment variable set. For Net::FTP alone, one can +also always set passive mode by running libnetcfg. +.SH "POPULATE AN INSTALLATION WITH LOTS OF MODULES" +.IX Header "POPULATE AN INSTALLATION WITH LOTS OF MODULES" +Populating a freshly installed perl with one's favorite modules is pretty +easy if you maintain a private bundle definition file. To get a useful +blueprint of a bundle definition file, the command autobundle can be used +on the \s-1CPAN\s0 shell command line. This command writes a bundle definition +file for all modules installed for the current perl +interpreter. It's recommended to run this command once only, and from then +on maintain the file manually under a private name, say +Bundle/my_bundle.pm. With a clever bundle file you can then simply say +.PP +.Vb 1 +\& cpan> install Bundle::my_bundle +.Ve +.PP +then answer a few questions and go out for coffee (possibly +even in a different city). +.PP +Maintaining a bundle definition file means keeping track of two +things: dependencies and interactivity. \s-1CPAN\s0.pm sometimes fails on +calculating dependencies because not all modules define all MakeMaker +attributes correctly, so a bundle definition file should specify +prerequisites as early as possible. On the other hand, it's +annoying that so many distributions need some interactive configuring. So +what you can try to accomplish in your private bundle file is to have the +packages that need to be configured early in the file and the gentle +ones later, so you can go out for coffee after a few minutes and leave \s-1CPAN\s0.pm +to churn away unattended. +.SH "WORKING WITH CPAN.pm BEHIND FIREWALLS" +.IX Header "WORKING WITH CPAN.pm BEHIND FIREWALLS" +Thanks to Graham Barr for contributing the following paragraphs about +the interaction between perl, and various firewall configurations. For +further information on firewalls, it is recommended to consult the +documentation that comes with the \fIncftp\fR program. If you are unable to +go through the firewall with a simple Perl setup, it is likely +that you can configure \fIncftp\fR so that it works through your firewall. +.SS "Three basic types of firewalls" +.IX Subsection "Three basic types of firewalls" +Firewalls can be categorized into three basic types. +.IP "http firewall" 4 +.IX Item "http firewall" +This is when the firewall machine runs a web server, and to access the +outside world, you must do so via that web server. If you set environment +variables like http_proxy or ftp_proxy to values beginning with http://, +or in your web browser you've proxy information set, then you know +you are running behind an http firewall. +.Sp +To access servers outside these types of firewalls with perl (even for +ftp), you need \s-1LWP\s0 or HTTP::Tiny. +.IP "ftp firewall" 4 +.IX Item "ftp firewall" +This where the firewall machine runs an ftp server. This kind of +firewall will only let you access ftp servers outside the firewall. +This is usually done by connecting to the firewall with ftp, then +entering a username like \*(L"user@outside.host.com\*(R". +.Sp +To access servers outside these type of firewalls with perl, you +need Net::FTP. +.IP "One-way visibility" 4 +.IX Item "One-way visibility" +One-way visibility means these firewalls try to make themselves +invisible to users inside the firewall. An \s-1FTP\s0 data connection is +normally created by sending your \s-1IP\s0 address to the remote server and then +listening for the return connection. But the remote server will not be able to +connect to you because of the firewall. For these types of firewall, +\&\s-1FTP\s0 connections need to be done in a passive mode. +.Sp +There are two that I can think off. +.RS 4 +.IP "\s-1SOCKS\s0" 4 +.IX Item "SOCKS" +If you are using a \s-1SOCKS\s0 firewall, you will need to compile perl and link +it with the \s-1SOCKS\s0 library. This is what is normally called a 'socksified' +perl. With this executable you will be able to connect to servers outside +the firewall as if it were not there. +.IP "\s-1IP\s0 Masquerade" 4 +.IX Item "IP Masquerade" +This is when the firewall implemented in the kernel (via \s-1NAT,\s0 or networking +address translation), it allows you to hide a complete network behind one +\&\s-1IP\s0 address. With this firewall no special compiling is needed as you can +access hosts directly. +.Sp +For accessing ftp servers behind such firewalls you usually need to +set the environment variable \f(CW\*(C`FTP_PASSIVE\*(C'\fR or the config variable +ftp_passive to a true value. +.RE +.RS 4 +.RE +.SS "Configuring lynx or ncftp for going through a firewall" +.IX Subsection "Configuring lynx or ncftp for going through a firewall" +If you can go through your firewall with e.g. lynx, presumably with a +command such as +.PP +.Vb 1 +\& /usr/local/bin/lynx \-pscott:tiger +.Ve +.PP +then you would configure \s-1CPAN\s0.pm with the command +.PP +.Vb 1 +\& o conf lynx "/usr/local/bin/lynx \-pscott:tiger" +.Ve +.PP +That's all. Similarly for ncftp or ftp, you would configure something +like +.PP +.Vb 1 +\& o conf ncftp "/usr/bin/ncftp \-f /home/scott/ncftplogin.cfg" +.Ve +.PP +Your mileage may vary... +.SH "FAQ" +.IX Header "FAQ" +.IP "1)" 4 +.IX Item "1)" +I installed a new version of module X but \s-1CPAN\s0 keeps saying, +I have the old version installed +.Sp +Probably you \fBdo\fR have the old version installed. This can +happen if a module installs itself into a different directory in the +\&\f(CW@INC\fR path than it was previously installed. This is not really a +\&\s-1CPAN\s0.pm problem, you would have the same problem when installing the +module manually. The easiest way to prevent this behaviour is to add +the argument \f(CW\*(C`UNINST=1\*(C'\fR to the \f(CW\*(C`make install\*(C'\fR call, and that is why +many people add this argument permanently by configuring +.Sp +.Vb 1 +\& o conf make_install_arg UNINST=1 +.Ve +.IP "2)" 4 +.IX Item "2)" +So why is UNINST=1 not the default? +.Sp +Because there are people who have their precise expectations about who +may install where in the \f(CW@INC\fR path and who uses which \f(CW@INC\fR array. In +fine tuned environments \f(CW\*(C`UNINST=1\*(C'\fR can cause damage. +.IP "3)" 4 +.IX Item "3)" +I want to clean up my mess, and install a new perl along with +all modules I have. How do I go about it? +.Sp +Run the autobundle command for your old perl and optionally rename the +resulting bundle file (e.g. Bundle/mybundle.pm), install the new perl +with the Configure option prefix, e.g. +.Sp +.Vb 1 +\& ./Configure \-Dprefix=/usr/local/perl\-5.6.78.9 +.Ve +.Sp +Install the bundle file you produced in the first step with something like +.Sp +.Vb 1 +\& cpan> install Bundle::mybundle +.Ve +.Sp +and you're done. +.IP "4)" 4 +.IX Item "4)" +When I install bundles or multiple modules with one command +there is too much output to keep track of. +.Sp +You may want to configure something like +.Sp +.Vb 2 +\& o conf make_arg "| tee \-ai /root/.cpan/logs/make.out" +\& o conf make_install_arg "| tee \-ai /root/.cpan/logs/make_install.out" +.Ve +.Sp +so that \s-1STDOUT\s0 is captured in a file for later inspection. +.IP "5)" 4 +.IX Item "5)" +I am not root, how can I install a module in a personal directory? +.Sp +As of \s-1CPAN 1.9463,\s0 if you do not have permission to write the default perl +library directories, \s-1CPAN\s0's configuration process will ask you whether +you want to bootstrap <local::lib>, which makes keeping a personal +perl library directory easy. +.Sp +Another thing you should bear in mind is that the \s-1UNINST\s0 parameter can +be dangerous when you are installing into a private area because you +might accidentally remove modules that other people depend on that are +not using the private area. +.IP "6)" 4 +.IX Item "6)" +How to get a package, unwrap it, and make a change before building it? +.Sp +Have a look at the \f(CW\*(C`look\*(C'\fR (!) command. +.IP "7)" 4 +.IX Item "7)" +I installed a Bundle and had a couple of fails. When I +retried, everything resolved nicely. Can this be fixed to work +on first try? +.Sp +The reason for this is that \s-1CPAN\s0 does not know the dependencies of all +modules when it starts out. To decide about the additional items to +install, it just uses data found in the \s-1META\s0.yml file or the generated +Makefile. An undetected missing piece breaks the process. But it may +well be that your Bundle installs some prerequisite later than some +depending item and thus your second try is able to resolve everything. +Please note, \s-1CPAN\s0.pm does not know the dependency tree in advance and +cannot sort the queue of things to install in a topologically correct +order. It resolves perfectly well \fBif\fR all modules declare the +prerequisites correctly with the \s-1PREREQ_PM\s0 attribute to MakeMaker or +the \f(CW\*(C`requires\*(C'\fR stanza of Module::Build. For bundles which fail and +you need to install often, it is recommended to sort the Bundle +definition file manually. +.IP "8)" 4 +.IX Item "8)" +In our intranet, we have many modules for internal use. How +can I integrate these modules with \s-1CPAN\s0.pm but without uploading +the modules to \s-1CPAN\s0? +.Sp +Have a look at the CPAN::Site module. +.IP "9)" 4 +.IX Item "9)" +When I run \s-1CPAN\s0's shell, I get an error message about things in my +\&\f(CW\*(C`/etc/inputrc\*(C'\fR (or \f(CW\*(C`~/.inputrc\*(C'\fR) file. +.Sp +These are readline issues and can only be fixed by studying readline +configuration on your architecture and adjusting the referenced file +accordingly. Please make a backup of the \f(CW\*(C`/etc/inputrc\*(C'\fR or \f(CW\*(C`~/.inputrc\*(C'\fR +and edit them. Quite often harmless changes like uppercasing or +lowercasing some arguments solves the problem. +.IP "10)" 4 +.IX Item "10)" +Some authors have strange characters in their names. +.Sp +Internally \s-1CPAN\s0.pm uses the \s-1UTF\-8\s0 charset. If your terminal is +expecting \s-1ISO\-8859\-1\s0 charset, a converter can be activated by setting +term_is_latin to a true value in your config file. One way of doing so +would be +.Sp +.Vb 1 +\& cpan> o conf term_is_latin 1 +.Ve +.Sp +If other charset support is needed, please file a bug report against +\&\s-1CPAN\s0.pm at rt.cpan.org and describe your needs. Maybe we can extend +the support or maybe \s-1UTF\-8\s0 terminals become widely available. +.Sp +Note: this config variable is deprecated and will be removed in a +future version of \s-1CPAN\s0.pm. It will be replaced with the conventions +around the family of \f(CW$LANG\fR and \f(CW$LC_\fR* environment variables. +.IP "11)" 4 +.IX Item "11)" +When an install fails for some reason and then I correct the error +condition and retry, \s-1CPAN\s0.pm refuses to install the module, saying +\&\f(CW\*(C`Already tried without success\*(C'\fR. +.Sp +Use the force pragma like so +.Sp +.Vb 1 +\& force install Foo::Bar +.Ve +.Sp +Or you can use +.Sp +.Vb 1 +\& look Foo::Bar +.Ve +.Sp +and then \f(CW\*(C`make install\*(C'\fR directly in the subshell. +.IP "12)" 4 +.IX Item "12)" +How do I install a \*(L"\s-1DEVELOPER RELEASE\*(R"\s0 of a module? +.Sp +By default, \s-1CPAN\s0 will install the latest non-developer release of a +module. If you want to install a dev release, you have to specify the +partial path starting with the author id to the tarball you wish to +install, like so: +.Sp +.Vb 1 +\& cpan> install KWILLIAMS/Module\-Build\-0.27_07.tar.gz +.Ve +.Sp +Note that you can use the \f(CW\*(C`ls\*(C'\fR command to get this path listed. +.IP "13)" 4 +.IX Item "13)" +How do I install a module and all its dependencies from the commandline, +without being prompted for anything, despite my \s-1CPAN\s0 configuration +(or lack thereof)? +.Sp +\&\s-1CPAN\s0 uses ExtUtils::MakeMaker's \fBprompt()\fR function to ask its questions, so +if you set the \s-1PERL_MM_USE_DEFAULT\s0 environment variable, you shouldn't be +asked any questions at all (assuming the modules you are installing are +nice about obeying that variable as well): +.Sp +.Vb 1 +\& % PERL_MM_USE_DEFAULT=1 perl \-MCPAN \-e \*(Aqinstall My::Module\*(Aq +.Ve +.IP "14)" 4 +.IX Item "14)" +How do I create a Module::Build based Build.PL derived from an +ExtUtils::MakeMaker focused Makefile.PL? +.Sp +http://search.cpan.org/dist/Module\-Build\-Convert/ +.IP "15)" 4 +.IX Item "15)" +I'm frequently irritated with the \s-1CPAN\s0 shell's inability to help me +select a good mirror. +.Sp +\&\s-1CPAN\s0 can now help you select a \*(L"good\*(R" mirror, based on which ones have the +lowest 'ping' round-trip times. From the shell, use the command 'o conf init +urllist' and allow \s-1CPAN\s0 to automatically select mirrors for you. +.Sp +Beyond that help, the urllist config parameter is yours. You can add and remove +sites at will. You should find out which sites have the best up-to-dateness, +bandwidth, reliability, etc. and are topologically close to you. Some people +prefer fast downloads, others up-to-dateness, others reliability. You decide +which to try in which order. +.Sp +Henk P. Penning maintains a site that collects data about \s-1CPAN\s0 sites: +.Sp +.Vb 1 +\& http://mirrors.cpan.org/ +.Ve +.Sp +Also, feel free to play with experimental features. Run +.Sp +.Vb 1 +\& o conf init randomize_urllist ftpstats_period ftpstats_size +.Ve +.Sp +and choose your favorite parameters. After a few downloads running the +\&\f(CW\*(C`hosts\*(C'\fR command will probably assist you in choosing the best mirror +sites. +.IP "16)" 4 +.IX Item "16)" +Why do I get asked the same questions every time I start the shell? +.Sp +You can make your configuration changes permanent by calling the +command \f(CW\*(C`o conf commit\*(C'\fR. Alternatively set the \f(CW\*(C`auto_commit\*(C'\fR +variable to true by running \f(CW\*(C`o conf init auto_commit\*(C'\fR and answering +the following question with yes. +.IP "17)" 4 +.IX Item "17)" +Older versions of \s-1CPAN\s0.pm had the original root directory of all +tarballs in the build directory. Now there are always random +characters appended to these directory names. Why was this done? +.Sp +The random characters are provided by File::Temp and ensure that each +module's individual build directory is unique. This makes running +\&\s-1CPAN\s0.pm in concurrent processes simultaneously safe. +.IP "18)" 4 +.IX Item "18)" +Speaking of the build directory. Do I have to clean it up myself? +.Sp +You have the choice to set the config variable \f(CW\*(C`scan_cache\*(C'\fR to +\&\f(CW\*(C`never\*(C'\fR. Then you must clean it up yourself. The other possible +values, \f(CW\*(C`atstart\*(C'\fR and \f(CW\*(C`atexit\*(C'\fR clean up the build directory when you +start (or more precisely, after the first extraction into the build +directory) or exit the \s-1CPAN\s0 shell, respectively. If you never start up +the \s-1CPAN\s0 shell, you probably also have to clean up the build directory +yourself. +.IP "19)" 4 +.IX Item "19)" +How can I switch to sudo instead of local::lib? +.Sp +The following 5 environment veriables need to be reset to the previous +values: \s-1PATH, PERL5LIB, PERL_LOCAL_LIB_ROOT, PERL_MB_OPT, PERL_MM_OPT\s0; +and these two \s-1CPAN\s0.pm config variables must be reconfigured: +make_install_make_command and mbuild_install_build_command. The five +env variables have probably been overwritten in your \f(CW$HOME\fR/.bashrc or +some equivalent. You either find them there and delete their traces +and logout/login or you override them temporarily, depending on your +exact desire. The two cpanpm config variables can be set with: +.Sp +.Vb 1 +\& o conf init /install_.*_command/ +.Ve +.Sp +probably followed by +.Sp +.Vb 1 +\& o conf commit +.Ve +.SH "COMPATIBILITY" +.IX Header "COMPATIBILITY" +.SS "\s-1OLD PERL VERSIONS\s0" +.IX Subsection "OLD PERL VERSIONS" +\&\s-1CPAN\s0.pm is regularly tested to run under 5.005 and assorted +newer versions. It is getting more and more difficult to get the +minimal prerequisites working on older perls. It is close to +impossible to get the whole Bundle::CPAN working there. If you're in +the position to have only these old versions, be advised that \s-1CPAN\s0 is +designed to work fine without the Bundle::CPAN installed. +.PP +To get things going, note that GBARR/Scalar\-List\-Utils\-1.18.tar.gz is +compatible with ancient perls and that File::Temp is listed as a +prerequisite but \s-1CPAN\s0 has reasonable workarounds if it is missing. +.SS "\s-1CPANPLUS\s0" +.IX Subsection "CPANPLUS" +This module and its competitor, the \s-1CPANPLUS\s0 module, are both much +cooler than the other. \s-1CPAN\s0.pm is older. \s-1CPANPLUS\s0 was designed to be +more modular, but it was never intended to be compatible with \s-1CPAN\s0.pm. +.SS "\s-1CPANMINUS\s0" +.IX Subsection "CPANMINUS" +In the year 2010 App::cpanminus was launched as a new approach to a +cpan shell with a considerably smaller footprint. Very cool stuff. +.SH "SECURITY ADVICE" +.IX Header "SECURITY ADVICE" +This software enables you to upgrade software on your computer and so +is inherently dangerous because the newly installed software may +contain bugs and may alter the way your computer works or even make it +unusable. Please consider backing up your data before every upgrade. +.SH "BUGS" +.IX Header "BUGS" +Please report bugs via <http://rt.cpan.org/> +.PP +Before submitting a bug, please make sure that the traditional method +of building a Perl module package from a shell by following the +installation instructions of that package still works in your +environment. +.SH "AUTHOR" +.IX Header "AUTHOR" +Andreas Koenig \f(CW\*(C`<andk@cpan.org>\*(C'\fR +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. +.PP +See <http://www.perl.com/perl/misc/Artistic.html> +.SH "TRANSLATIONS" +.IX Header "TRANSLATIONS" +Kawai,Takanori provides a Japanese translation of a very old version +of this manpage at +<http://homepage3.nifty.com/hippo2000/perltips/CPAN.htm> +.SH "SEE ALSO" +.IX Header "SEE ALSO" +Many people enter the \s-1CPAN\s0 shell by running the cpan utility +program which is installed in the same directory as perl itself. So if +you have this directory in your \s-1PATH\s0 variable (or some equivalent in +your operating system) then typing \f(CW\*(C`cpan\*(C'\fR in a console window will +work for you as well. Above that the utility provides several +commandline shortcuts. +.PP +melezhik (Alexey) sent me a link where he published a chef recipe to +work with \s-1CPAN\s0.pm: http://community.opscode.com/cookbooks/cpan. diff --git a/tools/msys/usr/share/man/man3/CPAN.API.HOWTO.3pm b/tools/msys/usr/share/man/man3/CPAN.API.HOWTO.3pm new file mode 100644 index 0000000000..8f0d90f18f --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.API.HOWTO.3pm @@ -0,0 +1,117 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::API::HOWTO 3" +.TH CPAN::API::HOWTO 3 "2012-09-08" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::API::HOWTO \- a recipe book for programming with CPAN.pm +.SH "RECIPES" +.IX Header "RECIPES" +All of these recipes assume that you have put \*(L"use \s-1CPAN\*(R"\s0 at the top of +your program. +.SS "What distribution contains a particular module?" +.IX Subsection "What distribution contains a particular module?" +.Vb 3 +\& my $distribution = CPAN::Shell\->expand( +\& "Module", "Data::UUID" +\& )\->distribution()\->pretty_id(); +.Ve +.PP +This returns a string of the form \*(L"\s-1AUTHORID/TARBALL\*(R".\s0 If you want the +full path and filename to this distribution on a \s-1CPAN\s0 mirror, then it is +\&\f(CW\*(C`.../authors/id/A/AU/AUTHORID/TARBALL\*(C'\fR. +.SS "What modules does a particular distribution contain?" +.IX Subsection "What modules does a particular distribution contain?" +.Vb 4 +\& CPAN::Index\->reload(); +\& my @modules = CPAN::Shell\->expand( +\& "Distribution", "JHI/Graph\-0.83.tar.gz" +\& )\->containsmods(); +.Ve +.PP +You may also refer to a distribution in the form A/AU/AUTHORID/TARBALL. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +the main \s-1CPAN\s0.pm documentation +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. +.PP +See <http://www.perl.com/perl/misc/Artistic.html> +.SH "AUTHOR" +.IX Header "AUTHOR" +David Cantrell diff --git a/tools/msys/usr/share/man/man3/CPAN.Admin.3pm b/tools/msys/usr/share/man/man3/CPAN.Admin.3pm new file mode 100644 index 0000000000..6e701d5714 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Admin.3pm @@ -0,0 +1,127 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Admin 3" +.TH CPAN::Admin 3 "2018-11-27" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +.Vb 1 +\& CPAN::Admin \- A CPAN Shell for CPAN admins +.Ve +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 1 +\& perl \-MCPAN::Admin \-e shell +.Ve +.SH "STATUS" +.IX Header "STATUS" +Note: this module is currently not maintained. If you need it and fix +it for your needs, please submit patches. +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +CPAN::Admin is a subclass of \s-1CPAN\s0 that adds the commands \f(CW\*(C`register\*(C'\fR +and \f(CW\*(C`modsearch\*(C'\fR to the \s-1CPAN\s0 shell. +.PP +\&\f(CW\*(C`register\*(C'\fR calls \f(CW\*(C`get\*(C'\fR on the named module, assembles a couple of +informations (description, language), and calls Netscape with the +\&\-remote argument so that a form is filled with all the assembled +informations and the registration can be performed with a single +click. If the command line has more than one argument, register does +not run a \f(CW\*(C`get\*(C'\fR, instead it interprets the rest of the line as \s-1DSLI\s0 +status, description, and userid and sends them to netscape such that +the form is again mostly filled and can be edited or confirmed with a +single click. CPAN::Admin never performs the submission click for you, +it is only intended to fill in the form on \s-1PAUSE\s0 and leave the +confirmation to you. +.PP +\&\f(CW\*(C`modsearch\*(C'\fR simply passes the arguments to the search engine for the +modules@perl.org mailing list at <http://www.xray.mpe.mpg.de> where all +registration requests are stored. It does so in the same way as +register, namely with the \f(CW\*(C`netscape \-remote\*(C'\fR command. +.PP +An experimental feature has also been added, namely to color already +registered modules in listings. If you have Term::ANSIColor installed, +the u, r, and m commands will show already registered modules in +green. +.SH "PREREQUISITES" +.IX Header "PREREQUISITES" +URI::Escape, a browser available in the path, the browser must +understand the \-remote switch (as far as I know, this is only +available on \s-1UNIX\s0); coloring of registered modules is only available +if Term::ANSIColor is installed. +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/CPAN.Debug.3pm b/tools/msys/usr/share/man/man3/CPAN.Debug.3pm new file mode 100644 index 0000000000..ca4736c366 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Debug.3pm @@ -0,0 +1,84 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Debug 3" +.TH CPAN::Debug 3 "2016-08-17" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Debug \- internal debugging for CPAN.pm +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/CPAN.Distroprefs.3pm b/tools/msys/usr/share/man/man3/CPAN.Distroprefs.3pm new file mode 100644 index 0000000000..769252a20f --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Distroprefs.3pm @@ -0,0 +1,222 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Distroprefs 3" +.TH CPAN::Distroprefs 3 "2020-03-01" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Distroprefs \-\- read and match distroprefs +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 1 +\& use CPAN::Distroprefs; +\& +\& my %info = (... distribution/environment info ...); +\& +\& my $finder = CPAN::Distroprefs\->find($prefs_dir, \e%ext_map); +\& +\& while (my $result = $finder\->next) { +\& +\& die $result\->as_string if $result\->is_fatal; +\& +\& warn($result\->as_string), next if $result\->is_warning; +\& +\& for my $pref (@{ $result\->prefs }) { +\& if ($pref\->matches(\e%info)) { +\& return $pref; +\& } +\& } +\& } +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +This module encapsulates reading Distroprefs and matching them against \s-1CPAN\s0 distributions. +.SH "INTERFACE" +.IX Header "INTERFACE" +.Vb 1 +\& my $finder = CPAN::Distroprefs\->find($dir, \e%ext_map); +\& +\& while (my $result = $finder\->next) { ... } +.Ve +.PP +Build an iterator which finds distroprefs files in the tree below the +given directory. Within the tree directories matching \f(CW\*(C`m/^[._]/\*(C'\fR are +pruned. +.PP +\&\f(CW%ext_map\fR is a hashref whose keys are file extensions and whose values are +modules used to load matching files: +.PP +.Vb 5 +\& { +\& \*(Aqyml\*(Aq => \*(AqYAML::Syck\*(Aq, +\& \*(Aqdd\*(Aq => \*(AqData::Dumper\*(Aq, +\& ... +\& } +.Ve +.PP +Each time \f(CW\*(C`$finder\->next\*(C'\fR is called, the iterator returns one of two +possible values: +.IP "\(bu" 4 +a CPAN::Distroprefs::Result object +.IP "\(bu" 4 +\&\f(CW\*(C`undef\*(C'\fR, indicating that no prefs files remain to be found +.SH "RESULTS" +.IX Header "RESULTS" +\&\f(CW\*(C`find()\*(C'\fR returns CPAN::Distroprefs::Result objects to +indicate success or failure when reading a prefs file. +.SS "Common" +.IX Subsection "Common" +All results share some common attributes: +.PP +\fItype\fR +.IX Subsection "type" +.PP +\&\f(CW\*(C`success\*(C'\fR, \f(CW\*(C`warning\*(C'\fR, or \f(CW\*(C`fatal\*(C'\fR +.PP +\fIfile\fR +.IX Subsection "file" +.PP +the file from which these prefs were read, or to which this error refers (relative filename) +.PP +\fIext\fR +.IX Subsection "ext" +.PP +the file's extension, which determines how to load it +.PP +\fIdir\fR +.IX Subsection "dir" +.PP +the directory the file was read from +.PP +\fIabs\fR +.IX Subsection "abs" +.PP +the absolute path to the file +.SS "Errors" +.IX Subsection "Errors" +Error results (warning and fatal) contain: +.PP +\fImsg\fR +.IX Subsection "msg" +.PP +the error message (usually either \f(CW$!\fR or a \s-1YAML\s0 error) +.SS "Successes" +.IX Subsection "Successes" +Success results contain: +.PP +\fIprefs\fR +.IX Subsection "prefs" +.PP +an arrayref of CPAN::Distroprefs::Pref objects +.SH "PREFS" +.IX Header "PREFS" +CPAN::Distroprefs::Pref objects represent individual distroprefs documents. +They are constructed automatically as part of \f(CW\*(C`success\*(C'\fR results from \f(CW\*(C`find()\*(C'\fR. +.PP +\fIdata\fR +.IX Subsection "data" +.PP +the pref information as a hashref, suitable for e.g. passing to Kwalify +.PP +\fImatch_attributes\fR +.IX Subsection "match_attributes" +.PP +returns a list of the valid match attributes (see the Distroprefs section in \s-1CPAN\s0) +.PP +currently: \f(CW\*(C`env perl perlconfig distribution module\*(C'\fR +.PP +\fIhas_any_match\fR +.IX Subsection "has_any_match" +.PP +true if this pref has a 'match' attribute at all +.PP +\fIhas_valid_subkeys\fR +.IX Subsection "has_valid_subkeys" +.PP +true if this pref has a 'match' attribute and at least one valid match attribute +.PP +\fImatches\fR +.IX Subsection "matches" +.PP +.Vb 1 +\& if ($pref\->matches(\e%arg)) { ... } +.Ve +.PP +true if this pref matches the passed-in hashref, which must have a value for +each of the \f(CW\*(C`match_attributes\*(C'\fR (above) +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or modify it under +the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/CPAN.FirstTime.3pm b/tools/msys/usr/share/man/man3/CPAN.FirstTime.3pm new file mode 100644 index 0000000000..48fccb6381 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.FirstTime.3pm @@ -0,0 +1,675 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::FirstTime 3" +.TH CPAN::FirstTime 3 "2020-06-13" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::FirstTime \- Utility for CPAN::Config file Initialization +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +\&\fBCPAN::FirstTime::init()\fR +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +The init routine asks a few questions and writes a CPAN/Config.pm or +CPAN/MyConfig.pm file (depending on what it is currently using). +.PP +In the following all questions and explanations regarding config +variables are collected. +.IP "allow_installing_module_downgrades" 2 +.IX Item "allow_installing_module_downgrades" +The \s-1CPAN\s0 shell can watch the \f(CW\*(C`blib/\*(C'\fR directories that are built up +before running \f(CW\*(C`make test\*(C'\fR to determine whether the current +distribution will end up with modules being overwritten with decreasing module version numbers. It +can then let the build of this distro fail when it discovers a +downgrade. +.Sp +Do you want to allow installing distros with decreasing module +versions compared to what you have installed (yes, no, ask/yes, +ask/no)? +.IP "allow_installing_outdated_dists" 2 +.IX Item "allow_installing_outdated_dists" +The \s-1CPAN\s0 shell can watch the \f(CW\*(C`blib/\*(C'\fR directories that are built up +before running \f(CW\*(C`make test\*(C'\fR to determine whether the current +distribution contains modules that are indexed with a distro with a +higher distro-version number than the current one. It can +then let the build of this distro fail when it would not represent the +most up-to-date version of the distro. +.Sp +Note: choosing anything but 'yes' for this option will need +CPAN::DistnameInfo being installed for taking effect. +.Sp +Do you want to allow installing distros that are not indexed as the +highest distro-version for all contained modules (yes, no, ask/yes, +ask/no)? +.IP "auto_commit" 2 +.IX Item "auto_commit" +Normally \s-1CPAN\s0.pm keeps config variables in memory and changes need to +be saved in a separate 'o conf commit' command to make them permanent +between sessions. If you set the 'auto_commit' option to true, changes +to a config variable are always automatically committed to disk. +.Sp +Always commit changes to config variables to disk? +.IP "build_cache" 2 +.IX Item "build_cache" +\&\s-1CPAN\s0.pm can limit the size of the disk area for keeping the build +directories with all the intermediate files. +.Sp +Cache size for build directory (in \s-1MB\s0)? +.IP "build_dir" 2 +.IX Item "build_dir" +Directory where the build process takes place? +.IP "build_dir_reuse" 2 +.IX Item "build_dir_reuse" +Until version 1.88 \s-1CPAN\s0.pm never trusted the contents of the build_dir +directory between sessions. Since 1.88_58 \s-1CPAN\s0.pm has a YAML-based +mechanism that makes it possible to share the contents of the +build_dir/ directory between different sessions with the same version +of perl. People who prefer to test things several days before +installing will like this feature because it saves a lot of time. +.Sp +If you say yes to the following question, \s-1CPAN\s0 will try to store +enough information about the build process so that it can pick up in +future sessions at the same state of affairs as it left a previous +session. +.Sp +Store and re-use state information about distributions between +\&\s-1CPAN\s0.pm sessions? +.IP "build_requires_install_policy" 2 +.IX Item "build_requires_install_policy" +When a module declares another one as a 'build_requires' prerequisite +this means that the other module is only needed for building or +testing the module but need not be installed permanently. In this case +you may wish to install that other module nonetheless or just keep it +in the 'build_dir' directory to have it available only temporarily. +Installing saves time on future installations but makes the perl +installation bigger. +.Sp +You can choose if you want to always install (yes), never install (no) +or be always asked. In the latter case you can set the default answer +for the question to yes (ask/yes) or no (ask/no). +.Sp +Policy on installing 'build_requires' modules (yes, no, ask/yes, +ask/no)? +.IP "cache_metadata" 2 +.IX Item "cache_metadata" +To considerably speed up the initial \s-1CPAN\s0 shell startup, it is +possible to use Storable to create a cache of metadata. If Storable is +not available, the normal index mechanism will be used. +.Sp +Note: this mechanism is not used when use_sqlite is on and SQLLite is +running. +.Sp +Cache metadata (yes/no)? +.IP "check_sigs" 2 +.IX Item "check_sigs" +\&\s-1CPAN\s0 packages can be digitally signed by authors and thus verified +with the security provided by strong cryptography. The exact mechanism +is defined in the Module::Signature module. While this is generally +considered a good thing, it is not always convenient to the end user +to install modules that are signed incorrectly or where the key of the +author is not available or where some prerequisite for +Module::Signature has a bug and so on. +.Sp +With the check_sigs parameter you can turn signature checking on and +off. The default is off for now because the whole tool chain for the +functionality is not yet considered mature by some. The author of +\&\s-1CPAN\s0.pm would recommend setting it to true most of the time and +turning it off only if it turns out to be annoying. +.Sp +Note that if you do not have Module::Signature installed, no signature +checks will be performed at all. +.Sp +Always try to check and verify signatures if a \s-1SIGNATURE\s0 file is in +the package and Module::Signature is installed (yes/no)? +.IP "cleanup_after_install" 2 +.IX Item "cleanup_after_install" +Users who install modules and do not intend to look back, can free +occupied disk space quickly by letting \s-1CPAN\s0.pm cleanup each build +directory immediately after a successful install. +.Sp +Remove build directory after a successful install? (yes/no)? +.IP "colorize_output" 2 +.IX Item "colorize_output" +When you have Term::ANSIColor installed, you can turn on colorized +output to have some visual differences between normal \s-1CPAN\s0.pm output, +warnings, debugging output, and the output of the modules being +installed. Set your favorite colors after some experimenting with the +Term::ANSIColor module. +.Sp +Please note that on Windows platforms colorized output also requires +the Win32::Console::ANSI module. +.Sp +Do you want to turn on colored output? +.IP "colorize_print" 2 +.IX Item "colorize_print" +Color for normal output? +.IP "colorize_warn" 2 +.IX Item "colorize_warn" +Color for warnings? +.IP "colorize_debug" 2 +.IX Item "colorize_debug" +Color for debugging messages? +.IP "commandnumber_in_prompt" 2 +.IX Item "commandnumber_in_prompt" +The prompt of the cpan shell can contain the current command number +for easier tracking of the session or be a plain string. +.Sp +Do you want the command number in the prompt (yes/no)? +.IP "connect_to_internet_ok" 2 +.IX Item "connect_to_internet_ok" +If you have never defined your own \f(CW\*(C`urllist\*(C'\fR in your configuration +then \f(CW\*(C`CPAN.pm\*(C'\fR will be hesitant to use the built in default sites for +downloading. It will ask you once per session if a connection to the +internet is \s-1OK\s0 and only if you say yes, it will try to connect. But to +avoid this question, you can choose your favorite download sites once +and get away with it. Or, if you have no favorite download sites +answer yes to the following question. +.Sp +If no urllist has been chosen yet, would you prefer \s-1CPAN\s0.pm to connect +to the built-in default sites without asking? (yes/no)? +.IP "ftp_passive" 2 +.IX Item "ftp_passive" +Shall we always set the \s-1FTP_PASSIVE\s0 environment variable when dealing +with ftp download (yes/no)? +.IP "ftpstats_period" 2 +.IX Item "ftpstats_period" +Statistics about downloads are truncated by size and period +simultaneously. +.Sp +How many days shall we keep statistics about downloads? +.IP "ftpstats_size" 2 +.IX Item "ftpstats_size" +Statistics about downloads are truncated by size and period +simultaneously. Setting this to zero or negative disables download +statistics. +.Sp +How many items shall we keep in the statistics about downloads? +.IP "getcwd" 2 +.IX Item "getcwd" +\&\s-1CPAN\s0.pm changes the current working directory often and needs to +determine its own current working directory. Per default it uses +Cwd::cwd but if this doesn't work on your system for some reason, +alternatives can be configured according to the following table: +.Sp +.Vb 5 +\& cwd Cwd::cwd +\& getcwd Cwd::getcwd +\& fastcwd Cwd::fastcwd +\& getdcwd Cwd::getdcwd +\& backtickcwd external command cwd +.Ve +.Sp +Preferred method for determining the current working directory? +.IP "halt_on_failure" 2 +.IX Item "halt_on_failure" +Normally, \s-1CPAN\s0.pm continues processing the full list of targets and +dependencies, even if one of them fails. However, you can specify +that \s-1CPAN\s0 should halt after the first failure. (Note that optional +recommended or suggested modules that fail will not cause a halt.) +.Sp +Do you want to halt on failure (yes/no)? +.IP "histfile" 2 +.IX Item "histfile" +If you have one of the readline packages (Term::ReadLine::Perl, +Term::ReadLine::Gnu, possibly others) installed, the interactive \s-1CPAN\s0 +shell will have history support. The next two questions deal with the +filename of the history file and with its size. If you do not want to +set this variable, please hit \s-1SPACE ENTER\s0 to the following question. +.Sp +File to save your history? +.IP "histsize" 2 +.IX Item "histsize" +Number of lines to save? +.IP "inactivity_timeout" 2 +.IX Item "inactivity_timeout" +Sometimes you may wish to leave the processes run by \s-1CPAN\s0 alone +without caring about them. Because the Makefile.PL or the Build.PL +sometimes contains question you're expected to answer, you can set a +timer that will kill a 'perl Makefile.PL' process after the specified +time in seconds. +.Sp +If you set this value to 0, these processes will wait forever. This is +the default and recommended setting. +.Sp +Timeout for inactivity during {Makefile,Build}.PL? +.IP "index_expire" 2 +.IX Item "index_expire" +The \s-1CPAN\s0 indexes are usually rebuilt once or twice per hour, but the +typical \s-1CPAN\s0 mirror mirrors only once or twice per day. Depending on +the quality of your mirror and your desire to be on the bleeding edge, +you may want to set the following value to more or less than one day +(which is the default). It determines after how many days \s-1CPAN\s0.pm +downloads new indexes. +.Sp +Let the index expire after how many days? +.IP "inhibit_startup_message" 2 +.IX Item "inhibit_startup_message" +When the \s-1CPAN\s0 shell is started it normally displays a greeting message +that contains the running version and the status of readline support. +.Sp +Do you want to turn this message off? +.IP "keep_source_where" 2 +.IX Item "keep_source_where" +Unless you are accessing the \s-1CPAN\s0 on your filesystem via a file: \s-1URL, +CPAN\s0.pm needs to keep the source files it downloads somewhere. Please +supply a directory where the downloaded files are to be kept. +.Sp +Download target directory? +.IP "load_module_verbosity" 2 +.IX Item "load_module_verbosity" +When \s-1CPAN\s0.pm loads a module it needs for some optional feature, it +usually reports about module name and version. Choose 'v' to get this +message, 'none' to suppress it. +.Sp +Verbosity level for loading modules (none or v)? +.IP "makepl_arg" 2 +.IX Item "makepl_arg" +Every Makefile.PL is run by perl in a separate process. Likewise we +run 'make' and 'make install' in separate processes. If you have +any parameters (e.g. \s-1PREFIX, UNINST\s0 or the like) you want to +pass to the calls, please specify them here. +.Sp +If you don't understand this question, just press \s-1ENTER.\s0 +.Sp +Typical frequently used settings: +.Sp +.Vb 1 +\& PREFIX=~/perl # non\-root users (please see manual for more hints) +.Ve +.Sp +Parameters for the 'perl Makefile.PL' command? +.IP "make_arg" 2 +.IX Item "make_arg" +Parameters for the 'make' command? Typical frequently used setting: +.Sp +.Vb 1 +\& \-j3 # dual processor system (on GNU make) +.Ve +.Sp +Your choice: +.IP "make_install_arg" 2 +.IX Item "make_install_arg" +Parameters for the 'make install' command? +Typical frequently used setting: +.Sp +.Vb 2 +\& UNINST=1 # to always uninstall potentially conflicting files +\& # (but do NOT use with local::lib or INSTALL_BASE) +.Ve +.Sp +Your choice: +.IP "make_install_make_command" 2 +.IX Item "make_install_make_command" +Do you want to use a different make command for 'make install'? +Cautious people will probably prefer: +.Sp +.Vb 5 +\& su root \-c make +\& or +\& sudo make +\& or +\& /path1/to/sudo \-u admin_account /path2/to/make +.Ve +.Sp +or some such. Your choice: +.IP "mbuildpl_arg" 2 +.IX Item "mbuildpl_arg" +A Build.PL is run by perl in a separate process. Likewise we run +\&'./Build' and './Build install' in separate processes. If you have any +parameters you want to pass to the calls, please specify them here. +.Sp +Typical frequently used settings: +.Sp +.Vb 1 +\& \-\-install_base /home/xxx # different installation directory +.Ve +.Sp +Parameters for the 'perl Build.PL' command? +.IP "mbuild_arg" 2 +.IX Item "mbuild_arg" +Parameters for the './Build' command? Setting might be: +.Sp +.Vb 1 +\& \-\-extra_linker_flags \-L/usr/foo/lib # non\-standard library location +.Ve +.Sp +Your choice: +.IP "mbuild_install_arg" 2 +.IX Item "mbuild_install_arg" +Parameters for the './Build install' command? Typical frequently used +setting: +.Sp +.Vb 2 +\& \-\-uninst 1 # uninstall conflicting files +\& # (but do NOT use with local::lib or INSTALL_BASE) +.Ve +.Sp +Your choice: +.IP "mbuild_install_build_command" 2 +.IX Item "mbuild_install_build_command" +Do you want to use a different command for './Build install'? Sudo +users will probably prefer: +.Sp +.Vb 5 +\& su root \-c ./Build +\& or +\& sudo ./Build +\& or +\& /path1/to/sudo \-u admin_account ./Build +.Ve +.Sp +or some such. Your choice: +.IP "pager" 2 +.IX Item "pager" +What is your favorite pager program? +.IP "prefer_installer" 2 +.IX Item "prefer_installer" +When you have Module::Build installed and a module comes with both a +Makefile.PL and a Build.PL, which shall have precedence? +.Sp +The main two standard installer modules are the old and well +established ExtUtils::MakeMaker (for short: \s-1EUMM\s0) which uses the +Makefile.PL. And the next generation installer Module::Build (\s-1MB\s0) +which works with the Build.PL (and often comes with a Makefile.PL +too). If a module comes only with one of the two we will use that one +but if both are supplied then a decision must be made between \s-1EUMM\s0 and +\&\s-1MB.\s0 See also http://rt.cpan.org/Ticket/Display.html?id=29235 for a +discussion about the right default. +.Sp +Or, as a third option you can choose \s-1RAND\s0 which will make a random +decision (something regular \s-1CPAN\s0 testers will enjoy). +.Sp +In case you can choose between running a Makefile.PL or a Build.PL, +which installer would you prefer (\s-1EUMM\s0 or \s-1MB\s0 or \s-1RAND\s0)? +.IP "prefs_dir" 2 +.IX Item "prefs_dir" +\&\s-1CPAN\s0.pm can store customized build environments based on regular +expressions for distribution names. These are \s-1YAML\s0 files where the +default options for \s-1CPAN\s0.pm and the environment can be overridden and +dialog sequences can be stored that can later be executed by an +Expect.pm object. The \s-1CPAN\s0.pm distribution comes with some prefab \s-1YAML\s0 +files that cover sample distributions that can be used as blueprints +to store your own prefs. Please check out the distroprefs/ directory of +the \s-1CPAN\s0.pm distribution to get a quick start into the prefs system. +.Sp +Directory where to store default options/environment/dialogs for +building modules that need some customization? +.IP "prerequisites_policy" 2 +.IX Item "prerequisites_policy" +The \s-1CPAN\s0 module can detect when a module which you are trying to build +depends on prerequisites. If this happens, it can build the +prerequisites for you automatically ('follow'), ask you for +confirmation ('ask'), or just ignore them ('ignore'). Choosing +\&'follow' also sets \s-1PERL_AUTOINSTALL\s0 and \s-1PERL_EXTUTILS_AUTOINSTALL\s0 for +\&\*(L"\-\-defaultdeps\*(R" if not already set. +.Sp +Please set your policy to one of the three values. +.Sp +Policy on building prerequisites (follow, ask or ignore)? +.IP "randomize_urllist" 2 +.IX Item "randomize_urllist" +\&\s-1CPAN\s0.pm can introduce some randomness when using hosts for download +that are configured in the urllist parameter. Enter a numeric value +between 0 and 1 to indicate how often you want to let \s-1CPAN\s0.pm try a +random host from the urllist. A value of one specifies to always use a +random host as the first try. A value of zero means no randomness at +all. Anything in between specifies how often, on average, a random +host should be tried first. +.Sp +Randomize parameter +.IP "recommends_policy" 2 +.IX Item "recommends_policy" +(Experimental feature!) Some \s-1CPAN\s0 modules recommend additional, optional dependencies. These should +generally be installed except in resource constrained environments. When this +policy is true, recommended modules will be included with required modules. +.Sp +Include recommended modules? +.IP "scan_cache" 2 +.IX Item "scan_cache" +By default, each time the \s-1CPAN\s0 module is started, cache scanning is +performed to keep the cache size in sync ('atstart'). Alternatively, +scanning and cleanup can happen when \s-1CPAN\s0 exits ('atexit'). To prevent +any cache cleanup, answer 'never'. +.Sp +Perform cache scanning ('atstart', 'atexit' or 'never')? +.IP "shell" 2 +.IX Item "shell" +What is your favorite shell? +.IP "show_unparsable_versions" 2 +.IX Item "show_unparsable_versions" +During the 'r' command \s-1CPAN\s0.pm finds modules without version number. +When the command finishes, it prints a report about this. If you +want this report to be very verbose, say yes to the following +variable. +.Sp +Show all individual modules that have no \f(CW$VERSION\fR? +.IP "show_upload_date" 2 +.IX Item "show_upload_date" +The 'd' and the 'm' command normally only show you information they +have in their in-memory database and thus will never connect to the +internet. If you set the 'show_upload_date' variable to true, 'm' and +\&'d' will additionally show you the upload date of the module or +distribution. Per default this feature is off because it may require a +net connection to get at the upload date. +.Sp +Always try to show upload date with 'd' and 'm' command (yes/no)? +.IP "show_zero_versions" 2 +.IX Item "show_zero_versions" +During the 'r' command \s-1CPAN\s0.pm finds modules with a version number of +zero. When the command finishes, it prints a report about this. If you +want this report to be very verbose, say yes to the following +variable. +.Sp +Show all individual modules that have a \f(CW$VERSION\fR of zero? +.IP "suggests_policy" 2 +.IX Item "suggests_policy" +(Experimental feature!) Some \s-1CPAN\s0 modules suggest additional, optional dependencies. These 'suggest' +dependencies provide enhanced operation. When this policy is true, suggested +modules will be included with required modules. +.Sp +Include suggested modules? +.IP "tar_verbosity" 2 +.IX Item "tar_verbosity" +When \s-1CPAN\s0.pm uses the tar command, which switch for the verbosity +shall be used? Choose 'none' for quiet operation, 'v' for file +name listing, 'vv' for full listing. +.Sp +Tar command verbosity level (none or v or vv)? +.IP "term_is_latin" 2 +.IX Item "term_is_latin" +The next option deals with the charset (a.k.a. character set) your +terminal supports. In general, \s-1CPAN\s0 is English speaking territory, so +the charset does not matter much but some \s-1CPAN\s0 have names that are +outside the \s-1ASCII\s0 range. If your terminal supports \s-1UTF\-8,\s0 you should +say no to the next question. If it expects \s-1ISO\-8859\-1\s0 (also known as +\&\s-1LATIN1\s0) then you should say yes. If it supports neither, your answer +does not matter because you will not be able to read the names of some +authors anyway. If you answer no, names will be output in \s-1UTF\-8.\s0 +.Sp +Your terminal expects \s-1ISO\-8859\-1\s0 (yes/no)? +.IP "term_ornaments" 2 +.IX Item "term_ornaments" +When using Term::ReadLine, you can turn ornaments on so that your +input stands out against the output from \s-1CPAN\s0.pm. +.Sp +Do you want to turn ornaments on? +.IP "test_report" 2 +.IX Item "test_report" +The goal of the \s-1CPAN\s0 Testers project (http://testers.cpan.org/) is to +test as many \s-1CPAN\s0 packages as possible on as many platforms as +possible. This provides valuable feedback to module authors and +potential users to identify bugs or platform compatibility issues and +improves the overall quality and value of \s-1CPAN.\s0 +.Sp +One way you can contribute is to send test results for each module +that you install. If you install the CPAN::Reporter module, you have +the option to automatically generate and deliver test reports to \s-1CPAN\s0 +Testers whenever you run tests on a \s-1CPAN\s0 package. +.Sp +See the CPAN::Reporter documentation for additional details and +configuration settings. If your firewall blocks outgoing traffic, +you may need to configure CPAN::Reporter before sending reports. +.Sp +Generate test reports if CPAN::Reporter is installed (yes/no)? +.IP "perl5lib_verbosity" 2 +.IX Item "perl5lib_verbosity" +When \s-1CPAN\s0.pm extends \f(CW@INC\fR via \s-1PERL5LIB,\s0 it prints a list of +directories added (or a summary of how many directories are +added). Choose 'v' to get this message, 'none' to suppress it. +.Sp +Verbosity level for \s-1PERL5LIB\s0 changes (none or v)? +.IP "prefer_external_tar" 2 +.IX Item "prefer_external_tar" +Per default all untar operations are done with the perl module +Archive::Tar; by setting this variable to true the external tar +command is used if available; on Unix this is usually preferred +because they have a reliable and fast gnutar implementation. +.Sp +Use the external tar program instead of Archive::Tar? +.IP "trust_test_report_history" 2 +.IX Item "trust_test_report_history" +When a distribution has already been tested by CPAN::Reporter on +this machine, \s-1CPAN\s0 can skip the test phase and just rely on the +test report history instead. +.Sp +Note that this will not apply to distributions that failed tests +because of missing dependencies. Also, tests can be run +regardless of the history using \*(L"force\*(R". +.Sp +Do you want to rely on the test report history (yes/no)? +.IP "urllist_ping_external" 2 +.IX Item "urllist_ping_external" +When automatic selection of the nearest cpan mirrors is performed, +turn on the use of the external ping via Net::Ping::External. This is +recommended in the case the local network has a transparent proxy. +.Sp +Do you want to use the external ping command when autoselecting +mirrors? +.IP "urllist_ping_verbose" 2 +.IX Item "urllist_ping_verbose" +When automatic selection of the nearest cpan mirrors is performed, +this option can be used to turn on verbosity during the selection +process. +.Sp +Do you want to see verbosity turned on when autoselecting mirrors? +.IP "use_prompt_default" 2 +.IX Item "use_prompt_default" +When this is true, \s-1CPAN\s0 will set \s-1PERL_MM_USE_DEFAULT\s0 to a true +value. This causes ExtUtils::MakeMaker (and compatible) prompts +to use default values instead of stopping to prompt you to answer +questions. It also sets \s-1NONINTERACTIVE_TESTING\s0 to a true value to +signal more generally that distributions should not try to +interact with you. +.Sp +Do you want to use prompt defaults (yes/no)? +.IP "use_sqlite" 2 +.IX Item "use_sqlite" +CPAN::SQLite is a layer between the index files that are downloaded +from the \s-1CPAN\s0 and \s-1CPAN\s0.pm that speeds up metadata queries and reduces +memory consumption of \s-1CPAN\s0.pm considerably. +.Sp +Use CPAN::SQLite if available? (yes/no)? +.IP "version_timeout" 2 +.IX Item "version_timeout" +This timeout prevents \s-1CPAN\s0 from hanging when trying to parse a +pathologically coded \f(CW$VERSION\fR from a module. +.Sp +The default is 15 seconds. If you set this value to 0, no timeout +will occur, but this is not recommended. +.Sp +Timeout for parsing module versions? +.IP "yaml_load_code" 2 +.IX Item "yaml_load_code" +Both \s-1YAML\s0.pm and YAML::Syck are capable of deserialising code. As this +requires a string eval, which might be a security risk, you can use +this option to enable or disable the deserialisation of code via +CPAN::DeferredCode. (Note: This does not work under perl 5.6) +.Sp +Do you want to enable code deserialisation (yes/no)? +.IP "yaml_module" 2 +.IX Item "yaml_module" +At the time of this writing (2009\-03) there are three \s-1YAML\s0 +implementations working: \s-1YAML,\s0 YAML::Syck, and \s-1YAML::XS.\s0 The latter +two are faster but need a C compiler installed on your system. There +may be more alternative \s-1YAML\s0 conforming modules. When I tried two +other players, YAML::Tiny and YAML::Perl, they seemed not powerful +enough to work with \s-1CPAN\s0.pm. This may have changed in the meantime. +.Sp +Which \s-1YAML\s0 implementation would you prefer? +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/CPAN.HandleConfig.3pm b/tools/msys/usr/share/man/man3/CPAN.HandleConfig.3pm new file mode 100644 index 0000000000..26097a49ff --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.HandleConfig.3pm @@ -0,0 +1,113 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::HandleConfig 3" +.TH CPAN::HandleConfig 3 "2020-05-19" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::HandleConfig \- internal configuration handling for CPAN.pm +.ie n .SS """CLASS\->safe_quote ITEM""" +.el .SS "\f(CWCLASS\->safe_quote ITEM\fP" +.IX Subsection "CLASS->safe_quote ITEM" +Quotes an item to become safe against spaces +in shell interpolation. An item is enclosed +in double quotes if: +.PP +.Vb 2 +\& \- the item contains spaces in the middle +\& \- the item does not start with a quote +.Ve +.PP +This happens to avoid shell interpolation +problems when whitespace is present in +directory names. +.PP +This method uses \f(CW\*(C`commands_quote\*(C'\fR to determine +the correct quote. If \f(CW\*(C`commands_quote\*(C'\fR is +a space, no quoting will take place. +.PP +if it starts and ends with the same quote character: leave it as it is +.PP +if it contains no whitespace: leave it as it is +.PP +if it contains whitespace, then +.PP +if it contains quotes: better leave it as it is +.PP +else: quote it with the correct quote type for the box we're on +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/CPAN.Kwalify.3pm b/tools/msys/usr/share/man/man3/CPAN.Kwalify.3pm new file mode 100644 index 0000000000..5628311269 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Kwalify.3pm @@ -0,0 +1,112 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Kwalify 3" +.TH CPAN::Kwalify 3 "2012-09-08" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Kwalify \- Interface between CPAN.pm and Kwalify.pm +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 2 +\& use CPAN::Kwalify; +\& validate($schema_name, $data, $file, $doc); +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +.ie n .IP "_validate($schema_name, $data, $file, $doc)" 4 +.el .IP "_validate($schema_name, \f(CW$data\fR, \f(CW$file\fR, \f(CW$doc\fR)" 4 +.IX Item "_validate($schema_name, $data, $file, $doc)" +\&\f(CW$schema_name\fR is the name of a supported schema. Currently only +\&\f(CW\*(C`distroprefs\*(C'\fR is supported. \f(CW$data\fR is the data to be validated. \f(CW$file\fR +is the absolute path to the file the data are coming from. \f(CW$doc\fR is the +index of the document within \f(CW$doc\fR that is to be validated. The last +two arguments are only there for better error reporting. +.Sp +Relies on being called from within \s-1CPAN\s0.pm. +.Sp +Dies if something fails. Does not return anything useful. +.IP "yaml($schema_name)" 4 +.IX Item "yaml($schema_name)" +Returns the \s-1YAML\s0 text of that schema. Dies if something fails. +.SH "AUTHOR" +.IX Header "AUTHOR" +Andreas Koenig \f(CW\*(C`<andk@cpan.org>\*(C'\fR +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. +.PP +See <http://www.perl.com/perl/misc/Artistic.html> diff --git a/tools/msys/usr/share/man/man3/CPAN.Mirrors.3pm b/tools/msys/usr/share/man/man3/CPAN.Mirrors.3pm new file mode 100644 index 0000000000..3a4d4f65c8 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Mirrors.3pm @@ -0,0 +1,225 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Mirrors 3" +.TH CPAN::Mirrors 3 "2020-05-23" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Mirrors \- Get CPAN mirror information and select a fast one +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 1 +\& use CPAN::Mirrors; +\& +\& my $mirrors = CPAN::Mirrors\->new( $mirrored_by_file ); +\& +\& my $seen = {}; +\& +\& my $best_continent = $mirrors\->find_best_continents( { seen => $seen } ); +\& my @mirrors = $mirrors\->get_mirrors_by_continents( $best_continent ); +\& +\& my $callback = sub { +\& my( $m ) = @_; +\& printf "%s = %s\en", $m\->hostname, $m\->rtt +\& }; +\& $mirrors\->get_mirrors_timings( \e@mirrors, $seen, $callback, %args ); +\& +\& @mirrors = sort { $a\->rtt <=> $b\->rtt } @mirrors; +\& +\& print "Best mirrors are ", map( { $_\->rtt } @mirrors[0..3] ), "\en"; +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +.IP "new( \s-1LOCAL_FILE_NAME\s0 )" 4 +.IX Item "new( LOCAL_FILE_NAME )" +Create a new CPAN::Mirrors object from \s-1LOCAL_FILE_NAME.\s0 This file +should look like that in http://www.cpan.org/MIRRORED.BY . +.IP "\fBcontinents()\fR" 4 +.IX Item "continents()" +Return a list of continents based on those defined in \fI\s-1MIRRORED.BY\s0\fR. +.IP "countries( [\s-1CONTINENTS\s0] )" 4 +.IX Item "countries( [CONTINENTS] )" +Return a list of countries based on those defined in \fI\s-1MIRRORED.BY\s0\fR. +It only returns countries for the continents you specify (as defined +in \f(CW\*(C`continents\*(C'\fR). If you don't specify any continents, it returns all +of the countries listed in \fI\s-1MIRRORED.BY\s0\fR. +.IP "mirrors( [\s-1COUNTRIES\s0] )" 4 +.IX Item "mirrors( [COUNTRIES] )" +Return a list of mirrors based on those defined in \fI\s-1MIRRORED.BY\s0\fR. +It only returns mirrors for the countries you specify (as defined +in \f(CW\*(C`countries\*(C'\fR). If you don't specify any countries, it returns all +of the mirrors listed in \fI\s-1MIRRORED.BY\s0\fR. +.IP "get_mirrors_by_countries( [\s-1COUNTRIES\s0] )" 4 +.IX Item "get_mirrors_by_countries( [COUNTRIES] )" +A more sensible synonym for mirrors. +.IP "get_mirrors_by_continents( [\s-1CONTINENTS\s0] )" 4 +.IX Item "get_mirrors_by_continents( [CONTINENTS] )" +Return a list of mirrors for all of continents you specify. If you don't +specify any continents, it returns all of the mirrors. +.Sp +You can specify a single continent or an array reference of continents. +.IP "get_countries_by_continents( [\s-1CONTINENTS\s0] )" 4 +.IX Item "get_countries_by_continents( [CONTINENTS] )" +A more sensible synonym for countries. +.IP "default_mirror" 4 +.IX Item "default_mirror" +Returns the default mirror, http://www.cpan.org/ . This mirror uses +dynamic \s-1DNS\s0 to give a close mirror. +.IP "best_mirrors" 4 +.IX Item "best_mirrors" +\&\f(CW\*(C`best_mirrors\*(C'\fR checks for the best mirrors based on the list of +continents you pass, or, without that, all continents, as defined +by \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR. It pings each mirror, up to the value of +\&\f(CW\*(C`how_many\*(C'\fR. In list context, it returns up to \f(CW\*(C`how_many\*(C'\fR mirrors. +In scalar context, it returns the single best mirror. +.Sp +Arguments +.Sp +.Vb 5 +\& how_many \- the number of mirrors to return. Default: 1 +\& callback \- a callback for find_best_continents +\& verbose \- true or false on all the whining and moaning. Default: false +\& continents \- an array ref of the continents to check +\& external_ping \- if true, use external ping via Net::Ping::External. Default: false +.Ve +.Sp +If you don't specify the continents, \f(CW\*(C`best_mirrors\*(C'\fR calls +\&\f(CW\*(C`find_best_continents\*(C'\fR to get the list of continents to check. +.Sp +If you don't have Net::Ping v2.13 or later, needed for timings, +this returns the default mirror. +.Sp +\&\f(CW\*(C`external_ping\*(C'\fR should be set and then \f(CW\*(C`Net::Ping::External\*(C'\fR needs +to be installed, if the local network has a transparent proxy. +.IP "get_n_random_mirrors_by_continents( N, [\s-1CONTINENTS\s0] )" 4 +.IX Item "get_n_random_mirrors_by_continents( N, [CONTINENTS] )" +Returns up to N random mirrors for the specified continents. Specify the +continents as an array reference. +.ie n .IP "get_mirrors_timings( \s-1MIRROR_LIST, SEEN, CALLBACK,\s0 %ARGS );" 4 +.el .IP "get_mirrors_timings( \s-1MIRROR_LIST, SEEN, CALLBACK,\s0 \f(CW%ARGS\fR );" 4 +.IX Item "get_mirrors_timings( MIRROR_LIST, SEEN, CALLBACK, %ARGS );" +Pings the listed mirrors and returns a list of mirrors sorted in +ascending ping times. +.Sp +\&\f(CW\*(C`MIRROR_LIST\*(C'\fR is an anonymous array of \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR objects to +ping. +.Sp +The optional argument \f(CW\*(C`SEEN\*(C'\fR is a hash reference used to track the +mirrors you've already pinged. +.Sp +The optional argument \f(CW\*(C`CALLBACK\*(C'\fR is a subroutine reference to call +after each ping. It gets the \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR object after each +ping. +.IP "find_best_continents( \s-1HASH_REF\s0 );" 4 +.IX Item "find_best_continents( HASH_REF );" +\&\f(CW\*(C`find_best_continents\*(C'\fR goes through each continent and pings \f(CW\*(C`N\*(C'\fR +random mirrors on that continent. It then orders the continents by +ascending median ping time. In list context, it returns the ordered list +of continent. In scalar context, it returns the same list as an +anonymous array. +.Sp +Arguments: +.Sp +.Vb 6 +\& n \- the number of hosts to ping for each continent. Default: 3 +\& seen \- a hashref of cached hostname ping times +\& verbose \- true or false for noisy or quiet. Default: false +\& callback \- a subroutine to run after each ping. +\& ping_cache_limit \- how long, in seconds, to reuse previous ping times. +\& Default: 1 day +.Ve +.Sp +The \f(CW\*(C`seen\*(C'\fR hash has hostnames as keys and anonymous arrays as values. +The anonymous array is a triplet of a \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR object, a +ping time, and the epoch time for the measurement. +.Sp +The callback subroutine gets the \f(CW\*(C`CPAN::Mirrored::By\*(C'\fR object, the ping +time, and measurement time (the same things in the \f(CW\*(C`seen\*(C'\fR hashref) as +arguments. \f(CW\*(C`find_best_continents\*(C'\fR doesn't care what the callback does +and ignores the return value. +.Sp +With a low value for \f(CW\*(C`N\*(C'\fR, a single mirror might skew the results enough +to choose a worse continent. If you have that problem, try a larger +value. +.SH "AUTHOR" +.IX Header "AUTHOR" +Andreas Koenig \f(CW\*(C`<andk@cpan.org>\*(C'\fR, David Golden \f(CW\*(C`<dagolden@cpan.org>\*(C'\fR, +brian d foy \f(CW\*(C`<bdfoy@cpan.org>\*(C'\fR +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. +.PP +See <http://www.perl.com/perl/misc/Artistic.html> diff --git a/tools/msys/usr/share/man/man3/CPAN.Nox.3pm b/tools/msys/usr/share/man/man3/CPAN.Nox.3pm new file mode 100644 index 0000000000..14ff7fa908 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Nox.3pm @@ -0,0 +1,100 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Nox 3" +.TH CPAN::Nox 3 "2016-08-17" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Nox \- Wrapper around CPAN.pm without using any XS module +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +Interactive mode: +.PP +.Vb 1 +\& perl \-MCPAN::Nox \-e shell; +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +This package has the same functionality as \s-1CPAN\s0.pm, but tries to +prevent the usage of compiled extensions during its own +execution. Its primary purpose is a rescue in case you upgraded perl +and broke binary compatibility somehow. +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\s-1CPAN\s0 diff --git a/tools/msys/usr/share/man/man3/CPAN.Plugin.3pm b/tools/msys/usr/share/man/man3/CPAN.Plugin.3pm new file mode 100644 index 0000000000..ac7dfc594f --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Plugin.3pm @@ -0,0 +1,123 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Plugin 3" +.TH CPAN::Plugin 3 "2020-05-19" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Plugin \- Base class for CPAN shell extensions +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 2 +\& package CPAN::Plugin::Flurb; +\& use parent \*(AqCPAN::Plugin\*(Aq; +\& +\& sub post_test { +\& my ($self, $distribution_object) = @_; +\& $self = $self\->new (distribution_object => $distribution_object); +\& ...; +\& } +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +.SS "Alpha Status" +.IX Subsection "Alpha Status" +The plugin system in the \s-1CPAN\s0 shell was introduced in version 2.07 and +is still considered experimental. +.SS "How Plugins work?" +.IX Subsection "How Plugins work?" +See \*(L"Plugin support\*(R" in \s-1CPAN\s0. +.SH "METHODS" +.IX Header "METHODS" +.SS "plugin_requires" +.IX Subsection "plugin_requires" +returns list of packages given plugin requires for functionality. +This list is evaluated using \f(CW\*(C`CPAN\->use_inst\*(C'\fR method. +.SS "distribution_object" +.IX Subsection "distribution_object" +Get current distribution object. +.SS "distribution" +.IX Subsection "distribution" +.SS "distribution_info" +.IX Subsection "distribution_info" +.SS "build_dir" +.IX Subsection "build_dir" +Simple delegatees for misc parameters derived from distribution +.SS "is_xs" +.IX Subsection "is_xs" +Predicate to detect whether package contains \s-1XS.\s0 +.SH "AUTHOR" +.IX Header "AUTHOR" +Branislav Zahradnik <barney@cpan.org> diff --git a/tools/msys/usr/share/man/man3/CPAN.Plugin.Specfile.3pm b/tools/msys/usr/share/man/man3/CPAN.Plugin.Specfile.3pm new file mode 100644 index 0000000000..9b5137fdd6 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Plugin.Specfile.3pm @@ -0,0 +1,127 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Plugin::Specfile 3" +.TH CPAN::Plugin::Specfile 3 "2020-05-19" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Plugin::Specfile \- Proof of concept implementation of a trivial CPAN::Plugin +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 2 +\& # once in the cpan shell +\& o conf plugin_list push CPAN::Plugin::Specfile +\& +\& # make permanent +\& o conf commit +\& +\& # any time in the cpan shell to write a spec file +\& test Acme::Meta +\& +\& # disable +\& # if it is the last in plugin_list: +\& o conf plugin_list pop +\& # otherwise, determine the index to splice: +\& o conf plugin_list +\& # and then use splice, e.g. to splice position 3: +\& o conf plugin_list splice 3 1 +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +Implemented as a post-test hook, this plugin writes a specfile after +every successful test run. The content is also written to the +terminal. +.PP +As a side effect, the timestamps of the written specfiles reflect the +linear order of all dependencies. +.PP +\&\fB\s-1WARNING:\s0\fR This code is just a small demo how to use the plugin +system of the \s-1CPAN\s0 shell, not a full fledged spec file writer. Do not +expect new features in this plugin. +.SS "\s-1OPTIONS\s0" +.IX Subsection "OPTIONS" +The target directory to store the spec files in can be set using \f(CW\*(C`dir\*(C'\fR +as in +.PP +.Vb 1 +\& o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/specfiles\-000042 +.Ve +.PP +The default directory for this is the +\&\f(CW\*(C`plugins/CPAN::Plugin::Specfile\*(C'\fR directory in the \fIcpan_home\fR +directory. +.SH "AUTHOR" +.IX Header "AUTHOR" +Andreas Koenig <andk@cpan.org>, Branislav Zahradnik <barney@cpan.org> diff --git a/tools/msys/usr/share/man/man3/CPAN.Queue.3pm b/tools/msys/usr/share/man/man3/CPAN.Queue.3pm new file mode 100644 index 0000000000..625d373ad1 --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Queue.3pm @@ -0,0 +1,84 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Queue 3" +.TH CPAN::Queue 3 "2020-05-19" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Queue \- internal queue support for CPAN.pm +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/CPAN.Tarzip.3pm b/tools/msys/usr/share/man/man3/CPAN.Tarzip.3pm new file mode 100644 index 0000000000..0dcfee83fc --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Tarzip.3pm @@ -0,0 +1,84 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Tarzip 3" +.TH CPAN::Tarzip 3 "2020-05-19" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Tarzip \- internal handling of tar archives for CPAN.pm +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/CPAN.Version.3pm b/tools/msys/usr/share/man/man3/CPAN.Version.3pm new file mode 100644 index 0000000000..411e460fce --- /dev/null +++ b/tools/msys/usr/share/man/man3/CPAN.Version.3pm @@ -0,0 +1,112 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "CPAN::Version 3" +.TH CPAN::Version 3 "2018-09-22" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +CPAN::Version \- utility functions to compare CPAN versions +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 1 +\& use CPAN::Version; +\& +\& CPAN::Version\->vgt("1.1","1.1.1"); # 1 bc. 1.1 > 1.001001 +\& +\& CPAN::Version\->vlt("1.1","1.1"); # 0 bc. 1.1 not < 1.1 +\& +\& CPAN::Version\->vcmp("1.1","1.1.1"); # 1 bc. first is larger +\& +\& CPAN::Version\->vcmp("1.1.1","1.1"); # \-1 bc. first is smaller +\& +\& CPAN::Version\->readable(v1.2.3); # "v1.2.3" +\& +\& CPAN::Version\->vstring("v1.2.3"); # v1.2.3 +\& +\& CPAN::Version\->float2vv(1.002003); # "v1.2.3" +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +This module mediates between some version that perl sees in a package +and the version that is published by the \s-1CPAN\s0 indexer. +.PP +It's only written as a helper module for both \s-1CPAN\s0.pm and \s-1CPANPLUS\s0.pm. +.PP +As it stands it predates version.pm but has the same goal: make +version strings visible and comparable. +.SH "LICENSE" +.IX Header "LICENSE" +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. diff --git a/tools/msys/usr/share/man/man3/MLDBM.3pm b/tools/msys/usr/share/man/man3/MLDBM.3pm new file mode 100644 index 0000000000..6f11ca6152 --- /dev/null +++ b/tools/msys/usr/share/man/man3/MLDBM.3pm @@ -0,0 +1,347 @@ +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "MLDBM 3" +.TH MLDBM 3 "2013-02-21" "perl v5.32.0" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +MLDBM \- store multi\-level Perl hash structure in single level tied hash +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +.Vb 3 +\& use MLDBM; # this gets the default, SDBM +\& #use MLDBM qw(DB_File FreezeThaw); # use FreezeThaw for serializing +\& #use MLDBM qw(DB_File Storable); # use Storable for serializing +\& +\& $dbm = tie %o, \*(AqMLDBM\*(Aq [..other DBM args..] or die $!; +.Ve +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +This module can serve as a transparent interface to any \s-1TIEHASH\s0 package +that is required to store arbitrary perl data, including nested references. +Thus, this module can be used for storing references and other arbitrary data +within \s-1DBM\s0 databases. +.PP +It works by serializing the references in the hash into a single string. In the +underlying \s-1TIEHASH\s0 package (usually a \s-1DBM\s0 database), it is this string that +gets stored. When the value is fetched again, the string is deserialized to +reconstruct the data structure into memory. +.PP +For historical and practical reasons, it requires the \fBData::Dumper\fR package, +available at any \s-1CPAN\s0 site. \fBData::Dumper\fR gives you really nice-looking dumps of +your data structures, in case you wish to look at them on the screen, and +it was the only serializing engine before version 2.00. However, as of version +2.00, you can use any of \fBData::Dumper\fR, \fBFreezeThaw\fR or \fBStorable\fR to +perform the underlying serialization, as hinted at by the \s-1SYNOPSIS\s0 overview +above. Using \fBStorable\fR is usually much faster than the other methods. +.PP +See the \s-1BUGS\s0 section for important limitations. +.SS "Changing the Defaults" +.IX Subsection "Changing the Defaults" +\&\fB\s-1MLDBM\s0\fR relies on an underlying \s-1TIEHASH\s0 implementation (usually a +\&\s-1DBM\s0 package), and an underlying serialization package. The respective +defaults are \fBSDBM_File\fR and \fBData::Dumper\fR. Both of these defaults +can be changed. Changing the \fBSDBM_File\fR default is strongly recommended. +See \s-1WARNINGS\s0 below. +.PP +Three serialization wrappers are currently supported: \fBData::Dumper\fR, +\&\fBStorable\fR, and \fBFreezeThaw\fR. Additional serializers can be +supported by writing a wrapper that implements the interface required by +\&\fBMLDBM::Serializer\fR. See the supported wrappers and the \fBMLDBM::Serializer\fR +source for details. +.PP +In the following, \fI\f(CI$OBJ\fI\fR stands for the tied object, as in: +.PP +.Vb 2 +\& $obj = tie %o, .... +\& $obj = tied %o; +.Ve +.ie n .IP "$MLDBM::UseDB \fIor\fR \fI\f(CI$OBJ\fI\fR\->UseDB(\fI[\s-1TIEDOBJECT\s0]\fR)" 4 +.el .IP "\f(CW$MLDBM::UseDB\fR \fIor\fR \fI\f(CI$OBJ\fI\fR\->UseDB(\fI[\s-1TIEDOBJECT\s0]\fR)" 4 +.IX Item "$MLDBM::UseDB or $OBJ->UseDB([TIEDOBJECT])" +The global \f(CW$MLDBM::UseDB\fR can be set to default to something other than +\&\f(CW\*(C`SDBM_File\*(C'\fR, in case you have a more efficient \s-1DBM,\s0 or if you want to use +this with some other \s-1TIEHASH\s0 implementation. Alternatively, you can specify +the name of the package at \f(CW\*(C`use\*(C'\fR time, as the first \*(L"parameter\*(R". +Nested module names can be specified as \*(L"Foo::Bar\*(R". +.Sp +The corresponding method call returns the underlying \s-1TIEHASH\s0 object when +called without arguments. It can be called with any object that +implements Perl's \s-1TIEHASH\s0 interface, to set that value. +.ie n .IP "$MLDBM::Serializer \fIor\fR \fI\f(CI$OBJ\fI\fR\->Serializer(\fI[\s-1SZROBJECT\s0]\fR)" 4 +.el .IP "\f(CW$MLDBM::Serializer\fR \fIor\fR \fI\f(CI$OBJ\fI\fR\->Serializer(\fI[\s-1SZROBJECT\s0]\fR)" 4 +.IX Item "$MLDBM::Serializer or $OBJ->Serializer([SZROBJECT])" +The global \f(CW$MLDBM::Serializer\fR can be set to the name of the serializing +package to be used. Currently can be set to one of \f(CW\*(C`Data::Dumper\*(C'\fR, +\&\f(CW\*(C`Storable\*(C'\fR, or \f(CW\*(C`FreezeThaw\*(C'\fR. Defaults to \f(CW\*(C`Data::Dumper\*(C'\fR. Alternatively, +you can specify the name of the serializer package at \f(CW\*(C`use\*(C'\fR time, as the +second \*(L"parameter\*(R". +.Sp +The corresponding method call returns the underlying \s-1MLDBM\s0 serializer object +when called without arguments. It can be called with an object that +implements the \s-1MLDBM\s0 serializer interface, to set that value. +.SS "Controlling Serializer Properties" +.IX Subsection "Controlling Serializer Properties" +These methods are meant to supply an interface to the properties of the +underlying serializer used. Do \fBnot\fR call or set them without +understanding the consequences in full. The defaults are usually sensible. +.PP +Not all of these necessarily apply to all the supplied serializers, so we +specify when to apply them. Failure to respect this will usually lead to +an exception. +.ie n .IP "$MLDBM::DumpMeth \fIor\fR \fI\f(CI$OBJ\fI\fR\->DumpMeth(\fI[\s-1METHNAME\s0]\fR)" 4 +.el .IP "\f(CW$MLDBM::DumpMeth\fR \fIor\fR \fI\f(CI$OBJ\fI\fR\->DumpMeth(\fI[\s-1METHNAME\s0]\fR)" 4 +.IX Item "$MLDBM::DumpMeth or $OBJ->DumpMeth([METHNAME])" +If the serializer provides alternative serialization methods, this +can be used to set them. +.Sp +With \fBData::Dumper\fR (which offers a pure Perl and an \s-1XS\s0 verion +of its serializing routine), this is set to \f(CW\*(C`Dumpxs\*(C'\fR by default if that +is supported in your installation. Otherwise, defaults to the slower +\&\f(CW\*(C`Dump\*(C'\fR method. +.Sp +With \fBStorable\fR, a value of \f(CW\*(C`portable\*(C'\fR requests that serialization be +architecture neutral, i.e. the deserialization can later occur on another +platform. Of course, this only makes sense if your database files are +themselves architecture neutral. By default, native format is used for +greater serializing speed in \fBStorable\fR. Both \fBData::Dumper\fR and +\&\fBFreezeThaw\fR are always architecture neutral. +.Sp +\&\fBFreezeThaw\fR does not honor this attribute. +.ie n .IP "$MLDBM::Key \fIor\fR \fI\f(CI$OBJ\fI\fR\->Key(\fI[\s-1KEYSTRING\s0]\fR)" 4 +.el .IP "\f(CW$MLDBM::Key\fR \fIor\fR \fI\f(CI$OBJ\fI\fR\->Key(\fI[\s-1KEYSTRING\s0]\fR)" 4 +.IX Item "$MLDBM::Key or $OBJ->Key([KEYSTRING])" +If the serializer only deals with part of the data (perhaps because +the \s-1TIEHASH\s0 object can natively store some types of data), it may need +a unique key string to recognize the data it handles. This can be used +to set that string. Best left alone. +.Sp +Defaults to the magic string used to recognize \s-1MLDBM\s0 data. It is a six +character wide, unique string. This is best left alone, unless you know +what you are doing. +.Sp +\&\fBStorable\fR and \fBFreezeThaw\fR do not honor this attribute. +.ie n .IP "$MLDBM::RemoveTaint \fIor\fR \fI\f(CI$OBJ\fI\fR\->RemoveTaint(\fI[\s-1BOOL\s0]\fR)" 4 +.el .IP "\f(CW$MLDBM::RemoveTaint\fR \fIor\fR \fI\f(CI$OBJ\fI\fR\->RemoveTaint(\fI[\s-1BOOL\s0]\fR)" 4 +.IX Item "$MLDBM::RemoveTaint or $OBJ->RemoveTaint([BOOL])" +If the serializer can optionally untaint any retrieved data subject to +taint checks in Perl, this can be used to request that feature. Data +that comes from external sources (like disk-files) must always be +viewed with caution, so use this only when you are sure that that is +not an issue. +.Sp +\&\fBData::Dumper\fR uses \f(CW\*(C`eval()\*(C'\fR to deserialize and is therefore subject to +taint checks. Can be set to a true value to make the \fBData::Dumper\fR +serializer untaint the data retrieved. It is not enabled by default. +Use with care. +.Sp +\&\fBStorable\fR and \fBFreezeThaw\fR do not honor this attribute. +.SH "EXAMPLES" +.IX Header "EXAMPLES" +Here is a simple example. Note that does not depend upon the underlying +serializing package\*(--most real life examples should not, usually. +.PP +.Vb 3 +\& use MLDBM; # this gets SDBM and Data::Dumper +\& #use MLDBM qw(SDBM_File Storable); # SDBM and Storable +\& use Fcntl; # to get \*(Aqem constants +\& +\& $dbm = tie %o, \*(AqMLDBM\*(Aq, \*(Aqtestmldbm\*(Aq, O_CREAT|O_RDWR, 0640 or die $!; +\& +\& $c = [\e \*(Aqc\*(Aq]; +\& $b = {}; +\& $a = [1, $b, $c]; +\& $b\->{a} = $a; +\& $b\->{b} = $a\->[1]; +\& $b\->{c} = $a\->[2]; +\& @o{qw(a b c)} = ($a, $b, $c); +\& +\& # +\& # to see what was stored +\& # +\& use Data::Dumper; +\& print Data::Dumper\->Dump([@o{qw(a b c)}], [qw(a b c)]); +\& +\& # +\& # to modify data in a substructure +\& # +\& $tmp = $o{a}; +\& $tmp\->[0] = \*(Aqfoo\*(Aq; +\& $o{a} = $tmp; +\& +\& # +\& # can access the underlying DBM methods transparently +\& # +\& #print $dbm\->fd, "\en"; # DB_File method +.Ve +.PP +Here is another small example using Storable, in a portable format: +.PP +.Vb 1 +\& use MLDBM qw(DB_File Storable); # DB_File and Storable +\& +\& tie %o, \*(AqMLDBM\*(Aq, \*(Aqtestmldbm\*(Aq, O_CREAT|O_RDWR, 0640 or die $!; +\& +\& (tied %o)\->DumpMeth(\*(Aqportable\*(Aq); # Ask for portable binary +\& $o{\*(AqENV\*(Aq} = \e%ENV; # Stores the whole environment +.Ve +.SH "BUGS" +.IX Header "BUGS" +.IP "1." 4 +Adding or altering substructures to a hash value is not entirely transparent +in current perl. If you want to store a reference or modify an existing +reference value in the \s-1DBM,\s0 it must first be retrieved and stored in a +temporary variable for further modifications. In particular, something like +this will \s-1NOT\s0 work properly: +.Sp +.Vb 1 +\& $mldb{key}{subkey}[3] = \*(Aqstuff\*(Aq; # won\*(Aqt work +.Ve +.Sp +Instead, that must be written as: +.Sp +.Vb 3 +\& $tmp = $mldb{key}; # retrieve value +\& $tmp\->{subkey}[3] = \*(Aqstuff\*(Aq; +\& $mldb{key} = $tmp; # store value +.Ve +.Sp +This limitation exists because the perl \s-1TIEHASH\s0 interface currently has no +support for multidimensional ties. +.IP "2." 4 +The \fBData::Dumper\fR serializer uses \fBeval()\fR. A lot. Try the \fBStorable\fR +serializer, which is generally the most efficient. +.SH "WARNINGS" +.IX Header "WARNINGS" +.IP "1." 4 +Many \s-1DBM\s0 implementations have arbitrary limits on the size of records +that can be stored. For example, \s-1SDBM\s0 and many \s-1ODBM\s0 or \s-1NDBM\s0 +implementations have a default limit of 1024 bytes for the size of a +record. \s-1MLDBM\s0 can easily exceed these limits when storing large data +structures, leading to mysterious failures. Although SDBM_File is +used by \s-1MLDBM\s0 by default, it is not a good choice if you're storing +large data structures. Berkeley \s-1DB\s0 and \s-1GDBM\s0 both do not have these +limits, so I recommend using either of those instead. +.IP "2." 4 +\&\s-1MLDBM\s0 does well with data structures that are not too deep and not +too wide. You also need to be careful about how many \f(CW\*(C`FETCH\*(C'\fRes your +code actually ends up doing. Meaning, you should get the most mileage +out of a \f(CW\*(C`FETCH\*(C'\fR by holding on to the highest level value for as long +as you need it. Remember that every toplevel access of the tied hash, +for example \f(CW$mldb{foo}\fR, translates to a \s-1MLDBM\s0 \f(CW\*(C`FETCH()\*(C'\fR call. +.Sp +Too often, people end up writing something like this: +.Sp +.Vb 4 +\& tie %h, \*(AqMLDBM\*(Aq, ...; +\& for my $k (keys %{$h{something}}) { +\& print $h{something}{$k}[0]{foo}{bar}; # FETCH _every_ time! +\& } +.Ve +.Sp +when it should be written this for efficiency: +.Sp +.Vb 5 +\& tie %h, \*(AqMLDBM\*(Aq, ...; +\& my $root = $h{something}; # FETCH _once_ +\& for my $k (keys %$root) { +\& print $k\->[0]{foo}{bar}; +\& } +.Ve +.SH "AUTHORS" +.IX Header "AUTHORS" +Gurusamy Sarathy <\fIgsar@umich.edu\fR>. +.PP +Support for multiple serializing packages by +Raphael Manfredi <\fIRaphael_Manfredi@grenoble.hp.com\fR>. +.PP +Test suite fixes for perl 5.8.0 done by Josh Chamas. +.PP +Copyright (c) 1995\-98 Gurusamy Sarathy. All rights reserved. +.PP +Copyright (c) 1998 Raphael Manfredi. +.PP +Copyright (c) 2002 Josh Chamas, Chamas Enterprises Inc. +.PP +Copyright (c) 2010\-2013 Alexandr Ciornii (alexchorny@gmail.com). +.PP +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. +.SH "VERSION" +.IX Header "VERSION" +Version 2.05 +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\fBperl\fR\|(1), \fBperltie\fR\|(1), \fBperlfunc\fR\|(1), Data::Dumper, FreezeThaw, Storable, DBM::Deep, MLDBM::Serializer::JSON. diff --git a/tools/msys/usr/share/perl5/site_perl/App/Cpan.pm b/tools/msys/usr/share/perl5/site_perl/App/Cpan.pm new file mode 100644 index 0000000000..efd04331c8 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/App/Cpan.pm @@ -0,0 +1,1719 @@ +package App::Cpan; + +use strict; +use warnings; +use vars qw($VERSION); + +use if $] < 5.008 => 'IO::Scalar'; + +$VERSION = '1.676'; + +=head1 NAME + +App::Cpan - easily interact with CPAN from the command line + +=head1 SYNOPSIS + + # with arguments and no switches, installs specified modules + cpan module_name [ module_name ... ] + + # with switches, installs modules with extra behavior + cpan [-cfFimtTw] module_name [ module_name ... ] + + # use local::lib + cpan -I module_name [ module_name ... ] + + # one time mirror override for faster mirrors + cpan -p ... + + # with just the dot, install from the distribution in the + # current directory + cpan . + + # without arguments, starts CPAN.pm shell + cpan + + # without arguments, but some switches + cpan [-ahpruvACDLOPX] + +=head1 DESCRIPTION + +This script provides a command interface (not a shell) to CPAN. At the +moment it uses CPAN.pm to do the work, but it is not a one-shot command +runner for CPAN.pm. + +=head2 Options + +=over 4 + +=item -a + +Creates a CPAN.pm autobundle with CPAN::Shell->autobundle. + +=item -A module [ module ... ] + +Shows the primary maintainers for the specified modules. + +=item -c module + +Runs a `make clean` in the specified module's directories. + +=item -C module [ module ... ] + +Show the F<Changes> files for the specified modules + +=item -D module [ module ... ] + +Show the module details. This prints one line for each out-of-date module +(meaning, modules locally installed but have newer versions on CPAN). +Each line has three columns: module name, local version, and CPAN +version. + +=item -f + +Force the specified action, when it normally would have failed. Use this +to install a module even if its tests fail. When you use this option, +-i is not optional for installing a module when you need to force it: + + % cpan -f -i Module::Foo + +=item -F + +Turn off CPAN.pm's attempts to lock anything. You should be careful with +this since you might end up with multiple scripts trying to muck in the +same directory. This isn't so much of a concern if you're loading a special +config with C<-j>, and that config sets up its own work directories. + +=item -g module [ module ... ] + +Downloads to the current directory the latest distribution of the module. + +=item -G module [ module ... ] + +UNIMPLEMENTED + +Download to the current directory the latest distribution of the +modules, unpack each distribution, and create a git repository for each +distribution. + +If you want this feature, check out Yanick Champoux's C<Git::CPAN::Patch> +distribution. + +=item -h + +Print a help message and exit. When you specify C<-h>, it ignores all +of the other options and arguments. + +=item -i module [ module ... ] + +Install the specified modules. With no other switches, this switch +is implied. + +=item -I + +Load C<local::lib> (think like C<-I> for loading lib paths). Too bad +C<-l> was already taken. + +=item -j Config.pm + +Load the file that has the CPAN configuration data. This should have the +same format as the standard F<CPAN/Config.pm> file, which defines +C<$CPAN::Config> as an anonymous hash. + +If the file does not exist, C<cpan> dies. + +=item -J + +Dump the configuration in the same format that CPAN.pm uses. This is useful +for checking the configuration as well as using the dump as a starting point +for a new, custom configuration. + +=item -l + +List all installed modules with their versions + +=item -L author [ author ... ] + +List the modules by the specified authors. + +=item -m + +Make the specified modules. + +=item -M mirror1,mirror2,... + +A comma-separated list of mirrors to use for just this run. The C<-P> +option can find them for you automatically. + +=item -n + +Do a dry run, but don't actually install anything. (unimplemented) + +=item -O + +Show the out-of-date modules. + +=item -p + +Ping the configured mirrors and print a report + +=item -P + +Find the best mirrors you could be using and use them for the current +session. + +=item -r + +Recompiles dynamically loaded modules with CPAN::Shell->recompile. + +=item -s + +Drop in the CPAN.pm shell. This command does this automatically if you don't +specify any arguments. + +=item -t module [ module ... ] + +Run a `make test` on the specified modules. + +=item -T + +Do not test modules. Simply install them. + +=item -u + +Upgrade all installed modules. Blindly doing this can really break things, +so keep a backup. + +=item -v + +Print the script version and CPAN.pm version then exit. + +=item -V + +Print detailed information about the cpan client. + +=item -w + +UNIMPLEMENTED + +Turn on cpan warnings. This checks various things, like directory permissions, +and tells you about problems you might have. + +=item -x module [ module ... ] + +Find close matches to the named modules that you think you might have +mistyped. This requires the optional installation of Text::Levenshtein or +Text::Levenshtein::Damerau. + +=item -X + +Dump all the namespaces to standard output. + +=back + +=head2 Examples + + # print a help message + cpan -h + + # print the version numbers + cpan -v + + # create an autobundle + cpan -a + + # recompile modules + cpan -r + + # upgrade all installed modules + cpan -u + + # install modules ( sole -i is optional ) + cpan -i Netscape::Booksmarks Business::ISBN + + # force install modules ( must use -i ) + cpan -fi CGI::Minimal URI + + # install modules but without testing them + cpan -Ti CGI::Minimal URI + +=head2 Environment variables + +There are several components in CPAN.pm that use environment variables. +The build tools, L<ExtUtils::MakeMaker> and L<Module::Build> use some, +while others matter to the levels above them. Some of these are specified +by the Perl Toolchain Gang: + +Lancaster Concensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md> + +Oslo Concensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/oslo-consensus.md> + +=over 4 + +=item NONINTERACTIVE_TESTING + +Assume no one is paying attention and skips prompts for distributions +that do that correctly. C<cpan(1)> sets this to C<1> unless it already +has a value (even if that value is false). + +=item PERL_MM_USE_DEFAULT + +Use the default answer for a prompted questions. C<cpan(1)> sets this +to C<1> unless it already has a value (even if that value is false). + +=item CPAN_OPTS + +As with C<PERL5OPT>, a string of additional C<cpan(1)> options to +add to those you specify on the command line. + +=item CPANSCRIPT_LOGLEVEL + +The log level to use, with either the embedded, minimal logger or +L<Log::Log4perl> if it is installed. Possible values are the same as +the C<Log::Log4perl> levels: C<TRACE>, C<DEBUG>, C<INFO>, C<WARN>, +C<ERROR>, and C<FATAL>. The default is C<INFO>. + +=item GIT_COMMAND + +The path to the C<git> binary to use for the Git features. The default +is C</usr/local/bin/git>. + +=back + +=head2 Methods + +=over 4 + +=cut + +use autouse Carp => qw(carp croak cluck); +use CPAN 1.80 (); # needs no test +use Config; +use autouse Cwd => qw(cwd); +use autouse 'Data::Dumper' => qw(Dumper); +use File::Spec::Functions qw(catfile file_name_is_absolute rel2abs); +use File::Basename; +use Getopt::Std; + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Internal constants +use constant TRUE => 1; +use constant FALSE => 0; + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# The return values +use constant HEY_IT_WORKED => 0; +use constant I_DONT_KNOW_WHAT_HAPPENED => 1; # 0b0000_0001 +use constant ITS_NOT_MY_FAULT => 2; +use constant THE_PROGRAMMERS_AN_IDIOT => 4; +use constant A_MODULE_FAILED_TO_INSTALL => 8; + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# set up the order of options that we layer over CPAN::Shell +BEGIN { # most of this should be in methods +use vars qw( @META_OPTIONS $Default %CPAN_METHODS @CPAN_OPTIONS @option_order + %Method_table %Method_table_index ); + +@META_OPTIONS = qw( h v V I g G M: C A D O l L a r p P j: J w x X ); + +$Default = 'default'; + +%CPAN_METHODS = ( # map switches to method names in CPAN::Shell + $Default => 'install', + 'c' => 'clean', + 'f' => 'force', + 'i' => 'install', + 'm' => 'make', + 't' => 'test', + 'u' => 'upgrade', + 'T' => 'notest', + 's' => 'shell', + ); +@CPAN_OPTIONS = grep { $_ ne $Default } sort keys %CPAN_METHODS; + +@option_order = ( @META_OPTIONS, @CPAN_OPTIONS ); + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# map switches to the subroutines in this script, along with other information. +# use this stuff instead of hard-coded indices and values +sub NO_ARGS () { 0 } +sub ARGS () { 1 } +sub GOOD_EXIT () { 0 } + +%Method_table = ( +# key => [ sub ref, takes args?, exit value, description ] + + # options that do their thing first, then exit + h => [ \&_print_help, NO_ARGS, GOOD_EXIT, 'Printing help' ], + v => [ \&_print_version, NO_ARGS, GOOD_EXIT, 'Printing version' ], + V => [ \&_print_details, NO_ARGS, GOOD_EXIT, 'Printing detailed version' ], + X => [ \&_list_all_namespaces, NO_ARGS, GOOD_EXIT, 'Listing all namespaces' ], + + # options that affect other options + j => [ \&_load_config, ARGS, GOOD_EXIT, 'Use specified config file' ], + J => [ \&_dump_config, NO_ARGS, GOOD_EXIT, 'Dump configuration to stdout' ], + F => [ \&_lock_lobotomy, NO_ARGS, GOOD_EXIT, 'Turn off CPAN.pm lock files' ], + I => [ \&_load_local_lib, NO_ARGS, GOOD_EXIT, 'Loading local::lib' ], + M => [ \&_use_these_mirrors, ARGS, GOOD_EXIT, 'Setting per session mirrors' ], + P => [ \&_find_good_mirrors, NO_ARGS, GOOD_EXIT, 'Finding good mirrors' ], + w => [ \&_turn_on_warnings, NO_ARGS, GOOD_EXIT, 'Turning on warnings' ], + + # options that do their one thing + g => [ \&_download, ARGS, GOOD_EXIT, 'Download the latest distro' ], + G => [ \&_gitify, ARGS, GOOD_EXIT, 'Down and gitify the latest distro' ], + + C => [ \&_show_Changes, ARGS, GOOD_EXIT, 'Showing Changes file' ], + A => [ \&_show_Author, ARGS, GOOD_EXIT, 'Showing Author' ], + D => [ \&_show_Details, ARGS, GOOD_EXIT, 'Showing Details' ], + O => [ \&_show_out_of_date, NO_ARGS, GOOD_EXIT, 'Showing Out of date' ], + l => [ \&_list_all_mods, NO_ARGS, GOOD_EXIT, 'Listing all modules' ], + + L => [ \&_show_author_mods, ARGS, GOOD_EXIT, 'Showing author mods' ], + a => [ \&_create_autobundle, NO_ARGS, GOOD_EXIT, 'Creating autobundle' ], + p => [ \&_ping_mirrors, NO_ARGS, GOOD_EXIT, 'Pinging mirrors' ], + + r => [ \&_recompile, NO_ARGS, GOOD_EXIT, 'Recompiling' ], + u => [ \&_upgrade, NO_ARGS, GOOD_EXIT, 'Running `make test`' ], + 's' => [ \&_shell, NO_ARGS, GOOD_EXIT, 'Running `make test`' ], + + 'x' => [ \&_guess_namespace, ARGS, GOOD_EXIT, 'Guessing namespaces' ], + c => [ \&_default, ARGS, GOOD_EXIT, 'Running `make clean`' ], + f => [ \&_default, ARGS, GOOD_EXIT, 'Installing with force' ], + i => [ \&_default, ARGS, GOOD_EXIT, 'Running `make install`' ], + 'm' => [ \&_default, ARGS, GOOD_EXIT, 'Running `make`' ], + t => [ \&_default, ARGS, GOOD_EXIT, 'Running `make test`' ], + T => [ \&_default, ARGS, GOOD_EXIT, 'Installing with notest' ], + ); + +%Method_table_index = ( + code => 0, + takes_args => 1, + exit_value => 2, + description => 3, + ); +} + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# finally, do some argument processing + +sub _stupid_interface_hack_for_non_rtfmers + { + no warnings 'uninitialized'; + shift @ARGV if( $ARGV[0] eq 'install' and @ARGV > 1 ) + } + +sub _process_options + { + my %options; + + push @ARGV, grep $_, split /\s+/, $ENV{CPAN_OPTS} || ''; + + # if no arguments, just drop into the shell + if( 0 == @ARGV ) { CPAN::shell(); exit 0 } + elsif (Getopt::Std::getopts( + join( '', @option_order ), \%options )) + { + \%options; + } + else { exit 1 } +} + +sub _process_setup_options + { + my( $class, $options ) = @_; + + if( $options->{j} ) + { + $Method_table{j}[ $Method_table_index{code} ]->( $options->{j} ); + delete $options->{j}; + } + elsif ( ! $options->{h} ) { # h "ignores all of the other options and arguments" + # this is what CPAN.pm would do otherwise + local $CPAN::Be_Silent = 1; + CPAN::HandleConfig->load( + # be_silent => 1, deprecated + write_file => 0, + ); + } + + $class->_turn_off_testing if $options->{T}; + + foreach my $o ( qw(F I w P M) ) + { + next unless exists $options->{$o}; + $Method_table{$o}[ $Method_table_index{code} ]->( $options->{$o} ); + delete $options->{$o}; + } + + if( $options->{o} ) + { + my @pairs = map { [ split /=/, $_, 2 ] } split /,/, $options->{o}; + foreach my $pair ( @pairs ) + { + my( $setting, $value ) = @$pair; + $CPAN::Config->{$setting} = $value; + # $logger->debug( "Setting [$setting] to [$value]" ); + } + delete $options->{o}; + } + + my $option_count = grep { $options->{$_} } @option_order; + no warnings 'uninitialized'; + + # don't count options that imply installation + foreach my $opt ( qw(f T) ) { # don't count force or notest + $option_count -= $options->{$opt}; + } + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # if there are no options, set -i (this line fixes RT ticket 16915) + $options->{i}++ unless $option_count; + } + +sub _setup_environment { +# should we override or set defaults? If this were a true interactive +# session, we'd be in the CPAN shell. + +# https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md + $ENV{NONINTERACTIVE_TESTING} = 1 unless defined $ENV{NONINTERACTIVE_TESTING}; + $ENV{PERL_MM_USE_DEFAULT} = 1 unless defined $ENV{PERL_MM_USE_DEFAULT}; + } + +=item run() + +Just do it. + +The C<run> method returns 0 on success and a positive number on +failure. See the section on EXIT CODES for details on the values. + +=cut + +my $logger; + +sub run + { + my $class = shift; + + my $return_value = HEY_IT_WORKED; # assume that things will work + + $logger = $class->_init_logger; + $logger->debug( "Using logger from @{[ref $logger]}" ); + + $class->_hook_into_CPANpm_report; + $logger->debug( "Hooked into output" ); + + $class->_stupid_interface_hack_for_non_rtfmers; + $logger->debug( "Patched cargo culting" ); + + my $options = $class->_process_options; + $logger->debug( "Options are @{[Dumper($options)]}" ); + + $class->_process_setup_options( $options ); + + $class->_setup_environment( $options ); + + OPTION: foreach my $option ( @option_order ) + { + next unless $options->{$option}; + + my( $sub, $takes_args, $description ) = + map { $Method_table{$option}[ $Method_table_index{$_} ] } + qw( code takes_args description ); + + unless( ref $sub eq ref sub {} ) + { + $return_value = THE_PROGRAMMERS_AN_IDIOT; + last OPTION; + } + + $logger->info( "[$option] $description -- ignoring other arguments" ) + if( @ARGV && ! $takes_args ); + + $return_value = $sub->( \ @ARGV, $options ); + + last; + } + + return $return_value; + } + +my $LEVEL; +{ +package + Local::Null::Logger; # hide from PAUSE + +my @LOGLEVELS = qw(TRACE DEBUG INFO WARN ERROR FATAL); +$LEVEL = uc($ENV{CPANSCRIPT_LOGLEVEL} || 'INFO'); +my %LL = map { $LOGLEVELS[$_] => $_ } 0..$#LOGLEVELS; +unless (defined $LL{$LEVEL}){ + warn "Unsupported loglevel '$LEVEL', setting to INFO"; + $LEVEL = 'INFO'; +} +sub new { bless \ my $x, $_[0] } +sub AUTOLOAD { + my $autoload = our $AUTOLOAD; + $autoload =~ s/.*://; + return if $LL{uc $autoload} < $LL{$LEVEL}; + $CPAN::Frontend->mywarn(">($autoload): $_\n") + for split /[\r\n]+/, $_[1]; +} +sub DESTROY { 1 } +} + +# load a module without searching the default entry for the current +# directory +sub _safe_load_module { + my $name = shift; + + local @INC = @INC; + pop @INC if $INC[-1] eq '.'; + + eval "require $name; 1"; +} + +sub _init_logger + { + my $log4perl_loaded = _safe_load_module("Log::Log4perl"); + + unless( $log4perl_loaded ) + { + print STDOUT "Loading internal logger. Log::Log4perl recommended for better logging\n"; + $logger = Local::Null::Logger->new; + return $logger; + } + + Log::Log4perl::init( \ <<"HERE" ); +log4perl.rootLogger=$LEVEL, A1 +log4perl.appender.A1=Log::Log4perl::Appender::Screen +log4perl.appender.A1.layout=PatternLayout +log4perl.appender.A1.layout.ConversionPattern=%m%n +HERE + + $logger = Log::Log4perl->get_logger( 'App::Cpan' ); + } + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +sub _default + { + my( $args, $options ) = @_; + + my $switch = ''; + + # choose the option that we're going to use + # we'll deal with 'f' (force) later, so skip it + foreach my $option ( @CPAN_OPTIONS ) + { + next if ( $option eq 'f' or $option eq 'T' ); + next unless $options->{$option}; + $switch = $option; + last; + } + + # 1. with no switches, but arguments, use the default switch (install) + # 2. with no switches and no args, start the shell + # 3. With a switch but no args, die! These switches need arguments. + if( not $switch and @$args ) { $switch = $Default; } + elsif( not $switch and not @$args ) { return CPAN::shell() } + elsif( $switch and not @$args ) + { die "Nothing to $CPAN_METHODS{$switch}!\n"; } + + # Get and check the method from CPAN::Shell + my $method = $CPAN_METHODS{$switch}; + die "CPAN.pm cannot $method!\n" unless CPAN::Shell->can( $method ); + + # call the CPAN::Shell method, with force or notest if specified + my $action = do { + if( $options->{f} ) { sub { CPAN::Shell->force( $method, @_ ) } } + elsif( $options->{T} ) { sub { CPAN::Shell->notest( $method, @_ ) } } + else { sub { CPAN::Shell->$method( @_ ) } } + }; + + # How do I handle exit codes for multiple arguments? + my @errors = (); + + $options->{x} or _disable_guessers(); + + foreach my $arg ( @$args ) + { + # check the argument and perhaps capture typos + my $module = _expand_module( $arg ) or do { + $logger->error( "Skipping $arg because I couldn't find a matching namespace." ); + next; + }; + + _clear_cpanpm_output(); + $action->( $arg ); + + my $error = _cpanpm_output_indicates_failure(); + push @errors, $error if $error; + } + + return do { + if( @errors ) { $errors[0] } + else { HEY_IT_WORKED } + }; + + } + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +=for comment + +CPAN.pm sends all the good stuff either to STDOUT, or to a temp +file if $CPAN::Be_Silent is set. I have to intercept that output +so I can find out what happened. + +=cut + +BEGIN { +my $scalar = ''; + +sub _hook_into_CPANpm_report + { + no warnings 'redefine'; + + *CPAN::Shell::myprint = sub { + my($self,$what) = @_; + $scalar .= $what if defined $what; + $self->print_ornamented($what, + $CPAN::Config->{colorize_print}||'bold blue on_white', + ); + }; + + *CPAN::Shell::mywarn = sub { + my($self,$what) = @_; + $scalar .= $what if defined $what; + $self->print_ornamented($what, + $CPAN::Config->{colorize_warn}||'bold red on_white' + ); + }; + + } + +sub _clear_cpanpm_output { $scalar = '' } + +sub _get_cpanpm_output { $scalar } + +# These are lines I don't care about in CPAN.pm output. If I can +# filter out the informational noise, I have a better chance to +# catch the error signal +my @skip_lines = ( + qr/^\QWarning \(usually harmless\)/, + qr/\bwill not store persistent state\b/, + qr(//hint//), + qr/^\s+reports\s+/, + qr/^Try the command/, + qr/^\s+$/, + qr/^to find objects/, + qr/^\s*Database was generated on/, + qr/^Going to read/, + qr|^\s+i\s+/|, # the i /Foo::Whatever/ line when it doesn't know + ); + +sub _get_cpanpm_last_line + { + my $fh; + + if( $] < 5.008 ) { + $fh = IO::Scalar->new( \ $scalar ); + } + else { + eval q{ open $fh, '<', \\ $scalar; }; + } + + my @lines = <$fh>; + + # This is a bit ugly. Once we examine a line, we have to + # examine the line before it and go through all of the same + # regexes. I could do something fancy, but this works. + REGEXES: { + foreach my $regex ( @skip_lines ) + { + if( $lines[-1] =~ m/$regex/ ) + { + pop @lines; + redo REGEXES; # we have to go through all of them for every line! + } + } + } + + $logger->debug( "Last interesting line of CPAN.pm output is:\n\t$lines[-1]" ); + + $lines[-1]; + } +} + +BEGIN { +my $epic_fail_words = join '|', + qw( Error stop(?:ping)? problems force not unsupported + fail(?:ed)? Cannot\s+install ); + +sub _cpanpm_output_indicates_failure + { + my $last_line = _get_cpanpm_last_line(); + + my $result = $last_line =~ /\b(?:$epic_fail_words)\b/i; + return A_MODULE_FAILED_TO_INSTALL if $last_line =~ /\b(?:Cannot\s+install)\b/i; + + $result || (); + } +} + +sub _cpanpm_output_indicates_success + { + my $last_line = _get_cpanpm_last_line(); + + my $result = $last_line =~ /\b(?:\s+-- OK|PASS)\b/; + $result || (); + } + +sub _cpanpm_output_is_vague + { + return FALSE if + _cpanpm_output_indicates_failure() || + _cpanpm_output_indicates_success(); + + return TRUE; + } + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +sub _turn_on_warnings { + carp "Warnings are implemented yet"; + return HEY_IT_WORKED; + } + +sub _turn_off_testing { + $logger->debug( 'Trusting test report history' ); + $CPAN::Config->{trust_test_report_history} = 1; + return HEY_IT_WORKED; + } + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +sub _print_help + { + $logger->info( "Use perldoc to read the documentation" ); + my $HAVE_PERLDOC = eval { require Pod::Perldoc; 1; }; + if ($HAVE_PERLDOC) { + system qq{"$^X" -e "require Pod::Perldoc; Pod::Perldoc->run()" $0}; + exit; + } else { + warn "Please install Pod::Perldoc, maybe try 'cpan -i Pod::Perldoc'\n"; + return HEY_IT_WORKED; + } + } + +sub _print_version # -v + { + $logger->info( + "$0 script version $VERSION, CPAN.pm version " . CPAN->VERSION ); + + return HEY_IT_WORKED; + } + +sub _print_details # -V + { + _print_version(); + + _check_install_dirs(); + + $logger->info( '-' x 50 . "\nChecking configured mirrors..." ); + foreach my $mirror ( @{ $CPAN::Config->{urllist} } ) { + _print_ping_report( $mirror ); + } + + $logger->info( '-' x 50 . "\nChecking for faster mirrors..." ); + + { + require CPAN::Mirrors; + + if ( $CPAN::Config->{connect_to_internet_ok} ) { + $CPAN::Frontend->myprint(qq{Trying to fetch a mirror list from the Internet\n}); + eval { CPAN::FTP->localize('MIRRORED.BY',File::Spec->catfile($CPAN::Config->{keep_source_where},'MIRRORED.BY'),3,1) } + or $CPAN::Frontend->mywarn(<<'HERE'); +We failed to get a copy of the mirror list from the Internet. +You will need to provide CPAN mirror URLs yourself. +HERE + $CPAN::Frontend->myprint("\n"); + } + + my $mirrors = CPAN::Mirrors->new( _mirror_file() ); + my @continents = $mirrors->find_best_continents; + + my @mirrors = $mirrors->get_mirrors_by_continents( $continents[0] ); + my @timings = $mirrors->get_mirrors_timings( \@mirrors ); + + foreach my $timing ( @timings ) { + $logger->info( sprintf "%s (%0.2f ms)", + $timing->hostname, $timing->rtt ); + } + } + + return HEY_IT_WORKED; + } + +sub _check_install_dirs + { + my $makepl_arg = $CPAN::Config->{makepl_arg}; + my $mbuildpl_arg = $CPAN::Config->{mbuildpl_arg}; + + my @custom_dirs; + # PERL_MM_OPT + push @custom_dirs, + $makepl_arg =~ m/INSTALL_BASE\s*=\s*(\S+)/g, + $mbuildpl_arg =~ m/--install_base\s*=\s*(\S+)/g; + + if( @custom_dirs ) { + foreach my $dir ( @custom_dirs ) { + _print_inc_dir_report( $dir ); + } + } + + # XXX: also need to check makepl_args, etc + + my @checks = ( + [ 'core', [ grep $_, @Config{qw(installprivlib installarchlib)} ] ], + [ 'vendor', [ grep $_, @Config{qw(installvendorlib installvendorarch)} ] ], + [ 'site', [ grep $_, @Config{qw(installsitelib installsitearch)} ] ], + [ 'PERL5LIB', _split_paths( $ENV{PERL5LIB} ) ], + [ 'PERLLIB', _split_paths( $ENV{PERLLIB} ) ], + ); + + $logger->info( '-' x 50 . "\nChecking install dirs..." ); + foreach my $tuple ( @checks ) { + my( $label ) = $tuple->[0]; + + $logger->info( "Checking $label" ); + $logger->info( "\tno directories for $label" ) unless @{ $tuple->[1] }; + foreach my $dir ( @{ $tuple->[1] } ) { + _print_inc_dir_report( $dir ); + } + } + + } + +sub _split_paths + { + [ map { _expand_filename( $_ ) } split /$Config{path_sep}/, $_[0] || '' ]; + } + + +=pod + +Stolen from File::Path::Expand + +=cut + +sub _expand_filename + { + my( $path ) = @_; + no warnings 'uninitialized'; + $logger->debug( "Expanding path $path\n" ); + $path =~ s{\A~([^/]+)?}{ + _home_of( $1 || $> ) || "~$1" + }e; + return $path; + } + +sub _home_of + { + require User::pwent; + my( $user ) = @_; + my $ent = User::pwent::getpw($user) or return; + return $ent->dir; + } + +sub _get_default_inc + { + require Config; + + [ @Config::Config{ _vars() }, '.' ]; + } + +sub _vars { + qw( + installarchlib + installprivlib + installsitearch + installsitelib + ); + } + +sub _ping_mirrors { + my $urls = $CPAN::Config->{urllist}; + require URI; + + foreach my $url ( @$urls ) { + my( $obj ) = URI->new( $url ); + next unless _is_pingable_scheme( $obj ); + my $host = $obj->host; + _print_ping_report( $obj ); + } + + } + +sub _is_pingable_scheme { + my( $uri ) = @_; + + $uri->scheme eq 'file' + } + +sub _mirror_file { + my $file = do { + my $file = 'MIRRORED.BY'; + my $local_path = File::Spec->catfile( + $CPAN::Config->{keep_source_where}, $file ); + + if( -e $local_path ) { $local_path } + else { + require CPAN::FTP; + CPAN::FTP->localize( $file, $local_path, 3, 1 ); + $local_path; + } + }; + } + +sub _find_good_mirrors { + require CPAN::Mirrors; + + my $mirrors = CPAN::Mirrors->new( _mirror_file() ); + + my @mirrors = $mirrors->best_mirrors( + how_many => 5, + verbose => 1, + ); + + foreach my $mirror ( @mirrors ) { + next unless eval { $mirror->can( 'http' ) }; + _print_ping_report( $mirror->http ); + } + + $CPAN::Config->{urllist} = [ + map { $_->http } @mirrors + ]; + } + +sub _print_inc_dir_report + { + my( $dir ) = shift; + + my $writeable = -w $dir ? '+' : '!!! (not writeable)'; + $logger->info( "\t$writeable $dir" ); + return -w $dir; + } + +sub _print_ping_report + { + my( $mirror ) = @_; + + my $rtt = eval { _get_ping_report( $mirror ) }; + my $result = $rtt ? sprintf "+ (%4d ms)", $rtt * 1000 : '!'; + + $logger->info( + sprintf "\t%s %s", $result, $mirror + ); + } + +sub _get_ping_report + { + require URI; + my( $mirror ) = @_; + my( $url ) = ref $mirror ? $mirror : URI->new( $mirror ); #XXX + require Net::Ping; + + my $ping = Net::Ping->new( 'tcp', 1 ); + + if( $url->scheme eq 'file' ) { + return -e $url->file; + } + + my( $port ) = $url->port; + + return unless $port; + + if ( $ping->can('port_number') ) { + $ping->port_number($port); + } + else { + $ping->{'port_num'} = $port; + } + + $ping->hires(1) if $ping->can( 'hires' ); + my( $alive, $rtt ) = eval{ $ping->ping( $url->host ) }; + $alive ? $rtt : undef; + } + +sub _load_local_lib # -I + { + $logger->debug( "Loading local::lib" ); + + my $rc = _safe_load_module("local::lib"); + unless( $rc ) { + $logger->logdie( "Could not load local::lib" ); + } + + local::lib->import; + + return HEY_IT_WORKED; + } + +sub _use_these_mirrors # -M + { + $logger->debug( "Setting per session mirrors" ); + unless( $_[0] ) { + $logger->logdie( "The -M switch requires a comma-separated list of mirrors" ); + } + + $CPAN::Config->{urllist} = [ split /,/, $_[0] ]; + + $logger->debug( "Mirrors are @{$CPAN::Config->{urllist}}" ); + + } + +sub _create_autobundle + { + $logger->info( + "Creating autobundle in $CPAN::Config->{cpan_home}/Bundle" ); + + CPAN::Shell->autobundle; + + return HEY_IT_WORKED; + } + +sub _recompile + { + $logger->info( "Recompiling dynamically-loaded extensions" ); + + CPAN::Shell->recompile; + + return HEY_IT_WORKED; + } + +sub _upgrade + { + $logger->info( "Upgrading all modules" ); + + CPAN::Shell->upgrade(); + + return HEY_IT_WORKED; + } + +sub _shell + { + $logger->info( "Dropping into shell" ); + + CPAN::shell(); + + return HEY_IT_WORKED; + } + +sub _load_config # -j + { + my $argument = shift; + + my $file = file_name_is_absolute( $argument ) ? $argument : rel2abs( $argument ); + croak( "cpan config file [$file] for -j does not exist!\n" ) unless -e $file; + + # should I clear out any existing config here? + $CPAN::Config = {}; + delete $INC{'CPAN/Config.pm'}; + + my $rc = eval "require '$file'"; + + # CPAN::HandleConfig::require_myconfig_or_config looks for this + $INC{'CPAN/MyConfig.pm'} = 'fake out!'; + + # CPAN::HandleConfig::load looks for this + $CPAN::Config_loaded = 'fake out'; + + croak( "Could not load [$file]: $@\n") unless $rc; + + return HEY_IT_WORKED; + } + +sub _dump_config # -J + { + my $args = shift; + require Data::Dumper; + + my $fh = $args->[0] || \*STDOUT; + + local $Data::Dumper::Sortkeys = 1; + my $dd = Data::Dumper->new( + [$CPAN::Config], + ['$CPAN::Config'] + ); + + print $fh $dd->Dump, "\n1;\n__END__\n"; + + return HEY_IT_WORKED; + } + +sub _lock_lobotomy # -F + { + no warnings 'redefine'; + + *CPAN::_flock = sub { 1 }; + *CPAN::checklock = sub { 1 }; + + return HEY_IT_WORKED; + } + +sub _download + { + my $args = shift; + + local $CPAN::DEBUG = 1; + + my %paths; + + foreach my $arg ( @$args ) { + $logger->info( "Checking $arg" ); + + my $module = _expand_module( $arg ) or next; + my $path = $module->cpan_file; + + $logger->debug( "Inst file would be $path\n" ); + + $paths{$module} = _get_file( _make_path( $path ) ); + + $logger->info( "Downloaded [$arg] to [$paths{$arg}]" ); + } + + return \%paths; + } + +sub _make_path { join "/", qw(authors id), $_[0] } + +sub _get_file + { + my $path = shift; + + my $loaded = _safe_load_module("LWP::Simple"); + croak "You need LWP::Simple to use features that fetch files from CPAN\n" + unless $loaded; + + my $file = substr $path, rindex( $path, '/' ) + 1; + my $store_path = catfile( cwd(), $file ); + $logger->debug( "Store path is $store_path" ); + + foreach my $site ( @{ $CPAN::Config->{urllist} } ) + { + my $fetch_path = join "/", $site, $path; + $logger->debug( "Trying $fetch_path" ); + my $status_code = LWP::Simple::getstore( $fetch_path, $store_path ); + last if( 200 <= $status_code and $status_code <= 300 ); + $logger->warn( "Could not get [$fetch_path]: Status code $status_code" ); + } + + return $store_path; + } + +sub _gitify + { + my $args = shift; + + my $loaded = _safe_load_module("Archive::Extract"); + croak "You need Archive::Extract to use features that gitify distributions\n" + unless $loaded; + + my $starting_dir = cwd(); + + foreach my $arg ( @$args ) + { + $logger->info( "Checking $arg" ); + my $store_paths = _download( [ $arg ] ); + $logger->debug( "gitify Store path is $store_paths->{$arg}" ); + my $dirname = dirname( $store_paths->{$arg} ); + + my $ae = Archive::Extract->new( archive => $store_paths->{$arg} ); + $ae->extract( to => $dirname ); + + chdir $ae->extract_path; + + my $git = $ENV{GIT_COMMAND} || '/usr/local/bin/git'; + croak "Could not find $git" unless -e $git; + croak "$git is not executable" unless -x $git; + + # can we do this in Pure Perl? + system( $git, 'init' ); + system( $git, qw( add . ) ); + system( $git, qw( commit -a -m ), 'initial import' ); + } + + chdir $starting_dir; + + return HEY_IT_WORKED; + } + +sub _show_Changes + { + my $args = shift; + + foreach my $arg ( @$args ) + { + $logger->info( "Checking $arg\n" ); + + my $module = _expand_module( $arg ) or next; + + my $out = _get_cpanpm_output(); + + next unless eval { $module->inst_file }; + #next if $module->uptodate; + + ( my $id = $module->id() ) =~ s/::/\-/; + + my $url = "http://search.cpan.org/~" . lc( $module->userid ) . "/" . + $id . "-" . $module->cpan_version() . "/"; + + #print "URL: $url\n"; + _get_changes_file($url); + } + + return HEY_IT_WORKED; + } + +sub _get_changes_file + { + croak "Reading Changes files requires LWP::Simple and URI\n" + unless _safe_load_module("LWP::Simple") && _safe_load_module("URI"); + + my $url = shift; + + my $content = LWP::Simple::get( $url ); + $logger->info( "Got $url ..." ) if defined $content; + #print $content; + + my( $change_link ) = $content =~ m|<a href="(.*?)">Changes</a>|gi; + + my $changes_url = URI->new_abs( $change_link, $url ); + $logger->debug( "Change link is: $changes_url" ); + + my $changes = LWP::Simple::get( $changes_url ); + + print $changes; + + return HEY_IT_WORKED; + } + +sub _show_Author + { + my $args = shift; + + foreach my $arg ( @$args ) + { + my $module = _expand_module( $arg ) or next; + + unless( $module ) + { + $logger->info( "Didn't find a $arg module, so no author!" ); + next; + } + + my $author = CPAN::Shell->expand( "Author", $module->userid ); + + next unless $module->userid; + + printf "%-25s %-8s %-25s %s\n", + $arg, $module->userid, $author->email, $author->name; + } + + return HEY_IT_WORKED; + } + +sub _show_Details + { + my $args = shift; + + foreach my $arg ( @$args ) + { + my $module = _expand_module( $arg ) or next; + my $author = CPAN::Shell->expand( "Author", $module->userid ); + + next unless $module->userid; + + print "$arg\n", "-" x 73, "\n\t"; + print join "\n\t", + $module->description ? $module->description : "(no description)", + $module->cpan_file ? $module->cpan_file : "(no cpanfile)", + $module->inst_file ? $module->inst_file :"(no installation file)" , + 'Installed: ' . ($module->inst_version ? $module->inst_version : "not installed"), + 'CPAN: ' . $module->cpan_version . ' ' . + ($module->uptodate ? "" : "Not ") . "up to date", + $author->fullname . " (" . $module->userid . ")", + $author->email; + print "\n\n"; + + } + + return HEY_IT_WORKED; + } + +BEGIN { +my $modules; +sub _get_all_namespaces + { + return $modules if $modules; + $modules = [ map { $_->id } CPAN::Shell->expand( "Module", "/./" ) ]; + } +} + +sub _show_out_of_date + { + my $modules = _get_all_namespaces(); + + printf "%-40s %6s %6s\n", "Module Name", "Local", "CPAN"; + print "-" x 73, "\n"; + + foreach my $module ( @$modules ) + { + next unless $module = _expand_module($module); + next unless $module->inst_file; + next if $module->uptodate; + printf "%-40s %.4f %.4f\n", + $module->id, + $module->inst_version ? $module->inst_version : '', + $module->cpan_version; + } + + return HEY_IT_WORKED; + } + +sub _show_author_mods + { + my $args = shift; + + my %hash = map { lc $_, 1 } @$args; + + my $modules = _get_all_namespaces(); + + foreach my $module ( @$modules ) { + next unless exists $hash{ lc $module->userid }; + print $module->id, "\n"; + } + + return HEY_IT_WORKED; + } + +sub _list_all_mods # -l + { + require File::Find; + + my $args = shift; + + + my $fh = \*STDOUT; + + INC: foreach my $inc ( @INC ) + { + my( $wanted, $reporter ) = _generator(); + File::Find::find( { wanted => $wanted }, $inc ); + + my $count = 0; + FILE: foreach my $file ( @{ $reporter->() } ) + { + my $version = _parse_version_safely( $file ); + + my $module_name = _path_to_module( $inc, $file ); + next FILE unless defined $module_name; + + print $fh "$module_name\t$version\n"; + + #last if $count++ > 5; + } + } + + return HEY_IT_WORKED; + } + +sub _generator + { + my @files = (); + + sub { push @files, + File::Spec->canonpath( $File::Find::name ) + if m/\A\w+\.pm\z/ }, + sub { \@files }, + } + +sub _parse_version_safely # stolen from PAUSE's mldistwatch, but refactored + { + my( $file ) = @_; + + local $/ = "\n"; + local $_; # don't mess with the $_ in the map calling this + + return unless open FILE, "<$file"; + + my $in_pod = 0; + my $version; + while( <FILE> ) + { + chomp; + $in_pod = /^=(?!cut)/ ? 1 : /^=cut/ ? 0 : $in_pod; + next if $in_pod || /^\s*#/; + + next unless /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/; + my( $sigil, $var ) = ( $1, $2 ); + + $version = _eval_version( $_, $sigil, $var ); + last; + } + close FILE; + + return 'undef' unless defined $version; + + return $version; + } + +sub _eval_version + { + my( $line, $sigil, $var ) = @_; + + # split package line to hide from PAUSE + my $eval = qq{ + package + ExtUtils::MakeMaker::_version; + + local $sigil$var; + \$$var=undef; do { + $line + }; \$$var + }; + + my $version = do { + local $^W = 0; + no strict; + eval( $eval ); + }; + + return $version; + } + +sub _path_to_module + { + my( $inc, $path ) = @_; + return if length $path < length $inc; + + my $module_path = substr( $path, length $inc ); + $module_path =~ s/\.pm\z//; + + # XXX: this is cheating and doesn't handle everything right + my @dirs = grep { ! /\W/ } File::Spec->splitdir( $module_path ); + shift @dirs; + + my $module_name = join "::", @dirs; + + return $module_name; + } + + +sub _expand_module + { + my( $module ) = @_; + + my $expanded = CPAN::Shell->expandany( $module ); + return $expanded if $expanded; + $expanded = CPAN::Shell->expand( "Module", $module ); + unless( defined $expanded ) { + $logger->error( "Could not expand [$module]. Check the module name." ); + my $threshold = ( + grep { int } + sort { length $a <=> length $b } + length($module)/4, 4 + )[0]; + + my $guesses = _guess_at_module_name( $module, $threshold ); + if( defined $guesses and @$guesses ) { + $logger->info( "Perhaps you meant one of these:" ); + foreach my $guess ( @$guesses ) { + $logger->info( "\t$guess" ); + } + } + return; + } + + return $expanded; + } + +my $guessers = [ + [ qw( Text::Levenshtein::XS distance 7 1 ) ], + [ qw( Text::Levenshtein::Damerau::XS xs_edistance 7 1 ) ], + + [ qw( Text::Levenshtein distance 7 1 ) ], + [ qw( Text::Levenshtein::Damerau::PP pp_edistance 7 1 ) ], + + ]; + +sub _disable_guessers + { + $_->[-1] = 0 for @$guessers; + } + +# for -x +sub _guess_namespace + { + my $args = shift; + + foreach my $arg ( @$args ) + { + $logger->debug( "Checking $arg" ); + my $guesses = _guess_at_module_name( $arg ); + + foreach my $guess ( @$guesses ) { + print $guess, "\n"; + } + } + + return HEY_IT_WORKED; + } + +sub _list_all_namespaces { + my $modules = _get_all_namespaces(); + + foreach my $module ( @$modules ) { + print $module, "\n"; + } + } + +BEGIN { +my $distance; +my $_threshold; +my $can_guess; +my $shown_help = 0; +sub _guess_at_module_name + { + my( $target, $threshold ) = @_; + + unless( defined $distance ) { + foreach my $try ( @$guessers ) { + $can_guess = eval "require $try->[0]; 1" or next; + + $try->[-1] or next; # disabled + no strict 'refs'; + $distance = \&{ join "::", @$try[0,1] }; + $threshold ||= $try->[2]; + } + } + $_threshold ||= $threshold; + + unless( $distance ) { + unless( $shown_help ) { + my $modules = join ", ", map { $_->[0] } @$guessers; + substr $modules, rindex( $modules, ',' ), 1, ', and'; + + # Should this be colorized? + if( $can_guess ) { + $logger->info( "I can suggest names if you provide the -x option on invocation." ); + } + else { + $logger->info( "I can suggest names if you install one of $modules" ); + $logger->info( "and you provide the -x option on invocation." ); + } + $shown_help++; + } + return; + } + + my $modules = _get_all_namespaces(); + $logger->info( "Checking " . @$modules . " namespaces for close match suggestions" ); + + my %guesses; + foreach my $guess ( @$modules ) { + my $distance = $distance->( $target, $guess ); + next if $distance > $_threshold; + $guesses{$guess} = $distance; + } + + my @guesses = sort { $guesses{$a} <=> $guesses{$b} } keys %guesses; + return [ grep { defined } @guesses[0..9] ]; + } +} + +1; + +=back + +=head1 EXIT VALUES + +The script exits with zero if it thinks that everything worked, or a +positive number if it thinks that something failed. Note, however, that +in some cases it has to divine a failure by the output of things it does +not control. For now, the exit codes are vague: + + 1 An unknown error + + 2 The was an external problem + + 4 There was an internal problem with the script + + 8 A module failed to install + +=head1 TO DO + +* There is initial support for Log4perl if it is available, but I +haven't gone through everything to make the NullLogger work out +correctly if Log4perl is not installed. + +* When I capture CPAN.pm output, I need to check for errors and +report them to the user. + +* Warnings switch + +* Check then exit + +=head1 BUGS + +* none noted + +=head1 SEE ALSO + +L<CPAN>, L<App::cpanminus> + +=head1 SOURCE AVAILABILITY + +This code is in Github in the CPAN.pm repository: + + https://github.com/andk/cpanpm + +The source used to be tracked separately in another GitHub repo, +but the canonical source is now in the above repo. + +=head1 CREDITS + +Japheth Cleaver added the bits to allow a forced install (C<-f>). + +Jim Brandt suggest and provided the initial implementation for the +up-to-date and Changes features. + +Adam Kennedy pointed out that C<exit()> causes problems on Windows +where this script ends up with a .bat extension + +David Golden helps integrate this into the C<CPAN.pm> repos. + +Jim Keenan fixed up various issues with _download + +=head1 AUTHOR + +brian d foy, C<< <bdfoy@cpan.org> >> + +=head1 COPYRIGHT + +Copyright (c) 2001-2018, brian d foy, All Rights Reserved. + +You may redistribute this under the same terms as Perl itself. + +=cut + +# Local Variables: +# mode: cperl +# indent-tabs-mode: t +# cperl-indent-level: 8 +# cperl-continued-statement-offset: 8 +# End: diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN.pm b/tools/msys/usr/share/perl5/site_perl/CPAN.pm new file mode 100644 index 0000000000..c93d98e8db --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN.pm @@ -0,0 +1,4093 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +use strict; +package CPAN; +$CPAN::VERSION = '2.28'; +$CPAN::VERSION =~ s/_//; + +# we need to run chdir all over and we would get at wrong libraries +# there +use File::Spec (); +BEGIN { + if (File::Spec->can("rel2abs")) { + for my $inc (@INC) { + $inc = File::Spec->rel2abs($inc) unless ref $inc; + } + } + $SIG{WINCH} = 'IGNORE' if exists $SIG{WINCH}; +} +use CPAN::Author; +use CPAN::HandleConfig; +use CPAN::Version; +use CPAN::Bundle; +use CPAN::CacheMgr; +use CPAN::Complete; +use CPAN::Debug; +use CPAN::Distribution; +use CPAN::Distrostatus; +use CPAN::FTP; +use CPAN::Index 1.93; # https://rt.cpan.org/Ticket/Display.html?id=43349 +use CPAN::InfoObj; +use CPAN::Module; +use CPAN::Prompt; +use CPAN::URL; +use CPAN::Queue; +use CPAN::Tarzip; +use CPAN::DeferredCode; +use CPAN::Shell; +use CPAN::LWP::UserAgent; +use CPAN::Exception::RecursiveDependency; +use CPAN::Exception::yaml_not_installed; +use CPAN::Exception::yaml_process_error; + +use Carp (); +use Config (); +use Cwd qw(chdir); +use DirHandle (); +use Exporter (); +use ExtUtils::MakeMaker qw(prompt); # for some unknown reason, + # 5.005_04 does not work without + # this +use File::Basename (); +use File::Copy (); +use File::Find; +use File::Path (); +use FileHandle (); +use Fcntl qw(:flock); +use Safe (); +use Sys::Hostname qw(hostname); +use Text::ParseWords (); +use Text::Wrap (); + +# protect against "called too early" +sub find_perl (); +sub anycwd (); +sub _uniq; + +no lib "."; + +require Mac::BuildTools if $^O eq 'MacOS'; +if ($ENV{PERL5_CPAN_IS_RUNNING} && $$ != $ENV{PERL5_CPAN_IS_RUNNING}) { + $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION} ||= $ENV{PERL5_CPAN_IS_RUNNING}; + my @rec = _uniq split(/,/, $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION}), $$; + $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION} = join ",", @rec; + # warn "# Note: Recursive call of CPAN.pm detected\n"; + my $w = sprintf "# Note: CPAN.pm is running in process %d now", pop @rec; + my %sleep = ( + 5 => 30, + 6 => 60, + 7 => 120, + ); + my $sleep = @rec > 7 ? 300 : ($sleep{scalar @rec}||0); + my $verbose = @rec >= 4; + while (@rec) { + $w .= sprintf " which has been called by process %d", pop @rec; + } + if ($sleep) { + $w .= ".\n\n# Sleeping $sleep seconds to protect other processes\n"; + } + if ($verbose) { + warn $w; + } + local $| = 1; + my $have_been_sleeping = 0; + while ($sleep > 0) { + printf "\r#%5d", --$sleep; + sleep 1; + ++$have_been_sleeping; + } + print "\n" if $have_been_sleeping; +} +$ENV{PERL5_CPAN_IS_RUNNING}=$$; +$ENV{PERL5_CPANPLUS_IS_RUNNING}=$$; # https://rt.cpan.org/Ticket/Display.html?id=23735 + +END { $CPAN::End++; &cleanup; } + +$CPAN::Signal ||= 0; +$CPAN::Frontend ||= "CPAN::Shell"; +unless (@CPAN::Defaultsites) { + @CPAN::Defaultsites = map { + CPAN::URL->new(TEXT => $_, FROM => "DEF") + } + "http://www.perl.org/CPAN/", + "ftp://ftp.perl.org/pub/CPAN/"; +} +# $CPAN::iCwd (i for initial) +$CPAN::iCwd ||= CPAN::anycwd(); +$CPAN::Perl ||= CPAN::find_perl(); +$CPAN::Defaultdocs ||= "http://search.cpan.org/perldoc?"; +$CPAN::Defaultrecent ||= "http://search.cpan.org/uploads.rdf"; +$CPAN::Defaultrecent ||= "http://cpan.uwinnipeg.ca/htdocs/cpan.xml"; + +# our globals are getting a mess +use vars qw( + $AUTOLOAD + $Be_Silent + $CONFIG_DIRTY + $Defaultdocs + $Echo_readline + $Frontend + $GOTOSHELL + $HAS_USABLE + $Have_warned + $MAX_RECURSION + $META + $RUN_DEGRADED + $Signal + $SQLite + $Suppress_readline + $VERSION + $autoload_recursion + $term + @Defaultsites + @EXPORT + ); + +$MAX_RECURSION = 32; + +@CPAN::ISA = qw(CPAN::Debug Exporter); + +# note that these functions live in CPAN::Shell and get executed via +# AUTOLOAD when called directly +@EXPORT = qw( + autobundle + bundle + clean + cvs_import + expand + force + fforce + get + install + install_tested + is_tested + make + mkmyconfig + notest + perldoc + readme + recent + recompile + report + shell + smoke + test + upgrade + ); + +sub soft_chdir_with_alternatives ($); + +{ + $autoload_recursion ||= 0; + + #-> sub CPAN::AUTOLOAD ; + sub AUTOLOAD { ## no critic + $autoload_recursion++; + my($l) = $AUTOLOAD; + $l =~ s/.*:://; + if ($CPAN::Signal) { + warn "Refusing to autoload '$l' while signal pending"; + $autoload_recursion--; + return; + } + if ($autoload_recursion > 1) { + my $fullcommand = join " ", map { "'$_'" } $l, @_; + warn "Refusing to autoload $fullcommand in recursion\n"; + $autoload_recursion--; + return; + } + my(%export); + @export{@EXPORT} = ''; + CPAN::HandleConfig->load unless $CPAN::Config_loaded++; + if (exists $export{$l}) { + CPAN::Shell->$l(@_); + } else { + die(qq{Unknown CPAN command "$AUTOLOAD". }. + qq{Type ? for help.\n}); + } + $autoload_recursion--; + } +} + +{ + my $x = *SAVEOUT; # avoid warning + open($x,">&STDOUT") or die "dup failed"; + my $redir = 0; + sub _redirect(@) { + #die if $redir; + local $_; + push(@_,undef); + while(defined($_=shift)) { + if (s/^\s*>//){ + my ($m) = s/^>// ? ">" : ""; + s/\s+//; + $_=shift unless length; + die "no dest" unless defined; + open(STDOUT,">$m$_") or die "open:$_:$!\n"; + $redir=1; + } elsif ( s/^\s*\|\s*// ) { + my $pipe="| $_"; + while(defined($_[0])){ + $pipe .= ' ' . shift; + } + open(STDOUT,$pipe) or die "open:$pipe:$!\n"; + $redir=1; + } else { + push(@_,$_); + } + } + return @_; + } + sub _unredirect { + return unless $redir; + $redir = 0; + ## redirect: unredirect and propagate errors. explicit close to wait for pipe. + close(STDOUT); + open(STDOUT,">&SAVEOUT"); + die "$@" if "$@"; + ## redirect: done + } +} + +sub _uniq { + my(@list) = @_; + my %seen; + return grep { !$seen{$_}++ } @list; +} + +#-> sub CPAN::shell ; +sub shell { + my($self) = @_; + $Suppress_readline = ! -t STDIN unless defined $Suppress_readline; + CPAN::HandleConfig->load unless $CPAN::Config_loaded++; + + my $oprompt = shift || CPAN::Prompt->new; + my $prompt = $oprompt; + my $commandline = shift || ""; + $CPAN::CurrentCommandId ||= 1; + + local($^W) = 1; + unless ($Suppress_readline) { + require Term::ReadLine; + if (! $term + or + $term->ReadLine eq "Term::ReadLine::Stub" + ) { + $term = Term::ReadLine->new('CPAN Monitor'); + } + if ($term->ReadLine eq "Term::ReadLine::Gnu") { + my $attribs = $term->Attribs; + $attribs->{attempted_completion_function} = sub { + &CPAN::Complete::gnu_cpl; + } + } else { + $readline::rl_completion_function = + $readline::rl_completion_function = 'CPAN::Complete::cpl'; + } + if (my $histfile = $CPAN::Config->{'histfile'}) {{ + unless ($term->can("AddHistory")) { + $CPAN::Frontend->mywarn("Terminal does not support AddHistory.\n"); + unless ($CPAN::META->has_inst('Term::ReadLine::Perl')) { + $CPAN::Frontend->mywarn("\nTo fix that, maybe try> install Term::ReadLine::Perl\n\n"); + } + last; + } + $META->readhist($term,$histfile); + }} + for ($CPAN::Config->{term_ornaments}) { # alias + local $Term::ReadLine::termcap_nowarn = 1; + $term->ornaments($_) if defined; + } + # $term->OUT is autoflushed anyway + my $odef = select STDERR; + $| = 1; + select STDOUT; + $| = 1; + select $odef; + } + + $META->checklock(); + my @cwd = grep { defined $_ and length $_ } + CPAN::anycwd(), + File::Spec->can("tmpdir") ? File::Spec->tmpdir() : (), + File::Spec->rootdir(); + my $try_detect_readline; + $try_detect_readline = $term->ReadLine eq "Term::ReadLine::Stub" if $term; + unless ($CPAN::Config->{inhibit_startup_message}) { + my $rl_avail = $Suppress_readline ? "suppressed" : + ($term->ReadLine ne "Term::ReadLine::Stub") ? "enabled" : + "available (maybe install Bundle::CPAN or Bundle::CPANxxl?)"; + $CPAN::Frontend->myprint( + sprintf qq{ +cpan shell -- CPAN exploration and modules installation (v%s) +Enter 'h' for help. + +}, + $CPAN::VERSION, + ) + } + my($continuation) = ""; + my $last_term_ornaments; + SHELLCOMMAND: while () { + if ($Suppress_readline) { + if ($Echo_readline) { + $|=1; + } + print $prompt; + last SHELLCOMMAND unless defined ($_ = <> ); + if ($Echo_readline) { + # backdoor: I could not find a way to record sessions + print $_; + } + chomp; + } else { + last SHELLCOMMAND unless + defined ($_ = $term->readline($prompt, $commandline)); + } + $_ = "$continuation$_" if $continuation; + s/^\s+//; + next SHELLCOMMAND if /^$/; + s/^\s*\?\s*/help /; + if (/^(?:q(?:uit)?|bye|exit)\s*$/i) { + last SHELLCOMMAND; + } elsif (s/\\$//s) { + chomp; + $continuation = $_; + $prompt = " > "; + } elsif (/^\!/) { + s/^\!//; + my($eval) = $_; + package + CPAN::Eval; # hide from the indexer + use strict; + use vars qw($import_done); + CPAN->import(':DEFAULT') unless $import_done++; + CPAN->debug("eval[$eval]") if $CPAN::DEBUG; + eval($eval); + warn $@ if $@; + $continuation = ""; + $prompt = $oprompt; + } elsif (/./) { + my(@line); + eval { @line = Text::ParseWords::shellwords($_) }; + warn($@), next SHELLCOMMAND if $@; + warn("Text::Parsewords could not parse the line [$_]"), + next SHELLCOMMAND unless @line; + $CPAN::META->debug("line[".join("|",@line)."]") if $CPAN::DEBUG; + my $command = shift @line; + eval { + local (*STDOUT)=*STDOUT; + @line = _redirect(@line); + CPAN::Shell->$command(@line) + }; + my $command_error = $@; + _unredirect; + my $reported_error; + if ($command_error) { + my $err = $command_error; + if (ref $err and $err->isa('CPAN::Exception::blocked_urllist')) { + $CPAN::Frontend->mywarn("Client not fully configured, please proceed with configuring.$err"); + $reported_error = ref $err; + } else { + # I'd prefer never to arrive here and make all errors exception objects + if ($err =~ /\S/) { + require Carp; + require Dumpvalue; + my $dv = Dumpvalue->new(tick => '"'); + Carp::cluck(sprintf "Catching error: %s", $dv->stringify($err)); + } + } + } + if ($command =~ /^( + # classic commands + make + |test + |install + |clean + + # pragmas for classic commands + |ff?orce + |notest + + # compounds + |report + |smoke + |upgrade + )$/x) { + # only commands that tell us something about failed distros + # eval necessary for people without an urllist + eval {CPAN::Shell->failed($CPAN::CurrentCommandId,1);}; + if (my $err = $@) { + unless (ref $err and $reported_error eq ref $err) { + die $@; + } + } + } + soft_chdir_with_alternatives(\@cwd); + $CPAN::Frontend->myprint("\n"); + $continuation = ""; + $CPAN::CurrentCommandId++; + $prompt = $oprompt; + } + } continue { + $commandline = ""; # I do want to be able to pass a default to + # shell, but on the second command I see no + # use in that + $Signal=0; + CPAN::Queue->nullify_queue; + if ($try_detect_readline) { + if ($CPAN::META->has_inst("Term::ReadLine::Gnu") + || + $CPAN::META->has_inst("Term::ReadLine::Perl") + ) { + delete $INC{"Term/ReadLine.pm"}; + my $redef = 0; + local($SIG{__WARN__}) = CPAN::Shell::paintdots_onreload(\$redef); + require Term::ReadLine; + $CPAN::Frontend->myprint("\n$redef subroutines in ". + "Term::ReadLine redefined\n"); + $GOTOSHELL = 1; + } + } + if ($term and $term->can("ornaments")) { + for ($CPAN::Config->{term_ornaments}) { # alias + if (defined $_) { + if (not defined $last_term_ornaments + or $_ != $last_term_ornaments + ) { + local $Term::ReadLine::termcap_nowarn = 1; + $term->ornaments($_); + $last_term_ornaments = $_; + } + } else { + undef $last_term_ornaments; + } + } + } + for my $class (qw(Module Distribution)) { + # again unsafe meta access? + for my $dm (sort keys %{$CPAN::META->{readwrite}{"CPAN::$class"}}) { + next unless $CPAN::META->{readwrite}{"CPAN::$class"}{$dm}{incommandcolor}; + CPAN->debug("BUG: $class '$dm' was in command state, resetting"); + delete $CPAN::META->{readwrite}{"CPAN::$class"}{$dm}{incommandcolor}; + } + } + if ($GOTOSHELL) { + $GOTOSHELL = 0; # not too often + $META->savehist if $CPAN::term && $CPAN::term->can("GetHistory"); + @_ = ($oprompt,""); + goto &shell; + } + } + soft_chdir_with_alternatives(\@cwd); +} + +#-> CPAN::soft_chdir_with_alternatives ; +sub soft_chdir_with_alternatives ($) { + my($cwd) = @_; + unless (@$cwd) { + my $root = File::Spec->rootdir(); + $CPAN::Frontend->mywarn(qq{Warning: no good directory to chdir to! +Trying '$root' as temporary haven. +}); + push @$cwd, $root; + } + while () { + if (chdir "$cwd->[0]") { + return; + } else { + if (@$cwd>1) { + $CPAN::Frontend->mywarn(qq{Could not chdir to "$cwd->[0]": $! +Trying to chdir to "$cwd->[1]" instead. +}); + shift @$cwd; + } else { + $CPAN::Frontend->mydie(qq{Could not chdir to "$cwd->[0]": $!}); + } + } + } +} + +sub _flock { + my($fh,$mode) = @_; + if ( $Config::Config{d_flock} || $Config::Config{d_fcntl_can_lock} ) { + return flock $fh, $mode; + } elsif (!$Have_warned->{"d_flock"}++) { + $CPAN::Frontend->mywarn("Your OS does not seem to support locking; continuing and ignoring all locking issues\n"); + $CPAN::Frontend->mysleep(5); + return 1; + } else { + return 1; + } +} + +sub _yaml_module () { + my $yaml_module = $CPAN::Config->{yaml_module} || "YAML"; + if ( + $yaml_module ne "YAML" + && + !$CPAN::META->has_inst($yaml_module) + ) { + # $CPAN::Frontend->mywarn("'$yaml_module' not installed, falling back to 'YAML'\n"); + $yaml_module = "YAML"; + } + if ($yaml_module eq "YAML" + && + $CPAN::META->has_inst($yaml_module) + && + $YAML::VERSION < 0.60 + && + !$Have_warned->{"YAML"}++ + ) { + $CPAN::Frontend->mywarn("Warning: YAML version '$YAML::VERSION' is too low, please upgrade!\n". + "I'll continue but problems are *very* likely to happen.\n" + ); + $CPAN::Frontend->mysleep(5); + } + return $yaml_module; +} + +# CPAN::_yaml_loadfile +sub _yaml_loadfile { + my($self,$local_file) = @_; + return +[] unless -s $local_file; + my $yaml_module = _yaml_module; + if ($CPAN::META->has_inst($yaml_module)) { + # temporarily enable yaml code deserialisation + no strict 'refs'; + # 5.6.2 could not do the local() with the reference + # so we do it manually instead + my $old_loadcode = ${"$yaml_module\::LoadCode"}; + my $old_loadblessed = ${"$yaml_module\::LoadBlessed"}; + ${ "$yaml_module\::LoadCode" } = $CPAN::Config->{yaml_load_code} || 0; + ${ "$yaml_module\::LoadBlessed" } = 1; + + my ($code, @yaml); + if ($code = UNIVERSAL::can($yaml_module, "LoadFile")) { + eval { @yaml = $code->($local_file); }; + if ($@) { + # this shall not be done by the frontend + die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"parse",$@); + } + } elsif ($code = UNIVERSAL::can($yaml_module, "Load")) { + local *FH; + if (open FH, $local_file) { + local $/; + my $ystream = <FH>; + eval { @yaml = $code->($ystream); }; + if ($@) { + # this shall not be done by the frontend + die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"parse",$@); + } + } else { + $CPAN::Frontend->mywarn("Could not open '$local_file': $!"); + } + } + ${"$yaml_module\::LoadCode"} = $old_loadcode; + ${"$yaml_module\::LoadBlessed"} = $old_loadblessed; + return \@yaml; + } else { + # this shall not be done by the frontend + die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "parse"); + } + return +[]; +} + +# CPAN::_yaml_dumpfile +sub _yaml_dumpfile { + my($self,$local_file,@what) = @_; + my $yaml_module = _yaml_module; + if ($CPAN::META->has_inst($yaml_module)) { + my $code; + if (UNIVERSAL::isa($local_file, "FileHandle")) { + $code = UNIVERSAL::can($yaml_module, "Dump"); + eval { print $local_file $code->(@what) }; + } elsif ($code = UNIVERSAL::can($yaml_module, "DumpFile")) { + eval { $code->($local_file,@what); }; + } elsif ($code = UNIVERSAL::can($yaml_module, "Dump")) { + local *FH; + open FH, ">$local_file" or die "Could not open '$local_file': $!"; + print FH $code->(@what); + } + if ($@) { + die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"dump",$@); + } + } else { + if (UNIVERSAL::isa($local_file, "FileHandle")) { + # I think this case does not justify a warning at all + } else { + die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "dump"); + } + } +} + +sub _init_sqlite () { + unless ($CPAN::META->has_inst("CPAN::SQLite")) { + $CPAN::Frontend->mywarn(qq{CPAN::SQLite not installed, trying to work without\n}) + unless $Have_warned->{"CPAN::SQLite"}++; + return; + } + require CPAN::SQLite::META; # not needed since CVS version of 2006-12-17 + $CPAN::SQLite ||= CPAN::SQLite::META->new($CPAN::META); +} + +{ + my $negative_cache = {}; + sub _sqlite_running { + if ($negative_cache->{time} && time < $negative_cache->{time} + 60) { + # need to cache the result, otherwise too slow + return $negative_cache->{fact}; + } else { + $negative_cache = {}; # reset + } + my $ret = $CPAN::Config->{use_sqlite} && ($CPAN::SQLite || _init_sqlite()); + return $ret if $ret; # fast anyway + $negative_cache->{time} = time; + return $negative_cache->{fact} = $ret; + } +} + +$META ||= CPAN->new; # In case we re-eval ourselves we need the || + +# from here on only subs. +################################################################################ + +sub _perl_fingerprint { + my($self,$other_fingerprint) = @_; + my $dll = eval {OS2::DLLname()}; + my $mtime_dll = 0; + if (defined $dll) { + $mtime_dll = (-f $dll ? (stat(_))[9] : '-1'); + } + my $mtime_perl = (-f CPAN::find_perl ? (stat(_))[9] : '-1'); + my $this_fingerprint = { + '$^X' => CPAN::find_perl, + sitearchexp => $Config::Config{sitearchexp}, + 'mtime_$^X' => $mtime_perl, + 'mtime_dll' => $mtime_dll, + }; + if ($other_fingerprint) { + if (exists $other_fingerprint->{'stat($^X)'}) { # repair fp from rev. 1.88_57 + $other_fingerprint->{'mtime_$^X'} = $other_fingerprint->{'stat($^X)'}[9]; + } + # mandatory keys since 1.88_57 + for my $key (qw($^X sitearchexp mtime_dll mtime_$^X)) { + return unless $other_fingerprint->{$key} eq $this_fingerprint->{$key}; + } + return 1; + } else { + return $this_fingerprint; + } +} + +sub suggest_myconfig () { + SUGGEST_MYCONFIG: if(!$INC{'CPAN/MyConfig.pm'}) { + $CPAN::Frontend->myprint("You don't seem to have a user ". + "configuration (MyConfig.pm) yet.\n"); + my $new = CPAN::Shell::colorable_makemaker_prompt("Do you want to create a ". + "user configuration now? (Y/n)", + "yes"); + if($new =~ m{^y}i) { + CPAN::Shell->mkmyconfig(); + return &checklock; + } else { + $CPAN::Frontend->mydie("OK, giving up."); + } + } +} + +#-> sub CPAN::all_objects ; +sub all_objects { + my($mgr,$class) = @_; + CPAN::HandleConfig->load unless $CPAN::Config_loaded++; + CPAN->debug("mgr[$mgr] class[$class]") if $CPAN::DEBUG; + CPAN::Index->reload; + values %{ $META->{readwrite}{$class} }; # unsafe meta access, ok +} + +# Called by shell, not in batch mode. In batch mode I see no risk in +# having many processes updating something as installations are +# continually checked at runtime. In shell mode I suspect it is +# unintentional to open more than one shell at a time + +#-> sub CPAN::checklock ; +sub checklock { + my($self) = @_; + my $lockfile = File::Spec->catfile($CPAN::Config->{cpan_home},".lock"); + if (-f $lockfile && -M _ > 0) { + my $fh = FileHandle->new($lockfile) or + $CPAN::Frontend->mydie("Could not open lockfile '$lockfile': $!"); + my $otherpid = <$fh>; + my $otherhost = <$fh>; + $fh->close; + if (defined $otherpid && length $otherpid) { + chomp $otherpid; + } + if (defined $otherhost && length $otherhost) { + chomp $otherhost; + } + my $thishost = hostname(); + my $ask_if_degraded_wanted = 0; + if (defined $otherhost && defined $thishost && + $otherhost ne '' && $thishost ne '' && + $otherhost ne $thishost) { + $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile '$lockfile'\n". + "reports other host $otherhost and other ". + "process $otherpid.\n". + "Cannot proceed.\n")); + } elsif ($RUN_DEGRADED) { + $CPAN::Frontend->mywarn("Running in downgraded mode (experimental)\n"); + } elsif (defined $otherpid && $otherpid) { + return if $$ == $otherpid; # should never happen + $CPAN::Frontend->mywarn( + qq{ +There seems to be running another CPAN process (pid $otherpid). Contacting... +}); + if (kill 0, $otherpid or $!{EPERM}) { + $CPAN::Frontend->mywarn(qq{Other job is running.\n}); + $ask_if_degraded_wanted = 1; + } elsif (-w $lockfile) { + my($ans) = + CPAN::Shell::colorable_makemaker_prompt + (qq{Other job not responding. Shall I overwrite }. + qq{the lockfile '$lockfile'? (Y/n)},"y"); + $CPAN::Frontend->myexit("Ok, bye\n") + unless $ans =~ /^y/i; + } else { + Carp::croak( + qq{Lockfile '$lockfile' not writable by you. }. + qq{Cannot proceed.\n}. + qq{ On UNIX try:\n}. + qq{ rm '$lockfile'\n}. + qq{ and then rerun us.\n} + ); + } + } elsif ($^O eq "MSWin32") { + $CPAN::Frontend->mywarn( + qq{ +There seems to be running another CPAN process according to '$lockfile'. +}); + $ask_if_degraded_wanted = 1; + } else { + $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Found invalid lockfile ". + "'$lockfile', please remove. Cannot proceed.\n")); + } + if ($ask_if_degraded_wanted) { + my($ans) = + CPAN::Shell::colorable_makemaker_prompt + (qq{Shall I try to run in downgraded }. + qq{mode? (Y/n)},"y"); + if ($ans =~ /^y/i) { + $CPAN::Frontend->mywarn("Running in downgraded mode (experimental). +Please report if something unexpected happens\n"); + $RUN_DEGRADED = 1; + for ($CPAN::Config) { + # XXX + # $_->{build_dir_reuse} = 0; # 2006-11-17 akoenig Why was that? + $_->{commandnumber_in_prompt} = 0; # visibility + $_->{histfile} = ""; # who should win otherwise? + $_->{cache_metadata} = 0; # better would be a lock? + $_->{use_sqlite} = 0; # better would be a write lock! + $_->{auto_commit} = 0; # we are violent, do not persist + $_->{test_report} = 0; # Oliver Paukstadt had sent wrong reports in degraded mode + } + } else { + my $msg = "You may want to kill the other job and delete the lockfile."; + if (defined $otherpid) { + $msg .= " Something like: + kill $otherpid + rm $lockfile +"; + } + $CPAN::Frontend->mydie("\n$msg"); + } + } + } + my $dotcpan = $CPAN::Config->{cpan_home}; + eval { File::Path::mkpath($dotcpan);}; + if ($@) { + # A special case at least for Jarkko. + my $firsterror = $@; + my $seconderror; + my $symlinkcpan; + if (-l $dotcpan) { + $symlinkcpan = readlink $dotcpan; + die "readlink $dotcpan failed: $!" unless defined $symlinkcpan; + eval { File::Path::mkpath($symlinkcpan); }; + if ($@) { + $seconderror = $@; + } else { + $CPAN::Frontend->mywarn(qq{ +Working directory $symlinkcpan created. +}); + } + } + unless (-d $dotcpan) { + my $mess = qq{ +Your configuration suggests "$dotcpan" as your +CPAN.pm working directory. I could not create this directory due +to this error: $firsterror\n}; + $mess .= qq{ +As "$dotcpan" is a symlink to "$symlinkcpan", +I tried to create that, but I failed with this error: $seconderror +} if $seconderror; + $mess .= qq{ +Please make sure the directory exists and is writable. +}; + $CPAN::Frontend->mywarn($mess); + return suggest_myconfig; + } + } # $@ after eval mkpath $dotcpan + if (0) { # to test what happens when a race condition occurs + for (reverse 1..10) { + print $_, "\n"; + sleep 1; + } + } + # locking + if (!$RUN_DEGRADED && !$self->{LOCKFH}) { + my $fh; + unless ($fh = FileHandle->new("+>>$lockfile")) { + $CPAN::Frontend->mywarn(qq{ + +Your configuration suggests that CPAN.pm should use a working +directory of + $CPAN::Config->{cpan_home} +Unfortunately we could not create the lock file + $lockfile +due to '$!'. + +Please make sure that the configuration variable + \$CPAN::Config->{cpan_home} +points to a directory where you can write a .lock file. You can set +this variable in either a CPAN/MyConfig.pm or a CPAN/Config.pm in your +\@INC path; +}); + return suggest_myconfig; + } + my $sleep = 1; + while (!CPAN::_flock($fh, LOCK_EX|LOCK_NB)) { + my $err = $! || "unknown error"; + if ($sleep>3) { + $CPAN::Frontend->mydie("Could not lock '$lockfile' with flock: $err; giving up\n"); + } + $CPAN::Frontend->mysleep($sleep+=0.1); + $CPAN::Frontend->mywarn("Could not lock '$lockfile' with flock: $err; retrying\n"); + } + + seek $fh, 0, 0; + truncate $fh, 0; + $fh->autoflush(1); + $fh->print($$, "\n"); + $fh->print(hostname(), "\n"); + $self->{LOCK} = $lockfile; + $self->{LOCKFH} = $fh; + } + $SIG{TERM} = sub { + my $sig = shift; + &cleanup; + $CPAN::Frontend->mydie("Got SIG$sig, leaving"); + }; + $SIG{INT} = sub { + # no blocks!!! + my $sig = shift; + &cleanup if $Signal; + die "Got yet another signal" if $Signal > 1; + $CPAN::Frontend->mydie("Got another SIG$sig") if $Signal; + $CPAN::Frontend->mywarn("Caught SIG$sig, trying to continue\n"); + $Signal++; + }; + +# From: Larry Wall <larry@wall.org> +# Subject: Re: deprecating SIGDIE +# To: perl5-porters@perl.org +# Date: Thu, 30 Sep 1999 14:58:40 -0700 (PDT) +# +# The original intent of __DIE__ was only to allow you to substitute one +# kind of death for another on an application-wide basis without respect +# to whether you were in an eval or not. As a global backstop, it should +# not be used any more lightly (or any more heavily :-) than class +# UNIVERSAL. Any attempt to build a general exception model on it should +# be politely squashed. Any bug that causes every eval {} to have to be +# modified should be not so politely squashed. +# +# Those are my current opinions. It is also my opinion that polite +# arguments degenerate to personal arguments far too frequently, and that +# when they do, it's because both people wanted it to, or at least didn't +# sufficiently want it not to. +# +# Larry + + # global backstop to cleanup if we should really die + $SIG{__DIE__} = \&cleanup; + $self->debug("Signal handler set.") if $CPAN::DEBUG; +} + +#-> sub CPAN::DESTROY ; +sub DESTROY { + &cleanup; # need an eval? +} + +#-> sub CPAN::anycwd ; +sub anycwd () { + my $getcwd; + $getcwd = $CPAN::Config->{'getcwd'} || 'cwd'; + CPAN->$getcwd(); +} + +#-> sub CPAN::cwd ; +sub cwd {Cwd::cwd();} + +#-> sub CPAN::getcwd ; +sub getcwd {Cwd::getcwd();} + +#-> sub CPAN::fastcwd ; +sub fastcwd {Cwd::fastcwd();} + +#-> sub CPAN::getdcwd ; +sub getdcwd {Cwd::getdcwd();} + +#-> sub CPAN::backtickcwd ; +sub backtickcwd {my $cwd = `cwd`; chomp $cwd; $cwd} + +# Adapted from Probe::Perl +#-> sub CPAN::_perl_is_same +sub _perl_is_same { + my ($perl) = @_; + return MM->maybe_command($perl) + && `$perl -MConfig=myconfig -e print -e myconfig` eq Config->myconfig; +} + +# Adapted in part from Probe::Perl +#-> sub CPAN::find_perl ; +sub find_perl () { + if ( File::Spec->file_name_is_absolute($^X) ) { + return $^X; + } + else { + my $exe = $Config::Config{exe_ext}; + my @candidates = ( + File::Spec->catfile($CPAN::iCwd,$^X), + $Config::Config{'perlpath'}, + ); + for my $perl_name ($^X, 'perl', 'perl5', "perl$]") { + for my $path (File::Spec->path(), $Config::Config{'binexp'}) { + if ( defined($path) && length $path && -d $path ) { + my $perl = File::Spec->catfile($path,$perl_name); + push @candidates, $perl; + # try with extension if not provided already + if ($^O eq 'VMS') { + # VMS might have a file version at the end + push @candidates, $perl . $exe + unless $perl =~ m/$exe(;\d+)?$/i; + } elsif (defined $exe && length $exe) { + push @candidates, $perl . $exe + unless $perl =~ m/$exe$/i; + } + } + } + } + for my $perl ( @candidates ) { + if (MM->maybe_command($perl) && _perl_is_same($perl)) { + $^X = $perl; + return $perl; + } + } + } + return $^X; # default fall back +} + +#-> sub CPAN::exists ; +sub exists { + my($mgr,$class,$id) = @_; + CPAN::HandleConfig->load unless $CPAN::Config_loaded++; + CPAN::Index->reload; + ### Carp::croak "exists called without class argument" unless $class; + $id ||= ""; + $id =~ s/:+/::/g if $class eq "CPAN::Module"; + my $exists; + if (CPAN::_sqlite_running) { + $exists = (exists $META->{readonly}{$class}{$id} or + $CPAN::SQLite->set($class, $id)); + } else { + $exists = exists $META->{readonly}{$class}{$id}; + } + $exists ||= exists $META->{readwrite}{$class}{$id}; # unsafe meta access, ok +} + +#-> sub CPAN::delete ; +sub delete { + my($mgr,$class,$id) = @_; + delete $META->{readonly}{$class}{$id}; # unsafe meta access, ok + delete $META->{readwrite}{$class}{$id}; # unsafe meta access, ok +} + +#-> sub CPAN::has_usable +# has_inst is sometimes too optimistic, we should replace it with this +# has_usable whenever a case is given +sub has_usable { + my($self,$mod,$message) = @_; + return 1 if $HAS_USABLE->{$mod}; + my $has_inst = $self->has_inst($mod,$message); + return unless $has_inst; + my $usable; + $usable = { + + # + # most of these subroutines warn on the frontend, then + # die if the installed version is unusable for some + # reason; has_usable() then returns false when it caught + # an exception, otherwise returns true and caches that; + # + 'CPAN::Meta' => [ + sub { + require CPAN::Meta; + unless (CPAN::Version->vge(CPAN::Meta->VERSION, 2.110350)) { + for ("Will not use CPAN::Meta, need version 2.110350\n") { + $CPAN::Frontend->mywarn($_); + die $_; + } + } + }, + ], + + 'CPAN::Meta::Requirements' => [ + sub { + if (defined $CPAN::Meta::Requirements::VERSION + && CPAN::Version->vlt($CPAN::Meta::Requirements::VERSION, "2.120920") + ) { + delete $INC{"CPAN/Meta/Requirements.pm"}; + } + require CPAN::Meta::Requirements; + unless (CPAN::Version->vge(CPAN::Meta::Requirements->VERSION, 2.120920)) { + for ("Will not use CPAN::Meta::Requirements, need version 2.120920\n") { + $CPAN::Frontend->mywarn($_); + die $_; + } + } + }, + ], + + 'CPAN::Reporter' => [ + sub { + if (defined $CPAN::Reporter::VERSION + && CPAN::Version->vlt($CPAN::Reporter::VERSION, "1.2011") + ) { + delete $INC{"CPAN/Reporter.pm"}; + } + require CPAN::Reporter; + unless (CPAN::Version->vge(CPAN::Reporter->VERSION, "1.2011")) { + for ("Will not use CPAN::Reporter, need version 1.2011\n") { + $CPAN::Frontend->mywarn($_); + die $_; + } + } + }, + ], + + LWP => [ # we frequently had "Can't locate object + # method "new" via package "LWP::UserAgent" at + # (eval 69) line 2006 + sub {require LWP}, + sub {require LWP::UserAgent}, + sub {require HTTP::Request}, + sub {require URI::URL; + unless (CPAN::Version->vge(URI::URL::->VERSION,0.08)) { + for ("Will not use URI::URL, need 0.08\n") { + $CPAN::Frontend->mywarn($_); + die $_; + } + } + }, + ], + 'Net::FTP' => [ + sub { + my $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy}; + if ($var and $var =~ /^http:/i) { + # rt #110833 + for ("Net::FTP cannot handle http proxy") { + $CPAN::Frontend->mywarn($_); + die $_; + } + } + }, + sub {require Net::FTP}, + sub {require Net::Config}, + ], + 'HTTP::Tiny' => [ + sub { + require HTTP::Tiny; + unless (CPAN::Version->vge(HTTP::Tiny->VERSION, 0.005)) { + for ("Will not use HTTP::Tiny, need version 0.005\n") { + $CPAN::Frontend->mywarn($_); + die $_; + } + } + }, + ], + 'File::HomeDir' => [ + sub {require File::HomeDir; + unless (CPAN::Version->vge(File::HomeDir::->VERSION, 0.52)) { + for ("Will not use File::HomeDir, need 0.52\n") { + $CPAN::Frontend->mywarn($_); + die $_; + } + } + }, + ], + 'Archive::Tar' => [ + sub {require Archive::Tar; + my $demand = "1.50"; + unless (CPAN::Version->vge(Archive::Tar::->VERSION, $demand)) { + my $atv = Archive::Tar->VERSION; + for ("You have Archive::Tar $atv, but $demand or later is recommended. Please upgrade.\n") { + $CPAN::Frontend->mywarn($_); + # don't die, because we may need + # Archive::Tar to upgrade + } + + } + }, + ], + 'File::Temp' => [ + # XXX we should probably delete from + # %INC too so we can load after we + # installed a new enough version -- + # I'm not sure. + sub {require File::Temp; + unless (CPAN::Version->vge(File::Temp::->VERSION,0.16)) { + for ("Will not use File::Temp, need 0.16\n") { + $CPAN::Frontend->mywarn($_); + die $_; + } + } + }, + ] + }; + if ($usable->{$mod}) { + local @INC = @INC; + pop @INC if $INC[-1] eq '.'; + for my $c (0..$#{$usable->{$mod}}) { + my $code = $usable->{$mod}[$c]; + my $ret = eval { &$code() }; + $ret = "" unless defined $ret; + if ($@) { + # warn "DEBUG: c[$c]\$\@[$@]ret[$ret]"; + return; + } + } + } + return $HAS_USABLE->{$mod} = 1; +} + +sub frontend { + shift; + $CPAN::Frontend = shift if @_; + $CPAN::Frontend; +} + +sub use_inst { + my ($self, $module) = @_; + + unless ($self->has_inst($module)) { + $self->frontend->mydie("$module not installed, cannot continue"); + } +} + +#-> sub CPAN::has_inst +sub has_inst { + my($self,$mod,$message) = @_; + Carp::croak("CPAN->has_inst() called without an argument") + unless defined $mod; + my %dont = map { $_ => 1 } keys %{$CPAN::META->{dontload_hash}||{}}, + keys %{$CPAN::Config->{dontload_hash}||{}}, + @{$CPAN::Config->{dontload_list}||[]}; + if (defined $message && $message eq "no" # as far as I remember only used by Nox + || + $dont{$mod} + ) { + $CPAN::META->{dontload_hash}{$mod}||=1; # unsafe meta access, ok + return 0; + } + local @INC = @INC; + pop @INC if $INC[-1] eq '.'; + my $file = $mod; + my $obj; + $file =~ s|::|/|g; + $file .= ".pm"; + if ($INC{$file}) { + # checking %INC is wrong, because $INC{LWP} may be true + # although $INC{"URI/URL.pm"} may have failed. But as + # I really want to say "blah loaded OK", I have to somehow + # cache results. + ### warn "$file in %INC"; #debug + return 1; + } elsif (eval { require $file }) { + # eval is good: if we haven't yet read the database it's + # perfect and if we have installed the module in the meantime, + # it tries again. The second require is only a NOOP returning + # 1 if we had success, otherwise it's retrying + + my $mtime = (stat $INC{$file})[9]; + # privileged files loaded by has_inst; Note: we use $mtime + # as a proxy for a checksum. + $CPAN::Shell::reload->{$file} = $mtime; + my $v = eval "\$$mod\::VERSION"; + $v = $v ? " (v$v)" : ""; + CPAN::Shell->optprint("load_module","CPAN: $mod loaded ok$v\n"); + if ($mod eq "CPAN::WAIT") { + push @CPAN::Shell::ISA, 'CPAN::WAIT'; + } + return 1; + } elsif ($mod eq "Net::FTP") { + $CPAN::Frontend->mywarn(qq{ + Please, install Net::FTP as soon as possible. CPAN.pm installs it for you + if you just type + install Bundle::libnet + +}) unless $Have_warned->{"Net::FTP"}++; + $CPAN::Frontend->mysleep(3); + } elsif ($mod eq "Digest::SHA") { + if ($Have_warned->{"Digest::SHA"}++) { + $CPAN::Frontend->mywarn(qq{CPAN: checksum security checks disabled }. + qq{because Digest::SHA not installed.\n}); + } else { + $CPAN::Frontend->mywarn(qq{ + CPAN: checksum security checks disabled because Digest::SHA not installed. + Please consider installing the Digest::SHA module. + +}); + $CPAN::Frontend->mysleep(2); + } + } elsif ($mod eq "Module::Signature") { + # NOT prefs_lookup, we are not a distro + my $check_sigs = $CPAN::Config->{check_sigs}; + if (not $check_sigs) { + # they do not want us:-( + } elsif (not $Have_warned->{"Module::Signature"}++) { + # No point in complaining unless the user can + # reasonably install and use it. + if (eval { require Crypt::OpenPGP; 1 } || + ( + defined $CPAN::Config->{'gpg'} + && + $CPAN::Config->{'gpg'} =~ /\S/ + ) + ) { + $CPAN::Frontend->mywarn(qq{ + CPAN: Module::Signature security checks disabled because Module::Signature + not installed. Please consider installing the Module::Signature module. + You may also need to be able to connect over the Internet to the public + key servers like pool.sks-keyservers.net or pgp.mit.edu. + +}); + $CPAN::Frontend->mysleep(2); + } + } + } else { + delete $INC{$file}; # if it inc'd LWP but failed during, say, URI + } + return 0; +} + +#-> sub CPAN::instance ; +sub instance { + my($mgr,$class,$id) = @_; + CPAN::Index->reload; + $id ||= ""; + # unsafe meta access, ok? + return $META->{readwrite}{$class}{$id} if exists $META->{readwrite}{$class}{$id}; + $META->{readwrite}{$class}{$id} ||= $class->new(ID => $id); +} + +#-> sub CPAN::new ; +sub new { + bless {}, shift; +} + +#-> sub CPAN::_exit_messages ; +sub _exit_messages { + my ($self) = @_; + $self->{exit_messages} ||= []; +} + +#-> sub CPAN::cleanup ; +sub cleanup { + # warn "cleanup called with arg[@_] End[$CPAN::End] Signal[$Signal]"; + local $SIG{__DIE__} = ''; + my($message) = @_; + my $i = 0; + my $ineval = 0; + my($subroutine); + while ((undef,undef,undef,$subroutine) = caller(++$i)) { + $ineval = 1, last if + $subroutine eq '(eval)'; + } + return if $ineval && !$CPAN::End; + return unless defined $META->{LOCK}; + return unless -f $META->{LOCK}; + $META->savehist; + $META->{cachemgr} ||= CPAN::CacheMgr->new('atexit'); + close $META->{LOCKFH}; + unlink $META->{LOCK}; + # require Carp; + # Carp::cluck("DEBUGGING"); + if ( $CPAN::CONFIG_DIRTY ) { + $CPAN::Frontend->mywarn("Warning: Configuration not saved.\n"); + } + $CPAN::Frontend->myprint("Lockfile removed.\n"); + for my $msg ( @{ $META->_exit_messages } ) { + $CPAN::Frontend->myprint($msg); + } +} + +#-> sub CPAN::readhist +sub readhist { + my($self,$term,$histfile) = @_; + my $histsize = $CPAN::Config->{'histsize'} || 100; + $term->Attribs->{'MaxHistorySize'} = $histsize if (defined($term->Attribs->{'MaxHistorySize'})); + my($fh) = FileHandle->new; + open $fh, "<$histfile" or return; + local $/ = "\n"; + while (<$fh>) { + chomp; + $term->AddHistory($_); + } + close $fh; +} + +#-> sub CPAN::savehist +sub savehist { + my($self) = @_; + my($histfile,$histsize); + unless ($histfile = $CPAN::Config->{'histfile'}) { + $CPAN::Frontend->mywarn("No history written (no histfile specified).\n"); + return; + } + $histsize = $CPAN::Config->{'histsize'} || 100; + if ($CPAN::term) { + unless ($CPAN::term->can("GetHistory")) { + $CPAN::Frontend->mywarn("Terminal does not support GetHistory.\n"); + return; + } + } else { + return; + } + my @h = $CPAN::term->GetHistory; + splice @h, 0, @h-$histsize if @h>$histsize; + my($fh) = FileHandle->new; + open $fh, ">$histfile" or $CPAN::Frontend->mydie("Couldn't open >$histfile: $!"); + local $\ = local $, = "\n"; + print $fh @h; + close $fh; +} + +#-> sub CPAN::is_tested +sub is_tested { + my($self,$what,$when) = @_; + unless ($what) { + Carp::cluck("DEBUG: empty what"); + return; + } + $self->{is_tested}{$what} = $when; +} + +#-> sub CPAN::reset_tested +# forget all distributions tested -- resets what gets included in PERL5LIB +sub reset_tested { + my ($self) = @_; + $self->{is_tested} = {}; +} + +#-> sub CPAN::is_installed +# unsets the is_tested flag: as soon as the thing is installed, it is +# not needed in set_perl5lib anymore +sub is_installed { + my($self,$what) = @_; + delete $self->{is_tested}{$what}; +} + +sub _list_sorted_descending_is_tested { + my($self) = @_; + my $foul = 0; + my @sorted = sort + { ($self->{is_tested}{$b}||0) <=> ($self->{is_tested}{$a}||0) } + grep + { if ($foul){ 0 } elsif (-e) { 1 } else { $foul = $_; 0 } } + keys %{$self->{is_tested}}; + if ($foul) { + $CPAN::Frontend->mywarn("Lost build_dir detected ($foul), giving up all cached test results of currently running session.\n"); + for my $dbd (sort keys %{$self->{is_tested}}) { # distro-build-dir + SEARCH: for my $d (sort { $a->id cmp $b->id } $CPAN::META->all_objects("CPAN::Distribution")) { + if ($d->{build_dir} && $d->{build_dir} eq $dbd) { + $CPAN::Frontend->mywarn(sprintf "Flushing cache for %s\n", $d->pretty_id); + $d->fforce(""); + last SEARCH; + } + } + delete $self->{is_tested}{$dbd}; + } + return (); + } else { + return @sorted; + } +} + +#-> sub CPAN::set_perl5lib +# Notes on max environment variable length: +# - Win32 : XP or later, 8191; Win2000 or NT4, 2047 +{ +my $fh; +sub set_perl5lib { + my($self,$for) = @_; + unless ($for) { + (undef,undef,undef,$for) = caller(1); + $for =~ s/.*://; + } + $self->{is_tested} ||= {}; + return unless %{$self->{is_tested}}; + my $env = $ENV{PERL5LIB}; + $env = $ENV{PERLLIB} unless defined $env; + my @env; + push @env, split /\Q$Config::Config{path_sep}\E/, $env if defined $env and length $env; + #my @dirs = map {("$_/blib/arch", "$_/blib/lib")} keys %{$self->{is_tested}}; + #$CPAN::Frontend->myprint("Prepending @dirs to PERL5LIB.\n"); + + my @dirs = map {("$_/blib/arch", "$_/blib/lib")} $self->_list_sorted_descending_is_tested; + return if !@dirs; + + if (@dirs < 12) { + $CPAN::Frontend->optprint('perl5lib', "Prepending @dirs to PERL5LIB for '$for'\n"); + $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env; + } elsif (@dirs < 24 ) { + my @d = map {my $cp = $_; + $cp =~ s/^\Q$CPAN::Config->{build_dir}\E/%BUILDDIR%/; + $cp + } @dirs; + $CPAN::Frontend->optprint('perl5lib', "Prepending @d to PERL5LIB; ". + "%BUILDDIR%=$CPAN::Config->{build_dir} ". + "for '$for'\n" + ); + $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env; + } else { + my $cnt = keys %{$self->{is_tested}}; + my $newenv = join $Config::Config{path_sep}, @dirs, @env; + $CPAN::Frontend->optprint('perl5lib', sprintf ("Prepending blib/arch and blib/lib of ". + "%d build dirs to PERL5LIB, reaching size %d; ". + "for '%s'\n", $cnt, length($newenv), $for) + ); + $ENV{PERL5LIB} = $newenv; + } +}} + + +1; + + +__END__ + +=head1 NAME + +CPAN - query, download and build perl modules from CPAN sites + +=head1 SYNOPSIS + +Interactive mode: + + perl -MCPAN -e shell + +--or-- + + cpan + +Basic commands: + + # Modules: + + cpan> install Acme::Meta # in the shell + + CPAN::Shell->install("Acme::Meta"); # in perl + + # Distributions: + + cpan> install NWCLARK/Acme-Meta-0.02.tar.gz # in the shell + + CPAN::Shell-> + install("NWCLARK/Acme-Meta-0.02.tar.gz"); # in perl + + # module objects: + + $mo = CPAN::Shell->expandany($mod); + $mo = CPAN::Shell->expand("Module",$mod); # same thing + + # distribution objects: + + $do = CPAN::Shell->expand("Module",$mod)->distribution; + $do = CPAN::Shell->expandany($distro); # same thing + $do = CPAN::Shell->expand("Distribution", + $distro); # same thing + +=head1 DESCRIPTION + +The CPAN module automates or at least simplifies the make and install +of perl modules and extensions. It includes some primitive searching +capabilities and knows how to use LWP, HTTP::Tiny, Net::FTP and certain +external download clients to fetch distributions from the net. + +These are fetched from one or more mirrored CPAN (Comprehensive +Perl Archive Network) sites and unpacked in a dedicated directory. + +The CPAN module also supports named and versioned +I<bundles> of modules. Bundles simplify handling of sets of +related modules. See Bundles below. + +The package contains a session manager and a cache manager. The +session manager keeps track of what has been fetched, built, and +installed in the current session. The cache manager keeps track of the +disk space occupied by the make processes and deletes excess space +using a simple FIFO mechanism. + +All methods provided are accessible in a programmer style and in an +interactive shell style. + +=head2 CPAN::shell([$prompt, $command]) Starting Interactive Mode + +Enter interactive mode by running + + perl -MCPAN -e shell + +or + + cpan + +which puts you into a readline interface. If C<Term::ReadKey> and +either of C<Term::ReadLine::Perl> or C<Term::ReadLine::Gnu> are installed, +history and command completion are supported. + +Once at the command line, type C<h> for one-page help +screen; the rest should be self-explanatory. + +The function call C<shell> takes two optional arguments: one the +prompt, the second the default initial command line (the latter +only works if a real ReadLine interface module is installed). + +The most common uses of the interactive modes are + +=over 2 + +=item Searching for authors, bundles, distribution files and modules + +There are corresponding one-letter commands C<a>, C<b>, C<d>, and C<m> +for each of the four categories and another, C<i> for any of the +mentioned four. Each of the four entities is implemented as a class +with slightly differing methods for displaying an object. + +Arguments to these commands are either strings exactly matching +the identification string of an object, or regular expressions +matched case-insensitively against various attributes of the +objects. The parser only recognizes a regular expression when you +enclose it with slashes. + +The principle is that the number of objects found influences how an +item is displayed. If the search finds one item, the result is +displayed with the rather verbose method C<as_string>, but if +more than one is found, each object is displayed with the terse method +C<as_glimpse>. + +Examples: + + cpan> m Acme::MetaSyntactic + Module id = Acme::MetaSyntactic + CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) + CPAN_VERSION 0.99 + CPAN_FILE B/BO/BOOK/Acme-MetaSyntactic-0.99.tar.gz + UPLOAD_DATE 2006-11-06 + MANPAGE Acme::MetaSyntactic - Themed metasyntactic variables names + INST_FILE /usr/local/lib/perl/5.10.0/Acme/MetaSyntactic.pm + INST_VERSION 0.99 + cpan> a BOOK + Author id = BOOK + EMAIL [...] + FULLNAME Philippe Bruhat (BooK) + cpan> d BOOK/Acme-MetaSyntactic-0.99.tar.gz + Distribution id = B/BO/BOOK/Acme-MetaSyntactic-0.99.tar.gz + CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) + CONTAINSMODS Acme::MetaSyntactic Acme::MetaSyntactic::Alias [...] + UPLOAD_DATE 2006-11-06 + cpan> m /lorem/ + Module = Acme::MetaSyntactic::loremipsum (BOOK/Acme-MetaSyntactic-0.99.tar.gz) + Module Text::Lorem (ADEOLA/Text-Lorem-0.3.tar.gz) + Module Text::Lorem::More (RKRIMEN/Text-Lorem-More-0.12.tar.gz) + Module Text::Lorem::More::Source (RKRIMEN/Text-Lorem-More-0.12.tar.gz) + cpan> i /berlin/ + Distribution BEATNIK/Filter-NumberLines-0.02.tar.gz + Module = DateTime::TimeZone::Europe::Berlin (DROLSKY/DateTime-TimeZone-0.7904.tar.gz) + Module Filter::NumberLines (BEATNIK/Filter-NumberLines-0.02.tar.gz) + Author [...] + +The examples illustrate several aspects: the first three queries +target modules, authors, or distros directly and yield exactly one +result. The last two use regular expressions and yield several +results. The last one targets all of bundles, modules, authors, and +distros simultaneously. When more than one result is available, they +are printed in one-line format. + +=item C<get>, C<make>, C<test>, C<install>, C<clean> modules or distributions + +These commands take any number of arguments and investigate what is +necessary to perform the action. Argument processing is as follows: + + known module name in format Foo/Bar.pm module + other embedded slash distribution + - with trailing slash dot directory + enclosing slashes regexp + known module name in format Foo::Bar module + +If the argument is a distribution file name (recognized by embedded +slashes), it is processed. If it is a module, CPAN determines the +distribution file in which this module is included and processes that, +following any dependencies named in the module's META.yml or +Makefile.PL (this behavior is controlled by the configuration +parameter C<prerequisites_policy>). If an argument is enclosed in +slashes it is treated as a regular expression: it is expanded and if +the result is a single object (distribution, bundle or module), this +object is processed. + +Example: + + install Dummy::Perl # installs the module + install AUXXX/Dummy-Perl-3.14.tar.gz # installs that distribution + install /Dummy-Perl-3.14/ # same if the regexp is unambiguous + +C<get> downloads a distribution file and untars or unzips it, C<make> +builds it, C<test> runs the test suite, and C<install> installs it. + +Any C<make> or C<test> is run unconditionally. An + + install <distribution_file> + +is also run unconditionally. But for + + install <module> + +CPAN checks whether an install is needed and prints +I<module up to date> if the distribution file containing +the module doesn't need updating. + +CPAN also keeps track of what it has done within the current session +and doesn't try to build a package a second time regardless of whether it +succeeded or not. It does not repeat a test run if the test +has been run successfully before. Same for install runs. + +The C<force> pragma may precede another command (currently: C<get>, +C<make>, C<test>, or C<install>) to execute the command from scratch +and attempt to continue past certain errors. See the section below on +the C<force> and the C<fforce> pragma. + +The C<notest> pragma skips the test part in the build +process. + +Example: + + cpan> notest install Tk + +A C<clean> command results in a + + make clean + +being executed within the distribution file's working directory. + +=item C<readme>, C<perldoc>, C<look> module or distribution + +C<readme> displays the README file of the associated distribution. +C<Look> gets and untars (if not yet done) the distribution file, +changes to the appropriate directory and opens a subshell process in +that directory. C<perldoc> displays the module's pod documentation +in html or plain text format. + +=item C<ls> author + +=item C<ls> globbing_expression + +The first form lists all distribution files in and below an author's +CPAN directory as stored in the CHECKSUMS files distributed on +CPAN. The listing recurses into subdirectories. + +The second form limits or expands the output with shell +globbing as in the following examples: + + ls JV/make* + ls GSAR/*make* + ls */*make* + +The last example is very slow and outputs extra progress indicators +that break the alignment of the result. + +Note that globbing only lists directories explicitly asked for, for +example FOO/* will not list FOO/bar/Acme-Sthg-n.nn.tar.gz. This may be +regarded as a bug that may be changed in some future version. + +=item C<failed> + +The C<failed> command reports all distributions that failed on one of +C<make>, C<test> or C<install> for some reason in the currently +running shell session. + +=item Persistence between sessions + +If the C<YAML> or the C<YAML::Syck> module is installed a record of +the internal state of all modules is written to disk after each step. +The files contain a signature of the currently running perl version +for later perusal. + +If the configurations variable C<build_dir_reuse> is set to a true +value, then CPAN.pm reads the collected YAML files. If the stored +signature matches the currently running perl, the stored state is +loaded into memory such that persistence between sessions +is effectively established. + +=item The C<force> and the C<fforce> pragma + +To speed things up in complex installation scenarios, CPAN.pm keeps +track of what it has already done and refuses to do some things a +second time. A C<get>, a C<make>, and an C<install> are not repeated. +A C<test> is repeated only if the previous test was unsuccessful. The +diagnostic message when CPAN.pm refuses to do something a second time +is one of I<Has already been >C<unwrapped|made|tested successfully> or +something similar. Another situation where CPAN refuses to act is an +C<install> if the corresponding C<test> was not successful. + +In all these cases, the user can override this stubborn behaviour by +prepending the command with the word force, for example: + + cpan> force get Foo + cpan> force make AUTHOR/Bar-3.14.tar.gz + cpan> force test Baz + cpan> force install Acme::Meta + +Each I<forced> command is executed with the corresponding part of its +memory erased. + +The C<fforce> pragma is a variant that emulates a C<force get> which +erases the entire memory followed by the action specified, effectively +restarting the whole get/make/test/install procedure from scratch. + +=item Lockfile + +Interactive sessions maintain a lockfile, by default C<~/.cpan/.lock>. +Batch jobs can run without a lockfile and not disturb each other. + +The shell offers to run in I<downgraded mode> when another process is +holding the lockfile. This is an experimental feature that is not yet +tested very well. This second shell then does not write the history +file, does not use the metadata file, and has a different prompt. + +=item Signals + +CPAN.pm installs signal handlers for SIGINT and SIGTERM. While you are +in the cpan-shell, it is intended that you can press C<^C> anytime and +return to the cpan-shell prompt. A SIGTERM will cause the cpan-shell +to clean up and leave the shell loop. You can emulate the effect of a +SIGTERM by sending two consecutive SIGINTs, which usually means by +pressing C<^C> twice. + +CPAN.pm ignores SIGPIPE. If the user sets C<inactivity_timeout>, a +SIGALRM is used during the run of the C<perl Makefile.PL> or C<perl +Build.PL> subprocess. A SIGALRM is also used during module version +parsing, and is controlled by C<version_timeout>. + +=back + +=head2 CPAN::Shell + +The commands available in the shell interface are methods in +the package CPAN::Shell. If you enter the shell command, your +input is split by the Text::ParseWords::shellwords() routine, which +acts like most shells do. The first word is interpreted as the +method to be invoked, and the rest of the words are treated as the method's arguments. +Continuation lines are supported by ending a line with a +literal backslash. + +=head2 autobundle + +C<autobundle> writes a bundle file into the +C<$CPAN::Config-E<gt>{cpan_home}/Bundle> directory. The file contains +a list of all modules that are both available from CPAN and currently +installed within @INC. Duplicates of each distribution are suppressed. +The name of the bundle file is based on the current date and a +counter, e.g. F<Bundle/Snapshot_2012_05_21_00.pm>. This is installed +again by running C<cpan Bundle::Snapshot_2012_05_21_00>, or installing +C<Bundle::Snapshot_2012_05_21_00> from the CPAN shell. + +Return value: path to the written file. + +=head2 hosts + +Note: this feature is still in alpha state and may change in future +versions of CPAN.pm + +This commands provides a statistical overview over recent download +activities. The data for this is collected in the YAML file +C<FTPstats.yml> in your C<cpan_home> directory. If no YAML module is +configured or YAML not installed, no stats are provided. + +=over + +=item install_tested + +Install all distributions that have been tested successfully but have +not yet been installed. See also C<is_tested>. + +=item is_tested + +List all build directories of distributions that have been tested +successfully but have not yet been installed. See also +C<install_tested>. + +=back + +=head2 mkmyconfig + +mkmyconfig() writes your own CPAN::MyConfig file into your C<~/.cpan/> +directory so that you can save your own preferences instead of the +system-wide ones. + +=head2 r [Module|/Regexp/]... + +scans current perl installation for modules that have a newer version +available on CPAN and provides a list of them. If called without +argument, all potential upgrades are listed; if called with arguments +the list is filtered to the modules and regexps given as arguments. + +The listing looks something like this: + + Package namespace installed latest in CPAN file + CPAN 1.94_64 1.9600 ANDK/CPAN-1.9600.tar.gz + CPAN::Reporter 1.1801 1.1902 DAGOLDEN/CPAN-Reporter-1.1902.tar.gz + YAML 0.70 0.73 INGY/YAML-0.73.tar.gz + YAML::Syck 1.14 1.17 AVAR/YAML-Syck-1.17.tar.gz + YAML::Tiny 1.44 1.50 ADAMK/YAML-Tiny-1.50.tar.gz + CGI 3.43 3.55 MARKSTOS/CGI.pm-3.55.tar.gz + Module::Build::YAML 1.40 1.41 DAGOLDEN/Module-Build-0.3800.tar.gz + TAP::Parser::Result::YAML 3.22 3.23 ANDYA/Test-Harness-3.23.tar.gz + YAML::XS 0.34 0.35 INGY/YAML-LibYAML-0.35.tar.gz + +It suppresses duplicates in the column C<in CPAN file> such that +distributions with many upgradeable modules are listed only once. + +Note that the list is not sorted. + +=head2 recent ***EXPERIMENTAL COMMAND*** + +The C<recent> command downloads a list of recent uploads to CPAN and +displays them I<slowly>. While the command is running, a $SIG{INT} +exits the loop after displaying the current item. + +B<Note>: This command requires XML::LibXML installed. + +B<Note>: This whole command currently is just a hack and will +probably change in future versions of CPAN.pm, but the general +approach will likely remain. + +B<Note>: See also L<smoke> + +=head2 recompile + +recompile() is a special command that takes no argument and +runs the make/test/install cycle with brute force over all installed +dynamically loadable extensions (a.k.a. XS modules) with 'force' in +effect. The primary purpose of this command is to finish a network +installation. Imagine you have a common source tree for two different +architectures. You decide to do a completely independent fresh +installation. You start on one architecture with the help of a Bundle +file produced earlier. CPAN installs the whole Bundle for you, but +when you try to repeat the job on the second architecture, CPAN +responds with a C<"Foo up to date"> message for all modules. So you +invoke CPAN's recompile on the second architecture and you're done. + +Another popular use for C<recompile> is to act as a rescue in case your +perl breaks binary compatibility. If one of the modules that CPAN uses +is in turn depending on binary compatibility (so you cannot run CPAN +commands), then you should try the CPAN::Nox module for recovery. + +=head2 report Bundle|Distribution|Module + +The C<report> command temporarily turns on the C<test_report> config +variable, then runs the C<force test> command with the given +arguments. The C<force> pragma reruns the tests and repeats +every step that might have failed before. + +=head2 smoke ***EXPERIMENTAL COMMAND*** + +B<*** WARNING: this command downloads and executes software from CPAN to +your computer of completely unknown status. You should never do +this with your normal account and better have a dedicated well +separated and secured machine to do this. ***> + +The C<smoke> command takes the list of recent uploads to CPAN as +provided by the C<recent> command and tests them all. While the +command is running $SIG{INT} is defined to mean that the current item +shall be skipped. + +B<Note>: This whole command currently is just a hack and will +probably change in future versions of CPAN.pm, but the general +approach will likely remain. + +B<Note>: See also L<recent> + +=head2 upgrade [Module|/Regexp/]... + +The C<upgrade> command first runs an C<r> command with the given +arguments and then installs the newest versions of all modules that +were listed by that. + +=head2 The four C<CPAN::*> Classes: Author, Bundle, Module, Distribution + +Although it may be considered internal, the class hierarchy does matter +for both users and programmer. CPAN.pm deals with the four +classes mentioned above, and those classes all share a set of methods. Classical +single polymorphism is in effect. A metaclass object registers all +objects of all kinds and indexes them with a string. The strings +referencing objects have a separated namespace (well, not completely +separated): + + Namespace Class + + words containing a "/" (slash) Distribution + words starting with Bundle:: Bundle + everything else Module or Author + +Modules know their associated Distribution objects. They always refer +to the most recent official release. Developers may mark their releases +as unstable development versions (by inserting an underscore into the +module version number which will also be reflected in the distribution +name when you run 'make dist'), so the really hottest and newest +distribution is not always the default. If a module Foo circulates +on CPAN in both version 1.23 and 1.23_90, CPAN.pm offers a convenient +way to install version 1.23 by saying + + install Foo + +This would install the complete distribution file (say +BAR/Foo-1.23.tar.gz) with all accompanying material. But if you would +like to install version 1.23_90, you need to know where the +distribution file resides on CPAN relative to the authors/id/ +directory. If the author is BAR, this might be BAR/Foo-1.23_90.tar.gz; +so you would have to say + + install BAR/Foo-1.23_90.tar.gz + +The first example will be driven by an object of the class +CPAN::Module, the second by an object of class CPAN::Distribution. + +=head2 Integrating local directories + +Note: this feature is still in alpha state and may change in future +versions of CPAN.pm + +Distribution objects are normally distributions from the CPAN, but +there is a slightly degenerate case for Distribution objects, too, of +projects held on the local disk. These distribution objects have the +same name as the local directory and end with a dot. A dot by itself +is also allowed for the current directory at the time CPAN.pm was +used. All actions such as C<make>, C<test>, and C<install> are applied +directly to that directory. This gives the command C<cpan .> an +interesting touch: while the normal mantra of installing a CPAN module +without CPAN.pm is one of + + perl Makefile.PL perl Build.PL + ( go and get prerequisites ) + make ./Build + make test ./Build test + make install ./Build install + +the command C<cpan .> does all of this at once. It figures out which +of the two mantras is appropriate, fetches and installs all +prerequisites, takes care of them recursively, and finally finishes the +installation of the module in the current directory, be it a CPAN +module or not. + +The typical usage case is for private modules or working copies of +projects from remote repositories on the local disk. + +=head2 Redirection + +The usual shell redirection symbols C< | > and C<< > >> are recognized +by the cpan shell B<only when surrounded by whitespace>. So piping to +pager or redirecting output into a file works somewhat as in a normal +shell, with the stipulation that you must type extra spaces. + +=head2 Plugin support ***EXPERIMENTAL*** + +Plugins are objects that implement any of currently eight methods: + + pre_get + post_get + pre_make + post_make + pre_test + post_test + pre_install + post_install + +The C<plugin_list> configuration parameter holds a list of strings of +the form + + Modulename=arg0,arg1,arg2,arg3,... + +eg: + + CPAN::Plugin::Flurb=dir,/opt/pkgs/flurb/raw,verbose,1 + +At run time, each listed plugin is instantiated as a singleton object +by running the equivalent of this pseudo code: + + my $plugin = <string representation from config>; + <generate Modulename and arguments from $plugin>; + my $p = $instance{$plugin} ||= Modulename->new($arg0,$arg1,...); + +The generated singletons are kept around from instantiation until the +end of the shell session. <plugin_list> can be reconfigured at any +time at run time. While the cpan shell is running, it checks all +activated plugins at each of the 8 reference points listed above and +runs the respective method if it is implemented for that object. The +method is called with the active CPAN::Distribution object passed in +as an argument. + +=head1 CONFIGURATION + +When the CPAN module is used for the first time, a configuration +dialogue tries to determine a couple of site specific options. The +result of the dialog is stored in a hash reference C< $CPAN::Config > +in a file CPAN/Config.pm. + +Default values defined in the CPAN/Config.pm file can be +overridden in a user specific file: CPAN/MyConfig.pm. Such a file is +best placed in C<$HOME/.cpan/CPAN/MyConfig.pm>, because C<$HOME/.cpan> is +added to the search path of the CPAN module before the use() or +require() statements. The mkmyconfig command writes this file for you. + +The C<o conf> command has various bells and whistles: + +=over + +=item completion support + +If you have a ReadLine module installed, you can hit TAB at any point +of the commandline and C<o conf> will offer you completion for the +built-in subcommands and/or config variable names. + +=item displaying some help: o conf help + +Displays a short help + +=item displaying current values: o conf [KEY] + +Displays the current value(s) for this config variable. Without KEY, +displays all subcommands and config variables. + +Example: + + o conf shell + +If KEY starts and ends with a slash, the string in between is +treated as a regular expression and only keys matching this regexp +are displayed + +Example: + + o conf /color/ + +=item changing of scalar values: o conf KEY VALUE + +Sets the config variable KEY to VALUE. The empty string can be +specified as usual in shells, with C<''> or C<""> + +Example: + + o conf wget /usr/bin/wget + +=item changing of list values: o conf KEY SHIFT|UNSHIFT|PUSH|POP|SPLICE|LIST + +If a config variable name ends with C<list>, it is a list. C<o conf +KEY shift> removes the first element of the list, C<o conf KEY pop> +removes the last element of the list. C<o conf KEYS unshift LIST> +prepends a list of values to the list, C<o conf KEYS push LIST> +appends a list of valued to the list. + +Likewise, C<o conf KEY splice LIST> passes the LIST to the corresponding +splice command. + +Finally, any other list of arguments is taken as a new list value for +the KEY variable discarding the previous value. + +Examples: + + o conf urllist unshift http://cpan.dev.local/CPAN + o conf urllist splice 3 1 + o conf urllist http://cpan1.local http://cpan2.local ftp://ftp.perl.org + +=item reverting to saved: o conf defaults + +Reverts all config variables to the state in the saved config file. + +=item saving the config: o conf commit + +Saves all config variables to the current config file (CPAN/Config.pm +or CPAN/MyConfig.pm that was loaded at start). + +=back + +The configuration dialog can be started any time later again by +issuing the command C< o conf init > in the CPAN shell. A subset of +the configuration dialog can be run by issuing C<o conf init WORD> +where WORD is any valid config variable or a regular expression. + +=head2 Config Variables + +The following keys in the hash reference $CPAN::Config are +currently defined: + + allow_installing_module_downgrades + allow or disallow installing module downgrades + allow_installing_outdated_dists + allow or disallow installing modules that are + indexed in the cpan index pointing to a distro + with a higher distro-version number + applypatch path to external prg + auto_commit commit all changes to config variables to disk + build_cache size of cache for directories to build modules + build_dir locally accessible directory to build modules + build_dir_reuse boolean if distros in build_dir are persistent + build_requires_install_policy + to install or not to install when a module is + only needed for building. yes|no|ask/yes|ask/no + bzip2 path to external prg + cache_metadata use serializer to cache metadata + check_sigs if signatures should be verified + cleanup_after_install + remove build directory immediately after a + successful install and remember that for the + duration of the session + colorize_debug Term::ANSIColor attributes for debugging output + colorize_output boolean if Term::ANSIColor should colorize output + colorize_print Term::ANSIColor attributes for normal output + colorize_warn Term::ANSIColor attributes for warnings + commandnumber_in_prompt + boolean if you want to see current command number + commands_quote preferred character to use for quoting external + commands when running them. Defaults to double + quote on Windows, single tick everywhere else; + can be set to space to disable quoting + connect_to_internet_ok + whether to ask if opening a connection is ok before + urllist is specified + cpan_home local directory reserved for this package + curl path to external prg + dontload_hash DEPRECATED + dontload_list arrayref: modules in the list will not be + loaded by the CPAN::has_inst() routine + ftp path to external prg + ftp_passive if set, the environment variable FTP_PASSIVE is set + for downloads + ftp_proxy proxy host for ftp requests + ftpstats_period max number of days to keep download statistics + ftpstats_size max number of items to keep in the download statistics + getcwd see below + gpg path to external prg + gzip location of external program gzip + halt_on_failure stop processing after the first failure of queued + items or dependencies + histfile file to maintain history between sessions + histsize maximum number of lines to keep in histfile + http_proxy proxy host for http requests + inactivity_timeout breaks interactive Makefile.PLs or Build.PLs + after this many seconds inactivity. Set to 0 to + disable timeouts. + index_expire refetch index files after this many days + inhibit_startup_message + if true, suppress the startup message + keep_source_where directory in which to keep the source (if we do) + load_module_verbosity + report loading of optional modules used by CPAN.pm + lynx path to external prg + make location of external make program + make_arg arguments that should always be passed to 'make' + make_install_make_command + the make command for running 'make install', for + example 'sudo make' + make_install_arg same as make_arg for 'make install' + makepl_arg arguments passed to 'perl Makefile.PL' + mbuild_arg arguments passed to './Build' + mbuild_install_arg arguments passed to './Build install' + mbuild_install_build_command + command to use instead of './Build' when we are + in the install stage, for example 'sudo ./Build' + mbuildpl_arg arguments passed to 'perl Build.PL' + ncftp path to external prg + ncftpget path to external prg + no_proxy don't proxy to these hosts/domains (comma separated list) + pager location of external program more (or any pager) + password your password if you CPAN server wants one + patch path to external prg + patches_dir local directory containing patch files + perl5lib_verbosity verbosity level for PERL5LIB additions + plugin_list list of active hooks (see Plugin support above + and the CPAN::Plugin module) + prefer_external_tar + per default all untar operations are done with + Archive::Tar; by setting this variable to true + the external tar command is used if available + prefer_installer legal values are MB and EUMM: if a module comes + with both a Makefile.PL and a Build.PL, use the + former (EUMM) or the latter (MB); if the module + comes with only one of the two, that one will be + used no matter the setting + prerequisites_policy + what to do if you are missing module prerequisites + ('follow' automatically, 'ask' me, or 'ignore') + For 'follow', also sets PERL_AUTOINSTALL and + PERL_EXTUTILS_AUTOINSTALL for "--defaultdeps" if + not already set + prefs_dir local directory to store per-distro build options + proxy_user username for accessing an authenticating proxy + proxy_pass password for accessing an authenticating proxy + randomize_urllist add some randomness to the sequence of the urllist + recommends_policy whether recommended prerequisites should be included + scan_cache controls scanning of cache ('atstart', 'atexit' or 'never') + shell your favorite shell + show_unparsable_versions + boolean if r command tells which modules are versionless + show_upload_date boolean if commands should try to determine upload date + show_zero_versions boolean if r command tells for which modules $version==0 + suggests_policy whether suggested prerequisites should be included + tar location of external program tar + tar_verbosity verbosity level for the tar command + term_is_latin deprecated: if true Unicode is translated to ISO-8859-1 + (and nonsense for characters outside latin range) + term_ornaments boolean to turn ReadLine ornamenting on/off + test_report email test reports (if CPAN::Reporter is installed) + trust_test_report_history + skip testing when previously tested ok (according to + CPAN::Reporter history) + unzip location of external program unzip + urllist arrayref to nearby CPAN sites (or equivalent locations) + urllist_ping_external + use external ping command when autoselecting mirrors + urllist_ping_verbose + increase verbosity when autoselecting mirrors + use_prompt_default set PERL_MM_USE_DEFAULT for configure/make/test/install + use_sqlite use CPAN::SQLite for metadata storage (fast and lean) + username your username if you CPAN server wants one + version_timeout stops version parsing after this many seconds. + Default is 15 secs. Set to 0 to disable. + wait_list arrayref to a wait server to try (See CPAN::WAIT) + wget path to external prg + yaml_load_code enable YAML code deserialisation via CPAN::DeferredCode + yaml_module which module to use to read/write YAML files + +You can set and query each of these options interactively in the cpan +shell with the C<o conf> or the C<o conf init> command as specified below. + +=over 2 + +=item C<o conf E<lt>scalar optionE<gt>> + +prints the current value of the I<scalar option> + +=item C<o conf E<lt>scalar optionE<gt> E<lt>valueE<gt>> + +Sets the value of the I<scalar option> to I<value> + +=item C<o conf E<lt>list optionE<gt>> + +prints the current value of the I<list option> in MakeMaker's +neatvalue format. + +=item C<o conf E<lt>list optionE<gt> [shift|pop]> + +shifts or pops the array in the I<list option> variable + +=item C<o conf E<lt>list optionE<gt> [unshift|push|splice] E<lt>listE<gt>> + +works like the corresponding perl commands. + +=item interactive editing: o conf init [MATCH|LIST] + +Runs an interactive configuration dialog for matching variables. +Without argument runs the dialog over all supported config variables. +To specify a MATCH the argument must be enclosed by slashes. + +Examples: + + o conf init ftp_passive ftp_proxy + o conf init /color/ + +Note: this method of setting config variables often provides more +explanation about the functioning of a variable than the manpage. + +=back + +=head2 CPAN::anycwd($path): Note on config variable getcwd + +CPAN.pm changes the current working directory often and needs to +determine its own current working directory. By default it uses +Cwd::cwd, but if for some reason this doesn't work on your system, +configure alternatives according to the following table: + +=over 4 + +=item cwd + +Calls Cwd::cwd + +=item getcwd + +Calls Cwd::getcwd + +=item fastcwd + +Calls Cwd::fastcwd + +=item getdcwd + +Calls Cwd::getdcwd + +=item backtickcwd + +Calls the external command cwd. + +=back + +=head2 Note on the format of the urllist parameter + +urllist parameters are URLs according to RFC 1738. We do a little +guessing if your URL is not compliant, but if you have problems with +C<file> URLs, please try the correct format. Either: + + file://localhost/whatever/ftp/pub/CPAN/ + +or + + file:///home/ftp/pub/CPAN/ + +=head2 The urllist parameter has CD-ROM support + +The C<urllist> parameter of the configuration table contains a list of +URLs used for downloading. If the list contains any +C<file> URLs, CPAN always tries there first. This +feature is disabled for index files. So the recommendation for the +owner of a CD-ROM with CPAN contents is: include your local, possibly +outdated CD-ROM as a C<file> URL at the end of urllist, e.g. + + o conf urllist push file://localhost/CDROM/CPAN + +CPAN.pm will then fetch the index files from one of the CPAN sites +that come at the beginning of urllist. It will later check for each +module to see whether there is a local copy of the most recent version. + +Another peculiarity of urllist is that the site that we could +successfully fetch the last file from automatically gets a preference +token and is tried as the first site for the next request. So if you +add a new site at runtime it may happen that the previously preferred +site will be tried another time. This means that if you want to disallow +a site for the next transfer, it must be explicitly removed from +urllist. + +=head2 Maintaining the urllist parameter + +If you have YAML.pm (or some other YAML module configured in +C<yaml_module>) installed, CPAN.pm collects a few statistical data +about recent downloads. You can view the statistics with the C<hosts> +command or inspect them directly by looking into the C<FTPstats.yml> +file in your C<cpan_home> directory. + +To get some interesting statistics, it is recommended that +C<randomize_urllist> be set; this introduces some amount of +randomness into the URL selection. + +=head2 The C<requires> and C<build_requires> dependency declarations + +Since CPAN.pm version 1.88_51 modules declared as C<build_requires> by +a distribution are treated differently depending on the config +variable C<build_requires_install_policy>. By setting +C<build_requires_install_policy> to C<no>, such a module is not +installed. It is only built and tested, and then kept in the list of +tested but uninstalled modules. As such, it is available during the +build of the dependent module by integrating the path to the +C<blib/arch> and C<blib/lib> directories in the environment variable +PERL5LIB. If C<build_requires_install_policy> is set to C<yes>, then +both modules declared as C<requires> and those declared as +C<build_requires> are treated alike. By setting to C<ask/yes> or +C<ask/no>, CPAN.pm asks the user and sets the default accordingly. + +=head2 Configuration of the allow_installing_* parameters + +The C<allow_installing_*> parameters are evaluated during +the C<make> phase. If set to C<yes>, they allow the testing and the installation of +the current distro and otherwise have no effect. If set to C<no>, they +may abort the build (preventing testing and installing), depending on the contents of the +C<blib/> directory. The C<blib/> directory is the directory that holds +all the files that would usually be installed in the C<install> phase. + +C<allow_installing_outdated_dists> compares the C<blib/> directory with the CPAN index. +If it finds something there that belongs, according to the index, to a different +dist, it aborts the current build. + +C<allow_installing_module_downgrades> compares the C<blib/> directory +with already installed modules, actually their version numbers, as +determined by ExtUtils::MakeMaker or equivalent. If a to-be-installed +module would downgrade an already installed module, the current build +is aborted. + +An interesting twist occurs when a distroprefs document demands the +installation of an outdated dist via goto while +C<allow_installing_outdated_dists> forbids it. Without additional +provisions, this would let the C<allow_installing_outdated_dists> +win and the distroprefs lose. So the proper arrangement in such a case +is to write a second distroprefs document for the distro that C<goto> +points to and overrule the C<cpanconfig> there. E.g.: + + --- + match: + distribution: "^MAUKE/Keyword-Simple-0.04.tar.gz" + goto: "MAUKE/Keyword-Simple-0.03.tar.gz" + --- + match: + distribution: "^MAUKE/Keyword-Simple-0.03.tar.gz" + cpanconfig: + allow_installing_outdated_dists: yes + +=head2 Configuration for individual distributions (I<Distroprefs>) + +(B<Note:> This feature has been introduced in CPAN.pm 1.8854) + +Distributions on CPAN usually behave according to what we call the +CPAN mantra. Or since the advent of Module::Build we should talk about +two mantras: + + perl Makefile.PL perl Build.PL + make ./Build + make test ./Build test + make install ./Build install + +But some modules cannot be built with this mantra. They try to get +some extra data from the user via the environment, extra arguments, or +interactively--thus disturbing the installation of large bundles like +Phalanx100 or modules with many dependencies like Plagger. + +The distroprefs system of C<CPAN.pm> addresses this problem by +allowing the user to specify extra informations and recipes in YAML +files to either + +=over + +=item + +pass additional arguments to one of the four commands, + +=item + +set environment variables + +=item + +instantiate an Expect object that reads from the console, waits for +some regular expressions and enters some answers + +=item + +temporarily override assorted C<CPAN.pm> configuration variables + +=item + +specify dependencies the original maintainer forgot + +=item + +disable the installation of an object altogether + +=back + +See the YAML and Data::Dumper files that come with the C<CPAN.pm> +distribution in the C<distroprefs/> directory for examples. + +=head2 Filenames + +The YAML files themselves must have the C<.yml> extension; all other +files are ignored (for two exceptions see I<Fallback Data::Dumper and +Storable> below). The containing directory can be specified in +C<CPAN.pm> in the C<prefs_dir> config variable. Try C<o conf init +prefs_dir> in the CPAN shell to set and activate the distroprefs +system. + +Every YAML file may contain arbitrary documents according to the YAML +specification, and every document is treated as an entity that +can specify the treatment of a single distribution. + +Filenames can be picked arbitrarily; C<CPAN.pm> always reads +all files (in alphabetical order) and takes the key C<match> (see +below in I<Language Specs>) as a hashref containing match criteria +that determine if the current distribution matches the YAML document +or not. + +=head2 Fallback Data::Dumper and Storable + +If neither your configured C<yaml_module> nor YAML.pm is installed, +CPAN.pm falls back to using Data::Dumper and Storable and looks for +files with the extensions C<.dd> or C<.st> in the C<prefs_dir> +directory. These files are expected to contain one or more hashrefs. +For Data::Dumper generated files, this is expected to be done with by +defining C<$VAR1>, C<$VAR2>, etc. The YAML shell would produce these +with the command + + ysh < somefile.yml > somefile.dd + +For Storable files the rule is that they must be constructed such that +C<Storable::retrieve(file)> returns an array reference and the array +elements represent one distropref object each. The conversion from +YAML would look like so: + + perl -MYAML=LoadFile -MStorable=nstore -e ' + @y=LoadFile(shift); + nstore(\@y, shift)' somefile.yml somefile.st + +In bootstrapping situations it is usually sufficient to translate only +a few YAML files to Data::Dumper for crucial modules like +C<YAML::Syck>, C<YAML.pm> and C<Expect.pm>. If you prefer Storable +over Data::Dumper, remember to pull out a Storable version that writes +an older format than all the other Storable versions that will need to +read them. + +=head2 Blueprint + +The following example contains all supported keywords and structures +with the exception of C<eexpect> which can be used instead of +C<expect>. + + --- + comment: "Demo" + match: + module: "Dancing::Queen" + distribution: "^CHACHACHA/Dancing-" + not_distribution: "\.zip$" + perl: "/usr/local/cariba-perl/bin/perl" + perlconfig: + archname: "freebsd" + not_cc: "gcc" + env: + DANCING_FLOOR: "Shubiduh" + disabled: 1 + cpanconfig: + make: gmake + pl: + args: + - "--somearg=specialcase" + + env: {} + + expect: + - "Which is your favorite fruit" + - "apple\n" + + make: + args: + - all + - extra-all + + env: {} + + expect: [] + + commandline: "echo SKIPPING make" + + test: + args: [] + + env: {} + + expect: [] + + install: + args: [] + + env: + WANT_TO_INSTALL: YES + + expect: + - "Do you really want to install" + - "y\n" + + patches: + - "ABCDE/Fedcba-3.14-ABCDE-01.patch" + + depends: + configure_requires: + LWP: 5.8 + build_requires: + Test::Exception: 0.25 + requires: + Spiffy: 0.30 + + +=head2 Language Specs + +Every YAML document represents a single hash reference. The valid keys +in this hash are as follows: + +=over + +=item comment [scalar] + +A comment + +=item cpanconfig [hash] + +Temporarily override assorted C<CPAN.pm> configuration variables. + +Supported are: C<build_requires_install_policy>, C<check_sigs>, +C<make>, C<make_install_make_command>, C<prefer_installer>, +C<test_report>. Please report as a bug when you need another one +supported. + +=item depends [hash] *** EXPERIMENTAL FEATURE *** + +All three types, namely C<configure_requires>, C<build_requires>, and +C<requires> are supported in the way specified in the META.yml +specification. The current implementation I<merges> the specified +dependencies with those declared by the package maintainer. In a +future implementation this may be changed to override the original +declaration. + +=item disabled [boolean] + +Specifies that this distribution shall not be processed at all. + +=item features [array] *** EXPERIMENTAL FEATURE *** + +Experimental implementation to deal with optional_features from +META.yml. Still needs coordination with installer software and +currently works only for META.yml declaring C<dynamic_config=0>. Use +with caution. + +=item goto [string] + +The canonical name of a delegate distribution to install +instead. Useful when a new version, although it tests OK itself, +breaks something else or a developer release or a fork is already +uploaded that is better than the last released version. + +=item install [hash] + +Processing instructions for the C<make install> or C<./Build install> +phase of the CPAN mantra. See below under I<Processing Instructions>. + +=item make [hash] + +Processing instructions for the C<make> or C<./Build> phase of the +CPAN mantra. See below under I<Processing Instructions>. + +=item match [hash] + +A hashref with one or more of the keys C<distribution>, C<module>, +C<perl>, C<perlconfig>, and C<env> that specify whether a document is +targeted at a specific CPAN distribution or installation. +Keys prefixed with C<not_> negates the corresponding match. + +The corresponding values are interpreted as regular expressions. The +C<distribution> related one will be matched against the canonical +distribution name, e.g. "AUTHOR/Foo-Bar-3.14.tar.gz". + +The C<module> related one will be matched against I<all> modules +contained in the distribution until one module matches. + +The C<perl> related one will be matched against C<$^X> (but with the +absolute path). + +The value associated with C<perlconfig> is itself a hashref that is +matched against corresponding values in the C<%Config::Config> hash +living in the C<Config.pm> module. +Keys prefixed with C<not_> negates the corresponding match. + +The value associated with C<env> is itself a hashref that is +matched against corresponding values in the C<%ENV> hash. +Keys prefixed with C<not_> negates the corresponding match. + +If more than one restriction of C<module>, C<distribution>, etc. is +specified, the results of the separately computed match values must +all match. If so, the hashref represented by the +YAML document is returned as the preference structure for the current +distribution. + +=item patches [array] + +An array of patches on CPAN or on the local disk to be applied in +order via an external patch program. If the value for the C<-p> +parameter is C<0> or C<1> is determined by reading the patch +beforehand. The path to each patch is either an absolute path on the +local filesystem or relative to a patch directory specified in the +C<patches_dir> configuration variable or in the format of a canonical +distro name. For examples please consult the distroprefs/ directory in +the CPAN.pm distribution (these examples are not installed by +default). + +Note: if the C<applypatch> program is installed and C<CPAN::Config> +knows about it B<and> a patch is written by the C<makepatch> program, +then C<CPAN.pm> lets C<applypatch> apply the patch. Both C<makepatch> +and C<applypatch> are available from CPAN in the C<JV/makepatch-*> +distribution. + +=item pl [hash] + +Processing instructions for the C<perl Makefile.PL> or C<perl +Build.PL> phase of the CPAN mantra. See below under I<Processing +Instructions>. + +=item test [hash] + +Processing instructions for the C<make test> or C<./Build test> phase +of the CPAN mantra. See below under I<Processing Instructions>. + +=back + +=head2 Processing Instructions + +=over + +=item args [array] + +Arguments to be added to the command line + +=item commandline + +A full commandline to run via C<system()>. +During execution, the environment variable PERL is set +to $^X (but with an absolute path). If C<commandline> is specified, +C<args> is not used. + +=item eexpect [hash] + +Extended C<expect>. This is a hash reference with four allowed keys, +C<mode>, C<timeout>, C<reuse>, and C<talk>. + +You must install the C<Expect> module to use C<eexpect>. CPAN.pm +does not install it for you. + +C<mode> may have the values C<deterministic> for the case where all +questions come in the order written down and C<anyorder> for the case +where the questions may come in any order. The default mode is +C<deterministic>. + +C<timeout> denotes a timeout in seconds. Floating-point timeouts are +OK. With C<mode=deterministic>, the timeout denotes the +timeout per question; with C<mode=anyorder> it denotes the +timeout per byte received from the stream or questions. + +C<talk> is a reference to an array that contains alternating questions +and answers. Questions are regular expressions and answers are literal +strings. The Expect module watches the stream from the +execution of the external program (C<perl Makefile.PL>, C<perl +Build.PL>, C<make>, etc.). + +For C<mode=deterministic>, the CPAN.pm injects the +corresponding answer as soon as the stream matches the regular expression. + +For C<mode=anyorder> CPAN.pm answers a question as soon +as the timeout is reached for the next byte in the input stream. In +this mode you can use the C<reuse> parameter to decide what will +happen with a question-answer pair after it has been used. In the +default case (reuse=0) it is removed from the array, avoiding being +used again accidentally. If you want to answer the +question C<Do you really want to do that> several times, then it must +be included in the array at least as often as you want this answer to +be given. Setting the parameter C<reuse> to 1 makes this repetition +unnecessary. + +=item env [hash] + +Environment variables to be set during the command + +=item expect [array] + +You must install the C<Expect> module to use C<expect>. CPAN.pm +does not install it for you. + +C<< expect: <array> >> is a short notation for this C<eexpect>: + + eexpect: + mode: deterministic + timeout: 15 + talk: <array> + +=back + +=head2 Schema verification with C<Kwalify> + +If you have the C<Kwalify> module installed (which is part of the +Bundle::CPANxxl), then all your distroprefs files are checked for +syntactic correctness. + +=head2 Example Distroprefs Files + +C<CPAN.pm> comes with a collection of example YAML files. Note that these +are really just examples and should not be used without care because +they cannot fit everybody's purpose. After all, the authors of the +packages that ask questions had a need to ask, so you should watch +their questions and adjust the examples to your environment and your +needs. You have been warned:-) + +=head1 PROGRAMMER'S INTERFACE + +If you do not enter the shell, shell commands are +available both as methods (C<CPAN::Shell-E<gt>install(...)>) and as +functions in the calling package (C<install(...)>). Before calling low-level +commands, it makes sense to initialize components of CPAN you need, e.g.: + + CPAN::HandleConfig->load; + CPAN::Shell::setup_output; + CPAN::Index->reload; + +High-level commands do such initializations automatically. + +There's currently only one class that has a stable interface - +CPAN::Shell. All commands that are available in the CPAN shell are +methods of the class CPAN::Shell. The arguments on the commandline are +passed as arguments to the method. + +So if you take for example the shell command + + notest install A B C + +the actually executed command is + + CPAN::Shell->notest("install","A","B","C"); + +Each of the commands that produce listings of modules (C<r>, +C<autobundle>, C<u>) also return a list of the IDs of all modules +within the list. + +=over 2 + +=item expand($type,@things) + +The IDs of all objects available within a program are strings that can +be expanded to the corresponding real objects with the +C<CPAN::Shell-E<gt>expand("Module",@things)> method. Expand returns a +list of CPAN::Module objects according to the C<@things> arguments +given. In scalar context, it returns only the first element of the +list. + +=item expandany(@things) + +Like expand, but returns objects of the appropriate type, i.e. +CPAN::Bundle objects for bundles, CPAN::Module objects for modules, and +CPAN::Distribution objects for distributions. Note: it does not expand +to CPAN::Author objects. + +=item Programming Examples + +This enables the programmer to do operations that combine +functionalities that are available in the shell. + + # install everything that is outdated on my disk: + perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)' + + # install my favorite programs if necessary: + for $mod (qw(Net::FTP Digest::SHA Data::Dumper)) { + CPAN::Shell->install($mod); + } + + # list all modules on my disk that have no VERSION number + for $mod (CPAN::Shell->expand("Module","/./")) { + next unless $mod->inst_file; + # MakeMaker convention for undefined $VERSION: + next unless $mod->inst_version eq "undef"; + print "No VERSION in ", $mod->id, "\n"; + } + + # find out which distribution on CPAN contains a module: + print CPAN::Shell->expand("Module","Apache::Constants")->cpan_file + +Or if you want to schedule a I<cron> job to watch CPAN, you could list +all modules that need updating. First a quick and dirty way: + + perl -e 'use CPAN; CPAN::Shell->r;' + +If you don't want any output should all modules be +up to date, parse the output of above command for the regular +expression C</modules are up to date/> and decide to mail the output +only if it doesn't match. + +If you prefer to do it more in a programmerish style in one single +process, something like this may better suit you: + + # list all modules on my disk that have newer versions on CPAN + for $mod (CPAN::Shell->expand("Module","/./")) { + next unless $mod->inst_file; + next if $mod->uptodate; + printf "Module %s is installed as %s, could be updated to %s from CPAN\n", + $mod->id, $mod->inst_version, $mod->cpan_version; + } + +If that gives too much output every day, you may want to +watch only for three modules. You can write + + for $mod (CPAN::Shell->expand("Module","/Apache|LWP|CGI/")) { + +as the first line instead. Or you can combine some of the above +tricks: + + # watch only for a new mod_perl module + $mod = CPAN::Shell->expand("Module","mod_perl"); + exit if $mod->uptodate; + # new mod_perl arrived, let me know all update recommendations + CPAN::Shell->r; + +=back + +=head2 Methods in the other Classes + +=over 4 + +=item CPAN::Author::as_glimpse() + +Returns a one-line description of the author + +=item CPAN::Author::as_string() + +Returns a multi-line description of the author + +=item CPAN::Author::email() + +Returns the author's email address + +=item CPAN::Author::fullname() + +Returns the author's name + +=item CPAN::Author::name() + +An alias for fullname + +=item CPAN::Bundle::as_glimpse() + +Returns a one-line description of the bundle + +=item CPAN::Bundle::as_string() + +Returns a multi-line description of the bundle + +=item CPAN::Bundle::clean() + +Recursively runs the C<clean> method on all items contained in the bundle. + +=item CPAN::Bundle::contains() + +Returns a list of objects' IDs contained in a bundle. The associated +objects may be bundles, modules or distributions. + +=item CPAN::Bundle::force($method,@args) + +Forces CPAN to perform a task that it normally would have refused to +do. Force takes as arguments a method name to be called and any number +of additional arguments that should be passed to the called method. +The internals of the object get the needed changes so that CPAN.pm +does not refuse to take the action. The C<force> is passed recursively +to all contained objects. See also the section above on the C<force> +and the C<fforce> pragma. + +=item CPAN::Bundle::get() + +Recursively runs the C<get> method on all items contained in the bundle + +=item CPAN::Bundle::inst_file() + +Returns the highest installed version of the bundle in either @INC or +C<< $CPAN::Config->{cpan_home} >>. Note that this is different from +CPAN::Module::inst_file. + +=item CPAN::Bundle::inst_version() + +Like CPAN::Bundle::inst_file, but returns the $VERSION + +=item CPAN::Bundle::uptodate() + +Returns 1 if the bundle itself and all its members are up-to-date. + +=item CPAN::Bundle::install() + +Recursively runs the C<install> method on all items contained in the bundle + +=item CPAN::Bundle::make() + +Recursively runs the C<make> method on all items contained in the bundle + +=item CPAN::Bundle::readme() + +Recursively runs the C<readme> method on all items contained in the bundle + +=item CPAN::Bundle::test() + +Recursively runs the C<test> method on all items contained in the bundle + +=item CPAN::Distribution::as_glimpse() + +Returns a one-line description of the distribution + +=item CPAN::Distribution::as_string() + +Returns a multi-line description of the distribution + +=item CPAN::Distribution::author + +Returns the CPAN::Author object of the maintainer who uploaded this +distribution + +=item CPAN::Distribution::pretty_id() + +Returns a string of the form "AUTHORID/TARBALL", where AUTHORID is the +author's PAUSE ID and TARBALL is the distribution filename. + +=item CPAN::Distribution::base_id() + +Returns the distribution filename without any archive suffix. E.g +"Foo-Bar-0.01" + +=item CPAN::Distribution::clean() + +Changes to the directory where the distribution has been unpacked and +runs C<make clean> there. + +=item CPAN::Distribution::containsmods() + +Returns a list of IDs of modules contained in a distribution file. +Works only for distributions listed in the 02packages.details.txt.gz +file. This typically means that just most recent version of a +distribution is covered. + +=item CPAN::Distribution::cvs_import() + +Changes to the directory where the distribution has been unpacked and +runs something like + + cvs -d $cvs_root import -m $cvs_log $cvs_dir $userid v$version + +there. + +=item CPAN::Distribution::dir() + +Returns the directory into which this distribution has been unpacked. + +=item CPAN::Distribution::force($method,@args) + +Forces CPAN to perform a task that it normally would have refused to +do. Force takes as arguments a method name to be called and any number +of additional arguments that should be passed to the called method. +The internals of the object get the needed changes so that CPAN.pm +does not refuse to take the action. See also the section above on the +C<force> and the C<fforce> pragma. + +=item CPAN::Distribution::get() + +Downloads the distribution from CPAN and unpacks it. Does nothing if +the distribution has already been downloaded and unpacked within the +current session. + +=item CPAN::Distribution::install() + +Changes to the directory where the distribution has been unpacked and +runs the external command C<make install> there. If C<make> has not +yet been run, it will be run first. A C<make test> is issued in +any case and if this fails, the install is cancelled. The +cancellation can be avoided by letting C<force> run the C<install> for +you. + +This install method only has the power to install the distribution if +there are no dependencies in the way. To install an object along with all +its dependencies, use CPAN::Shell->install. + +Note that install() gives no meaningful return value. See uptodate(). + +=item CPAN::Distribution::isa_perl() + +Returns 1 if this distribution file seems to be a perl distribution. +Normally this is derived from the file name only, but the index from +CPAN can contain a hint to achieve a return value of true for other +filenames too. + +=item CPAN::Distribution::look() + +Changes to the directory where the distribution has been unpacked and +opens a subshell there. Exiting the subshell returns. + +=item CPAN::Distribution::make() + +First runs the C<get> method to make sure the distribution is +downloaded and unpacked. Changes to the directory where the +distribution has been unpacked and runs the external commands C<perl +Makefile.PL> or C<perl Build.PL> and C<make> there. + +=item CPAN::Distribution::perldoc() + +Downloads the pod documentation of the file associated with a +distribution (in HTML format) and runs it through the external +command I<lynx> specified in C<< $CPAN::Config->{lynx} >>. If I<lynx> +isn't available, it converts it to plain text with the external +command I<html2text> and runs it through the pager specified +in C<< $CPAN::Config->{pager} >>. + +=item CPAN::Distribution::prefs() + +Returns the hash reference from the first matching YAML file that the +user has deposited in the C<prefs_dir/> directory. The first +succeeding match wins. The files in the C<prefs_dir/> are processed +alphabetically, and the canonical distro name (e.g. +AUTHOR/Foo-Bar-3.14.tar.gz) is matched against the regular expressions +stored in the $root->{match}{distribution} attribute value. +Additionally all module names contained in a distribution are matched +against the regular expressions in the $root->{match}{module} attribute +value. The two match values are ANDed together. Each of the two +attributes are optional. + +=item CPAN::Distribution::prereq_pm() + +Returns the hash reference that has been announced by a distribution +as the C<requires> and C<build_requires> elements. These can be +declared either by the C<META.yml> (if authoritative) or can be +deposited after the run of C<Build.PL> in the file C<./_build/prereqs> +or after the run of C<Makfile.PL> written as the C<PREREQ_PM> hash in +a comment in the produced C<Makefile>. I<Note>: this method only works +after an attempt has been made to C<make> the distribution. Returns +undef otherwise. + +=item CPAN::Distribution::readme() + +Downloads the README file associated with a distribution and runs it +through the pager specified in C<< $CPAN::Config->{pager} >>. + +=item CPAN::Distribution::reports() + +Downloads report data for this distribution from www.cpantesters.org +and displays a subset of them. + +=item CPAN::Distribution::read_yaml() + +Returns the content of the META.yml of this distro as a hashref. Note: +works only after an attempt has been made to C<make> the distribution. +Returns undef otherwise. Also returns undef if the content of META.yml +is not authoritative. (The rules about what exactly makes the content +authoritative are still in flux.) + +=item CPAN::Distribution::test() + +Changes to the directory where the distribution has been unpacked and +runs C<make test> there. + +=item CPAN::Distribution::uptodate() + +Returns 1 if all the modules contained in the distribution are +up-to-date. Relies on containsmods. + +=item CPAN::Index::force_reload() + +Forces a reload of all indices. + +=item CPAN::Index::reload() + +Reloads all indices if they have not been read for more than +C<< $CPAN::Config->{index_expire} >> days. + +=item CPAN::InfoObj::dump() + +CPAN::Author, CPAN::Bundle, CPAN::Module, and CPAN::Distribution +inherit this method. It prints the data structure associated with an +object. Useful for debugging. Note: the data structure is considered +internal and thus subject to change without notice. + +=item CPAN::Module::as_glimpse() + +Returns a one-line description of the module in four columns: The +first column contains the word C<Module>, the second column consists +of one character: an equals sign if this module is already installed +and up-to-date, a less-than sign if this module is installed but can be +upgraded, and a space if the module is not installed. The third column +is the name of the module and the fourth column gives maintainer or +distribution information. + +=item CPAN::Module::as_string() + +Returns a multi-line description of the module + +=item CPAN::Module::clean() + +Runs a clean on the distribution associated with this module. + +=item CPAN::Module::cpan_file() + +Returns the filename on CPAN that is associated with the module. + +=item CPAN::Module::cpan_version() + +Returns the latest version of this module available on CPAN. + +=item CPAN::Module::cvs_import() + +Runs a cvs_import on the distribution associated with this module. + +=item CPAN::Module::description() + +Returns a 44 character description of this module. Only available for +modules listed in The Module List (CPAN/modules/00modlist.long.html +or 00modlist.long.txt.gz) + +=item CPAN::Module::distribution() + +Returns the CPAN::Distribution object that contains the current +version of this module. + +=item CPAN::Module::dslip_status() + +Returns a hash reference. The keys of the hash are the letters C<D>, +C<S>, C<L>, C<I>, and <P>, for development status, support level, +language, interface and public licence respectively. The data for the +DSLIP status are collected by pause.perl.org when authors register +their namespaces. The values of the 5 hash elements are one-character +words whose meaning is described in the table below. There are also 5 +hash elements C<DV>, C<SV>, C<LV>, C<IV>, and <PV> that carry a more +verbose value of the 5 status variables. + +Where the 'DSLIP' characters have the following meanings: + + D - Development Stage (Note: *NO IMPLIED TIMESCALES*): + i - Idea, listed to gain consensus or as a placeholder + c - under construction but pre-alpha (not yet released) + a/b - Alpha/Beta testing + R - Released + M - Mature (no rigorous definition) + S - Standard, supplied with Perl 5 + + S - Support Level: + m - Mailing-list + d - Developer + u - Usenet newsgroup comp.lang.perl.modules + n - None known, try comp.lang.perl.modules + a - abandoned; volunteers welcome to take over maintenance + + L - Language Used: + p - Perl-only, no compiler needed, should be platform independent + c - C and perl, a C compiler will be needed + h - Hybrid, written in perl with optional C code, no compiler needed + + - C++ and perl, a C++ compiler will be needed + o - perl and another language other than C or C++ + + I - Interface Style + f - plain Functions, no references used + h - hybrid, object and function interfaces available + n - no interface at all (huh?) + r - some use of unblessed References or ties + O - Object oriented using blessed references and/or inheritance + + P - Public License + p - Standard-Perl: user may choose between GPL and Artistic + g - GPL: GNU General Public License + l - LGPL: "GNU Lesser General Public License" (previously known as + "GNU Library General Public License") + b - BSD: The BSD License + a - Artistic license alone + 2 - Artistic license 2.0 or later + o - open source: approved by www.opensource.org + d - allows distribution without restrictions + r - restricted distribution + n - no license at all + +=item CPAN::Module::force($method,@args) + +Forces CPAN to perform a task it would normally refuse to +do. Force takes as arguments a method name to be invoked and any number +of additional arguments to pass that method. +The internals of the object get the needed changes so that CPAN.pm +does not refuse to take the action. See also the section above on the +C<force> and the C<fforce> pragma. + +=item CPAN::Module::get() + +Runs a get on the distribution associated with this module. + +=item CPAN::Module::inst_file() + +Returns the filename of the module found in @INC. The first file found +is reported, just as perl itself stops searching @INC once it finds a +module. + +=item CPAN::Module::available_file() + +Returns the filename of the module found in PERL5LIB or @INC. The +first file found is reported. The advantage of this method over +C<inst_file> is that modules that have been tested but not yet +installed are included because PERL5LIB keeps track of tested modules. + +=item CPAN::Module::inst_version() + +Returns the version number of the installed module in readable format. + +=item CPAN::Module::available_version() + +Returns the version number of the available module in readable format. + +=item CPAN::Module::install() + +Runs an C<install> on the distribution associated with this module. + +=item CPAN::Module::look() + +Changes to the directory where the distribution associated with this +module has been unpacked and opens a subshell there. Exiting the +subshell returns. + +=item CPAN::Module::make() + +Runs a C<make> on the distribution associated with this module. + +=item CPAN::Module::manpage_headline() + +If module is installed, peeks into the module's manpage, reads the +headline, and returns it. Moreover, if the module has been downloaded +within this session, does the equivalent on the downloaded module even +if it hasn't been installed yet. + +=item CPAN::Module::perldoc() + +Runs a C<perldoc> on this module. + +=item CPAN::Module::readme() + +Runs a C<readme> on the distribution associated with this module. + +=item CPAN::Module::reports() + +Calls the reports() method on the associated distribution object. + +=item CPAN::Module::test() + +Runs a C<test> on the distribution associated with this module. + +=item CPAN::Module::uptodate() + +Returns 1 if the module is installed and up-to-date. + +=item CPAN::Module::userid() + +Returns the author's ID of the module. + +=back + +=head2 Cache Manager + +Currently the cache manager only keeps track of the build directory +($CPAN::Config->{build_dir}). It is a simple FIFO mechanism that +deletes complete directories below C<build_dir> as soon as the size of +all directories there gets bigger than $CPAN::Config->{build_cache} +(in MB). The contents of this cache may be used for later +re-installations that you intend to do manually, but will never be +trusted by CPAN itself. This is due to the fact that the user might +use these directories for building modules on different architectures. + +There is another directory ($CPAN::Config->{keep_source_where}) where +the original distribution files are kept. This directory is not +covered by the cache manager and must be controlled by the user. If +you choose to have the same directory as build_dir and as +keep_source_where directory, then your sources will be deleted with +the same fifo mechanism. + +=head2 Bundles + +A bundle is just a perl module in the namespace Bundle:: that does not +define any functions or methods. It usually only contains documentation. + +It starts like a perl module with a package declaration and a $VERSION +variable. After that the pod section looks like any other pod with the +only difference being that I<one special pod section> exists starting with +(verbatim): + + =head1 CONTENTS + +In this pod section each line obeys the format + + Module_Name [Version_String] [- optional text] + +The only required part is the first field, the name of a module +(e.g. Foo::Bar, i.e. I<not> the name of the distribution file). The rest +of the line is optional. The comment part is delimited by a dash just +as in the man page header. + +The distribution of a bundle should follow the same convention as +other distributions. + +Bundles are treated specially in the CPAN package. If you say 'install +Bundle::Tkkit' (assuming such a bundle exists), CPAN will install all +the modules in the CONTENTS section of the pod. You can install your +own Bundles locally by placing a conformant Bundle file somewhere into +your @INC path. The autobundle() command which is available in the +shell interface does that for you by including all currently installed +modules in a snapshot bundle file. + +=head1 PREREQUISITES + +The CPAN program is trying to depend on as little as possible so the +user can use it in hostile environment. It works better the more goodies +the environment provides. For example if you try in the CPAN shell + + install Bundle::CPAN + +or + + install Bundle::CPANxxl + +you will find the shell more convenient than the bare shell before. + +If you have a local mirror of CPAN and can access all files with +"file:" URLs, then you only need a perl later than perl5.003 to run +this module. Otherwise Net::FTP is strongly recommended. LWP may be +required for non-UNIX systems, or if your nearest CPAN site is +associated with a URL that is not C<ftp:>. + +If you have neither Net::FTP nor LWP, there is a fallback mechanism +implemented for an external ftp command or for an external lynx +command. + +=head1 UTILITIES + +=head2 Finding packages and VERSION + +This module presumes that all packages on CPAN + +=over 2 + +=item * + +declare their $VERSION variable in an easy to parse manner. This +prerequisite can hardly be relaxed because it consumes far too much +memory to load all packages into the running program just to determine +the $VERSION variable. Currently all programs that are dealing with +version use something like this + + perl -MExtUtils::MakeMaker -le \ + 'print MM->parse_version(shift)' filename + +If you are author of a package and wonder if your $VERSION can be +parsed, please try the above method. + +=item * + +come as compressed or gzipped tarfiles or as zip files and contain a +C<Makefile.PL> or C<Build.PL> (well, we try to handle a bit more, but +with little enthusiasm). + +=back + +=head2 Debugging + +Debugging this module is more than a bit complex due to interference from +the software producing the indices on CPAN, the mirroring process on CPAN, +packaging, configuration, synchronicity, and even (gasp!) due to bugs +within the CPAN.pm module itself. + +For debugging the code of CPAN.pm itself in interactive mode, some +debugging aid can be turned on for most packages within +CPAN.pm with one of + +=over 2 + +=item o debug package... + +sets debug mode for packages. + +=item o debug -package... + +unsets debug mode for packages. + +=item o debug all + +turns debugging on for all packages. + +=item o debug number + +=back + +which sets the debugging packages directly. Note that C<o debug 0> +turns debugging off. + +What seems a successful strategy is the combination of C<reload +cpan> and the debugging switches. Add a new debug statement while +running in the shell and then issue a C<reload cpan> and see the new +debugging messages immediately without losing the current context. + +C<o debug> without an argument lists the valid package names and the +current set of packages in debugging mode. C<o debug> has built-in +completion support. + +For debugging of CPAN data there is the C<dump> command which takes +the same arguments as make/test/install and outputs each object's +Data::Dumper dump. If an argument looks like a perl variable and +contains one of C<$>, C<@> or C<%>, it is eval()ed and fed to +Data::Dumper directly. + +=head2 Floppy, Zip, Offline Mode + +CPAN.pm works nicely without network access, too. If you maintain machines +that are not networked at all, you should consider working with C<file:> +URLs. You'll have to collect your modules somewhere first. So +you might use CPAN.pm to put together all you need on a networked +machine. Then copy the $CPAN::Config->{keep_source_where} (but not +$CPAN::Config->{build_dir}) directory on a floppy. This floppy is kind +of a personal CPAN. CPAN.pm on the non-networked machines works nicely +with this floppy. See also below the paragraph about CD-ROM support. + +=head2 Basic Utilities for Programmers + +=over 2 + +=item has_inst($module) + +Returns true if the module is installed. Used to load all modules into +the running CPAN.pm that are considered optional. The config variable +C<dontload_list> intercepts the C<has_inst()> call such +that an optional module is not loaded despite being available. For +example, the following command will prevent C<YAML.pm> from being +loaded: + + cpan> o conf dontload_list push YAML + +See the source for details. + +=item use_inst($module) + +Similary to L<has_inst()> tries to load optional library but also dies if +library is not available + +=item has_usable($module) + +Returns true if the module is installed and in a usable state. Only +useful for a handful of modules that are used internally. See the +source for details. + +=item instance($module) + +The constructor for all the singletons used to represent modules, +distributions, authors, and bundles. If the object already exists, this +method returns the object; otherwise, it calls the constructor. + +=item frontend() + +=item frontend($new_frontend) + +Getter/setter for frontend object. Method just allows to subclass CPAN.pm. + +=back + +=head1 SECURITY + +There's no strong security layer in CPAN.pm. CPAN.pm helps you to +install foreign, unmasked, unsigned code on your machine. We compare +to a checksum that comes from the net just as the distribution file +itself. But we try to make it easy to add security on demand: + +=head2 Cryptographically signed modules + +Since release 1.77, CPAN.pm has been able to verify cryptographically +signed module distributions using Module::Signature. The CPAN modules +can be signed by their authors, thus giving more security. The simple +unsigned MD5 checksums that were used before by CPAN protect mainly +against accidental file corruption. + +You will need to have Module::Signature installed, which in turn +requires that you have at least one of Crypt::OpenPGP module or the +command-line F<gpg> tool installed. + +You will also need to be able to connect over the Internet to the public +key servers, like pgp.mit.edu, and their port 11731 (the HKP protocol). + +The configuration parameter check_sigs is there to turn signature +checking on or off. + +=head1 EXPORT + +Most functions in package CPAN are exported by default. The reason +for this is that the primary use is intended for the cpan shell or for +one-liners. + +=head1 ENVIRONMENT + +When the CPAN shell enters a subshell via the look command, it sets +the environment CPAN_SHELL_LEVEL to 1, or increments that variable if it is +already set. + +When CPAN runs, it sets the environment variable PERL5_CPAN_IS_RUNNING +to the ID of the running process. It also sets +PERL5_CPANPLUS_IS_RUNNING to prevent runaway processes which could +happen with older versions of Module::Install. + +When running C<perl Makefile.PL>, the environment variable +C<PERL5_CPAN_IS_EXECUTING> is set to the full path of the +C<Makefile.PL> that is being executed. This prevents runaway processes +with newer versions of Module::Install. + +When the config variable ftp_passive is set, all downloads will be run +with the environment variable FTP_PASSIVE set to this value. This is +in general a good idea as it influences both Net::FTP and LWP based +connections. The same effect can be achieved by starting the cpan +shell with this environment variable set. For Net::FTP alone, one can +also always set passive mode by running libnetcfg. + +=head1 POPULATE AN INSTALLATION WITH LOTS OF MODULES + +Populating a freshly installed perl with one's favorite modules is pretty +easy if you maintain a private bundle definition file. To get a useful +blueprint of a bundle definition file, the command autobundle can be used +on the CPAN shell command line. This command writes a bundle definition +file for all modules installed for the current perl +interpreter. It's recommended to run this command once only, and from then +on maintain the file manually under a private name, say +Bundle/my_bundle.pm. With a clever bundle file you can then simply say + + cpan> install Bundle::my_bundle + +then answer a few questions and go out for coffee (possibly +even in a different city). + +Maintaining a bundle definition file means keeping track of two +things: dependencies and interactivity. CPAN.pm sometimes fails on +calculating dependencies because not all modules define all MakeMaker +attributes correctly, so a bundle definition file should specify +prerequisites as early as possible. On the other hand, it's +annoying that so many distributions need some interactive configuring. So +what you can try to accomplish in your private bundle file is to have the +packages that need to be configured early in the file and the gentle +ones later, so you can go out for coffee after a few minutes and leave CPAN.pm +to churn away unattended. + +=head1 WORKING WITH CPAN.pm BEHIND FIREWALLS + +Thanks to Graham Barr for contributing the following paragraphs about +the interaction between perl, and various firewall configurations. For +further information on firewalls, it is recommended to consult the +documentation that comes with the I<ncftp> program. If you are unable to +go through the firewall with a simple Perl setup, it is likely +that you can configure I<ncftp> so that it works through your firewall. + +=head2 Three basic types of firewalls + +Firewalls can be categorized into three basic types. + +=over 4 + +=item http firewall + +This is when the firewall machine runs a web server, and to access the +outside world, you must do so via that web server. If you set environment +variables like http_proxy or ftp_proxy to values beginning with http://, +or in your web browser you've proxy information set, then you know +you are running behind an http firewall. + +To access servers outside these types of firewalls with perl (even for +ftp), you need LWP or HTTP::Tiny. + +=item ftp firewall + +This where the firewall machine runs an ftp server. This kind of +firewall will only let you access ftp servers outside the firewall. +This is usually done by connecting to the firewall with ftp, then +entering a username like "user@outside.host.com". + +To access servers outside these type of firewalls with perl, you +need Net::FTP. + +=item One-way visibility + +One-way visibility means these firewalls try to make themselves +invisible to users inside the firewall. An FTP data connection is +normally created by sending your IP address to the remote server and then +listening for the return connection. But the remote server will not be able to +connect to you because of the firewall. For these types of firewall, +FTP connections need to be done in a passive mode. + +There are two that I can think off. + +=over 4 + +=item SOCKS + +If you are using a SOCKS firewall, you will need to compile perl and link +it with the SOCKS library. This is what is normally called a 'socksified' +perl. With this executable you will be able to connect to servers outside +the firewall as if it were not there. + +=item IP Masquerade + +This is when the firewall implemented in the kernel (via NAT, or networking +address translation), it allows you to hide a complete network behind one +IP address. With this firewall no special compiling is needed as you can +access hosts directly. + +For accessing ftp servers behind such firewalls you usually need to +set the environment variable C<FTP_PASSIVE> or the config variable +ftp_passive to a true value. + +=back + +=back + +=head2 Configuring lynx or ncftp for going through a firewall + +If you can go through your firewall with e.g. lynx, presumably with a +command such as + + /usr/local/bin/lynx -pscott:tiger + +then you would configure CPAN.pm with the command + + o conf lynx "/usr/local/bin/lynx -pscott:tiger" + +That's all. Similarly for ncftp or ftp, you would configure something +like + + o conf ncftp "/usr/bin/ncftp -f /home/scott/ncftplogin.cfg" + +Your mileage may vary... + +=head1 FAQ + +=over 4 + +=item 1) + +I installed a new version of module X but CPAN keeps saying, +I have the old version installed + +Probably you B<do> have the old version installed. This can +happen if a module installs itself into a different directory in the +@INC path than it was previously installed. This is not really a +CPAN.pm problem, you would have the same problem when installing the +module manually. The easiest way to prevent this behaviour is to add +the argument C<UNINST=1> to the C<make install> call, and that is why +many people add this argument permanently by configuring + + o conf make_install_arg UNINST=1 + +=item 2) + +So why is UNINST=1 not the default? + +Because there are people who have their precise expectations about who +may install where in the @INC path and who uses which @INC array. In +fine tuned environments C<UNINST=1> can cause damage. + +=item 3) + +I want to clean up my mess, and install a new perl along with +all modules I have. How do I go about it? + +Run the autobundle command for your old perl and optionally rename the +resulting bundle file (e.g. Bundle/mybundle.pm), install the new perl +with the Configure option prefix, e.g. + + ./Configure -Dprefix=/usr/local/perl-5.6.78.9 + +Install the bundle file you produced in the first step with something like + + cpan> install Bundle::mybundle + +and you're done. + +=item 4) + +When I install bundles or multiple modules with one command +there is too much output to keep track of. + +You may want to configure something like + + o conf make_arg "| tee -ai /root/.cpan/logs/make.out" + o conf make_install_arg "| tee -ai /root/.cpan/logs/make_install.out" + +so that STDOUT is captured in a file for later inspection. + + +=item 5) + +I am not root, how can I install a module in a personal directory? + +As of CPAN 1.9463, if you do not have permission to write the default perl +library directories, CPAN's configuration process will ask you whether +you want to bootstrap <local::lib>, which makes keeping a personal +perl library directory easy. + +Another thing you should bear in mind is that the UNINST parameter can +be dangerous when you are installing into a private area because you +might accidentally remove modules that other people depend on that are +not using the private area. + +=item 6) + +How to get a package, unwrap it, and make a change before building it? + +Have a look at the C<look> (!) command. + +=item 7) + +I installed a Bundle and had a couple of fails. When I +retried, everything resolved nicely. Can this be fixed to work +on first try? + +The reason for this is that CPAN does not know the dependencies of all +modules when it starts out. To decide about the additional items to +install, it just uses data found in the META.yml file or the generated +Makefile. An undetected missing piece breaks the process. But it may +well be that your Bundle installs some prerequisite later than some +depending item and thus your second try is able to resolve everything. +Please note, CPAN.pm does not know the dependency tree in advance and +cannot sort the queue of things to install in a topologically correct +order. It resolves perfectly well B<if> all modules declare the +prerequisites correctly with the PREREQ_PM attribute to MakeMaker or +the C<requires> stanza of Module::Build. For bundles which fail and +you need to install often, it is recommended to sort the Bundle +definition file manually. + +=item 8) + +In our intranet, we have many modules for internal use. How +can I integrate these modules with CPAN.pm but without uploading +the modules to CPAN? + +Have a look at the CPAN::Site module. + +=item 9) + +When I run CPAN's shell, I get an error message about things in my +C</etc/inputrc> (or C<~/.inputrc>) file. + +These are readline issues and can only be fixed by studying readline +configuration on your architecture and adjusting the referenced file +accordingly. Please make a backup of the C</etc/inputrc> or C<~/.inputrc> +and edit them. Quite often harmless changes like uppercasing or +lowercasing some arguments solves the problem. + +=item 10) + +Some authors have strange characters in their names. + +Internally CPAN.pm uses the UTF-8 charset. If your terminal is +expecting ISO-8859-1 charset, a converter can be activated by setting +term_is_latin to a true value in your config file. One way of doing so +would be + + cpan> o conf term_is_latin 1 + +If other charset support is needed, please file a bug report against +CPAN.pm at rt.cpan.org and describe your needs. Maybe we can extend +the support or maybe UTF-8 terminals become widely available. + +Note: this config variable is deprecated and will be removed in a +future version of CPAN.pm. It will be replaced with the conventions +around the family of $LANG and $LC_* environment variables. + +=item 11) + +When an install fails for some reason and then I correct the error +condition and retry, CPAN.pm refuses to install the module, saying +C<Already tried without success>. + +Use the force pragma like so + + force install Foo::Bar + +Or you can use + + look Foo::Bar + +and then C<make install> directly in the subshell. + +=item 12) + +How do I install a "DEVELOPER RELEASE" of a module? + +By default, CPAN will install the latest non-developer release of a +module. If you want to install a dev release, you have to specify the +partial path starting with the author id to the tarball you wish to +install, like so: + + cpan> install KWILLIAMS/Module-Build-0.27_07.tar.gz + +Note that you can use the C<ls> command to get this path listed. + +=item 13) + +How do I install a module and all its dependencies from the commandline, +without being prompted for anything, despite my CPAN configuration +(or lack thereof)? + +CPAN uses ExtUtils::MakeMaker's prompt() function to ask its questions, so +if you set the PERL_MM_USE_DEFAULT environment variable, you shouldn't be +asked any questions at all (assuming the modules you are installing are +nice about obeying that variable as well): + + % PERL_MM_USE_DEFAULT=1 perl -MCPAN -e 'install My::Module' + +=item 14) + +How do I create a Module::Build based Build.PL derived from an +ExtUtils::MakeMaker focused Makefile.PL? + +http://search.cpan.org/dist/Module-Build-Convert/ + +=item 15) + +I'm frequently irritated with the CPAN shell's inability to help me +select a good mirror. + +CPAN can now help you select a "good" mirror, based on which ones have the +lowest 'ping' round-trip times. From the shell, use the command 'o conf init +urllist' and allow CPAN to automatically select mirrors for you. + +Beyond that help, the urllist config parameter is yours. You can add and remove +sites at will. You should find out which sites have the best up-to-dateness, +bandwidth, reliability, etc. and are topologically close to you. Some people +prefer fast downloads, others up-to-dateness, others reliability. You decide +which to try in which order. + +Henk P. Penning maintains a site that collects data about CPAN sites: + + http://mirrors.cpan.org/ + +Also, feel free to play with experimental features. Run + + o conf init randomize_urllist ftpstats_period ftpstats_size + +and choose your favorite parameters. After a few downloads running the +C<hosts> command will probably assist you in choosing the best mirror +sites. + +=item 16) + +Why do I get asked the same questions every time I start the shell? + +You can make your configuration changes permanent by calling the +command C<o conf commit>. Alternatively set the C<auto_commit> +variable to true by running C<o conf init auto_commit> and answering +the following question with yes. + +=item 17) + +Older versions of CPAN.pm had the original root directory of all +tarballs in the build directory. Now there are always random +characters appended to these directory names. Why was this done? + +The random characters are provided by File::Temp and ensure that each +module's individual build directory is unique. This makes running +CPAN.pm in concurrent processes simultaneously safe. + +=item 18) + +Speaking of the build directory. Do I have to clean it up myself? + +You have the choice to set the config variable C<scan_cache> to +C<never>. Then you must clean it up yourself. The other possible +values, C<atstart> and C<atexit> clean up the build directory when you +start (or more precisely, after the first extraction into the build +directory) or exit the CPAN shell, respectively. If you never start up +the CPAN shell, you probably also have to clean up the build directory +yourself. + +=item 19) + +How can I switch to sudo instead of local::lib? + +The following 5 environment veriables need to be reset to the previous +values: PATH, PERL5LIB, PERL_LOCAL_LIB_ROOT, PERL_MB_OPT, PERL_MM_OPT; +and these two CPAN.pm config variables must be reconfigured: +make_install_make_command and mbuild_install_build_command. The five +env variables have probably been overwritten in your $HOME/.bashrc or +some equivalent. You either find them there and delete their traces +and logout/login or you override them temporarily, depending on your +exact desire. The two cpanpm config variables can be set with: + + o conf init /install_.*_command/ + +probably followed by + + o conf commit + +=back + +=head1 COMPATIBILITY + +=head2 OLD PERL VERSIONS + +CPAN.pm is regularly tested to run under 5.005 and assorted +newer versions. It is getting more and more difficult to get the +minimal prerequisites working on older perls. It is close to +impossible to get the whole Bundle::CPAN working there. If you're in +the position to have only these old versions, be advised that CPAN is +designed to work fine without the Bundle::CPAN installed. + +To get things going, note that GBARR/Scalar-List-Utils-1.18.tar.gz is +compatible with ancient perls and that File::Temp is listed as a +prerequisite but CPAN has reasonable workarounds if it is missing. + +=head2 CPANPLUS + +This module and its competitor, the CPANPLUS module, are both much +cooler than the other. CPAN.pm is older. CPANPLUS was designed to be +more modular, but it was never intended to be compatible with CPAN.pm. + +=head2 CPANMINUS + +In the year 2010 App::cpanminus was launched as a new approach to a +cpan shell with a considerably smaller footprint. Very cool stuff. + +=head1 SECURITY ADVICE + +This software enables you to upgrade software on your computer and so +is inherently dangerous because the newly installed software may +contain bugs and may alter the way your computer works or even make it +unusable. Please consider backing up your data before every upgrade. + +=head1 BUGS + +Please report bugs via L<http://rt.cpan.org/> + +Before submitting a bug, please make sure that the traditional method +of building a Perl module package from a shell by following the +installation instructions of that package still works in your +environment. + +=head1 AUTHOR + +Andreas Koenig C<< <andk@cpan.org> >> + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +See L<http://www.perl.com/perl/misc/Artistic.html> + +=head1 TRANSLATIONS + +Kawai,Takanori provides a Japanese translation of a very old version +of this manpage at +L<http://homepage3.nifty.com/hippo2000/perltips/CPAN.htm> + +=head1 SEE ALSO + +Many people enter the CPAN shell by running the L<cpan> utility +program which is installed in the same directory as perl itself. So if +you have this directory in your PATH variable (or some equivalent in +your operating system) then typing C<cpan> in a console window will +work for you as well. Above that the utility provides several +commandline shortcuts. + +melezhik (Alexey) sent me a link where he published a chef recipe to +work with CPAN.pm: http://community.opscode.com/cookbooks/cpan. + + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/API/HOWTO.pod b/tools/msys/usr/share/perl5/site_perl/CPAN/API/HOWTO.pod new file mode 100644 index 0000000000..e65a4bc931 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/API/HOWTO.pod @@ -0,0 +1,44 @@ +=head1 NAME + +CPAN::API::HOWTO - a recipe book for programming with CPAN.pm + +=head1 RECIPES + +All of these recipes assume that you have put "use CPAN" at the top of +your program. + +=head2 What distribution contains a particular module? + + my $distribution = CPAN::Shell->expand( + "Module", "Data::UUID" + )->distribution()->pretty_id(); + +This returns a string of the form "AUTHORID/TARBALL". If you want the +full path and filename to this distribution on a CPAN mirror, then it is +C<.../authors/id/A/AU/AUTHORID/TARBALL>. + +=head2 What modules does a particular distribution contain? + + CPAN::Index->reload(); + my @modules = CPAN::Shell->expand( + "Distribution", "JHI/Graph-0.83.tar.gz" + )->containsmods(); + +You may also refer to a distribution in the form A/AU/AUTHORID/TARBALL. + +=head1 SEE ALSO + +the main CPAN.pm documentation + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +See L<http://www.perl.com/perl/misc/Artistic.html> + +=head1 AUTHOR + +David Cantrell + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Admin.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Admin.pm new file mode 100644 index 0000000000..35252e9f38 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Admin.pm @@ -0,0 +1,230 @@ +package CPAN::Admin; +use base CPAN; +use CPAN; # old base.pm did not load CPAN on previous line +use strict; +use vars qw(@EXPORT $VERSION); +use constant PAUSE_IP => "pause.perl.org"; + +@EXPORT = qw(shell); +$VERSION = "5.501"; +push @CPAN::Complete::COMMANDS, qw(register modsearch); +$CPAN::Shell::COLOR_REGISTERED = 1; + +sub shell { + CPAN::shell($_[0]||"admin's cpan> ",$_[1]); +} + +sub CPAN::Shell::register { + my($self,$mod,@rest) = @_; + unless ($mod) { + print "register called without argument\n"; + return; + } + if ($CPAN::META->has_inst("URI::Escape")) { + require URI::Escape; + } else { + print "register requires URI::Escape installed, otherwise it cannot work\n"; + return; + } + print "Got request for mod[$mod]\n"; + if (@rest) { + my $modline = join " ", $mod, @rest; + print "Sending to PAUSE [$modline]\n"; + my $emodline = URI::Escape::uri_escape($modline, '^\w '); + $emodline =~ s/ /+/g; + my $url = + sprintf("https://%s/pause/authenquery?pause99_add_mod_modid=". + "%s;SUBMIT_pause99_add_mod_hint=hint", + PAUSE_IP, + $emodline, + ); + print "url[$url]\n\n"; + print ">>>>Trying to open a netscape window<<<<\n"; + sleep 1; + system("netscape","-remote","openURL($url)"); + return; + } + my $m = CPAN::Shell->expand("Module",$mod); + unless (ref $m) { + print "Could not determine the object for $mod\n"; + return; + } + my $id = $m->id; + print "Found module id[$id] in database\n"; + + if (exists $m->{RO} && $m->{RO}{chapterid}) { + print "$id is already registered\n"; + return; + } + + my(@namespace) = split /::/, $id; + my $rootns = $namespace[0]; + + # Tk, XML and Apache need special treatment + if ($rootns=~/^(Bundle)\b/) { + print "Bundles are not yet ready for registering\n"; + return; + } + + # make a good suggestion for the chapter + my(@simile) = CPAN::Shell->expand("Module","/^$rootns(:|\$)/"); + print "Found within this namespace ", join(", ", map { $_->id } @simile), "\n"; + my(%seench); + for my $ch (map { exists $_->{RO} ? $_->{RO}{chapterid} : ""} @simile) { + next unless $ch; + $seench{$ch}=undef; + } + my(@seench) = sort grep {length($_)} keys %seench; + my $reco_ch = ""; + if (@seench>1) { + print "Found rootnamespace[$rootns] in the chapters [", join(", ", @seench), "]\n"; + $reco_ch = $seench[0]; + print "Picking $reco_ch\n"; + } elsif (@seench==1) { + print "Found rootnamespace[$rootns] in the chapter[$seench[0]]\n"; + $reco_ch = $seench[0]; + } else { + print "The new rootnamespace[$rootns] needs to be introduced. Oh well.\n"; + } + + # Look closer at the dist + my $d = CPAN::Shell->expand("Distribution", $m->cpan_file); + printf "Module comes with dist[%s]\n", $d->id; + for my $contm ($d->containsmods) { + if ($CPAN::META->exists("CPAN::Module",$contm)) { + my $contm_obj = CPAN::Shell->expand("Module",$contm) or next; + my $is_reg = exists $contm_obj->{RO} && $contm_obj->{RO}{description}; + printf(" in same dist: %s%s\n", + $contm, + $is_reg ? " already in modulelist" : "", + ); + } + } + + # get it so that m is better and we can inspect for XS + CPAN::Shell->get($id); + CPAN::Shell->m($id); + CPAN::Shell->d($d->id); + + my $has_xs = 0; + { + my($mani,@mani); + local $/ = "\n"; + open $mani, "$d->{build_dir}/MANIFEST" and @mani = <$mani>; + my @xs = grep /\.xs\b/, @mani; + if (@xs) { + print "Found XS files: @xs"; + $has_xs=1; + } + } + my $emodid = URI::Escape::uri_escape($id, '\W'); + my $ech = $reco_ch; + $ech =~ s/ /+/g; + my $description = $m->{MANPAGE} || ""; + $description =~ s/[A-Z]<//; # POD markup (and maybe more) + $description =~ s/^\s+//; # leading spaces + $description =~ s/>//; # POD + $description =~ s/^\Q$id\E//; # usually this line starts with the modid + $description =~ s/^[ \-]+//; # leading spaces and dashes + substr($description,44) = "" if length($description)>44; + $description = ucfirst($description); + my $edescription = URI::Escape::uri_escape($description, '^\w '); + $edescription =~ s/ /+/g; + my $url = + sprintf("https://%s/pause/authenquery?pause99_add_mod_modid=". + "%s;pause99_add_mod_chapterid=%s;pause99_add_mod_statd=%s;". + "pause99_add_mod_stats=%s;pause99_add_mod_statl=%s;". + "pause99_add_mod_stati=%s;pause99_add_mod_description=%s;". + "pause99_add_mod_userid=%s;SUBMIT_pause99_add_mod_preview=preview", + PAUSE_IP, + $emodid, + $ech, + "R", + "d", + $has_xs ? "c" : "p", + "O", + $edescription, + $m->{RO}{CPAN_USERID}, + ); + print "$url\n\n"; + print ">>>>Trying to open a netscape window<<<<\n"; + system("netscape","-remote","openURL($url)"); +} + +sub CPAN::Shell::modsearch { + my($self,@line) = @_; + unless (@line) { + print "modsearch called without argument\n"; + return; + } + my $request = join " ", @line; + print "Got request[$request]\n"; + my $erequest = URI::Escape::uri_escape($request, '^\w '); + $erequest =~ s/ /+/g; + my $url = + sprintf("http://www.xray.mpe.mpg.de/cgi-bin/w3glimpse/modules?query=%s". + "&errors=0&case=on&maxfiles=100&maxlines=30", + $erequest, + ); + print "$url\n\n"; + print ">>>>Trying to open a netscape window<<<<\n"; + system("netscape","-remote","openURL('$url')"); +} + +1; + +__END__ + +=head1 NAME + + CPAN::Admin - A CPAN Shell for CPAN admins + +=head1 SYNOPSIS + + perl -MCPAN::Admin -e shell + +=head1 STATUS + +Note: this module is currently not maintained. If you need it and fix +it for your needs, please submit patches. + +=head1 DESCRIPTION + +CPAN::Admin is a subclass of CPAN that adds the commands C<register> +and C<modsearch> to the CPAN shell. + +C<register> calls C<get> on the named module, assembles a couple of +informations (description, language), and calls Netscape with the +-remote argument so that a form is filled with all the assembled +informations and the registration can be performed with a single +click. If the command line has more than one argument, register does +not run a C<get>, instead it interprets the rest of the line as DSLI +status, description, and userid and sends them to netscape such that +the form is again mostly filled and can be edited or confirmed with a +single click. CPAN::Admin never performs the submission click for you, +it is only intended to fill in the form on PAUSE and leave the +confirmation to you. + +C<modsearch> simply passes the arguments to the search engine for the +modules@perl.org mailing list at L<http://www.xray.mpe.mpg.de> where all +registration requests are stored. It does so in the same way as +register, namely with the C<netscape -remote> command. + +An experimental feature has also been added, namely to color already +registered modules in listings. If you have L<Term::ANSIColor> installed, +the u, r, and m commands will show already registered modules in +green. + +=head1 PREREQUISITES + +L<URI::Escape>, a browser available in the path, the browser must +understand the -remote switch (as far as I know, this is only +available on UNIX); coloring of registered modules is only available +if L<Term::ANSIColor> is installed. + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Author.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Author.pm new file mode 100644 index 0000000000..572f3ab31d --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Author.pm @@ -0,0 +1,236 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Author; +use strict; + +use CPAN::InfoObj; +@CPAN::Author::ISA = qw(CPAN::InfoObj); +use vars qw( + $VERSION +); +$VERSION = "5.5002"; + +package CPAN::Author; +use strict; + +#-> sub CPAN::Author::force +sub force { + my $self = shift; + $self->{force}++; +} + +#-> sub CPAN::Author::force +sub unforce { + my $self = shift; + delete $self->{force}; +} + +#-> sub CPAN::Author::id +sub id { + my $self = shift; + my $id = $self->{ID}; + $CPAN::Frontend->mydie("Illegal author id[$id]") unless $id =~ /^[A-Z]/; + $id; +} + +#-> sub CPAN::Author::as_glimpse ; +sub as_glimpse { + my($self) = @_; + my(@m); + my $class = ref($self); + $class =~ s/^CPAN:://; + push @m, sprintf(qq{%-15s %s ("%s" <%s>)\n}, + $class, + $self->{ID}, + $self->fullname, + $self->email); + join "", @m; +} + +#-> sub CPAN::Author::fullname ; +sub fullname { + shift->ro->{FULLNAME}; +} +*name = \&fullname; + +#-> sub CPAN::Author::email ; +sub email { shift->ro->{EMAIL}; } + +#-> sub CPAN::Author::ls ; +sub ls { + my $self = shift; + my $glob = shift || ""; + my $silent = shift || 0; + my $id = $self->id; + + # adapted from CPAN::Distribution::verifyCHECKSUM ; + my(@csf); # chksumfile + @csf = $self->id =~ /(.)(.)(.*)/; + $csf[1] = join "", @csf[0,1]; + $csf[2] = join "", @csf[1,2]; # ("A","AN","ANDK") + my(@dl); + @dl = $self->dir_listing([$csf[0],"CHECKSUMS"], 0, 1); + unless (grep {$_->[2] eq $csf[1]} @dl) { + $CPAN::Frontend->myprint("Directory $csf[1]/ does not exist\n") unless $silent ; + return; + } + @dl = $self->dir_listing([@csf[0,1],"CHECKSUMS"], 0, 1); + unless (grep {$_->[2] eq $csf[2]} @dl) { + $CPAN::Frontend->myprint("Directory $id/ does not exist\n") unless $silent; + return; + } + @dl = $self->dir_listing([@csf,"CHECKSUMS"], 1, 1); + if ($glob) { + if ($CPAN::META->has_inst("Text::Glob")) { + $glob =~ s|/$|/*|; + my $rglob = Text::Glob::glob_to_regex($glob); + CPAN->debug("glob[$glob]rglob[$rglob]dl[@dl]") if $CPAN::DEBUG; + my @tmpdl = grep { $_->[2] =~ /$rglob/ } @dl; + if (1==@tmpdl && $tmpdl[0][0]==0) { + $rglob = Text::Glob::glob_to_regex("$glob/*"); + @dl = grep { $_->[2] =~ /$rglob/ } @dl; + } else { + @dl = @tmpdl; + } + CPAN->debug("rglob[$rglob]dl[@dl]") if $CPAN::DEBUG; + } else { + $CPAN::Frontend->mydie("Text::Glob not installed, cannot proceed"); + } + } + unless ($silent >= 2) { + $CPAN::Frontend->myprint + ( + join "", + map { + sprintf + ( + "%8d %10s %s/%s%s\n", + $_->[0], + $_->[1], + $id, + $_->[2], + 0==$_->[0]?"/":"", + ) + } sort { $a->[2] cmp $b->[2] } @dl + ); + } + @dl; +} + +# returns an array of arrays, the latter contain (size,mtime,filename) +#-> sub CPAN::Author::dir_listing ; +sub dir_listing { + my $self = shift; + my $chksumfile = shift; + my $recursive = shift; + my $may_ftp = shift; + + my $lc_want = + File::Spec->catfile($CPAN::Config->{keep_source_where}, + "authors", "id", @$chksumfile); + + my $fh; + + CPAN->debug("chksumfile[@$chksumfile]recursive[$recursive]may_ftp[$may_ftp]") if $CPAN::DEBUG; + # Purge and refetch old (pre-PGP) CHECKSUMS; they are a security + # hazard. (Without GPG installed they are not that much better, + # though.) + $fh = FileHandle->new; + if (open($fh, $lc_want)) { + my $line = <$fh>; close $fh; + unlink($lc_want) unless $line =~ /PGP/; + } + + local($") = "/"; + # connect "force" argument with "index_expire". + my $force = $self->{force}; + if (my @stat = stat $lc_want) { + $force ||= $stat[9] + $CPAN::Config->{index_expire}*86400 <= time; + } + my $lc_file; + if ($may_ftp) { + $lc_file = eval { + CPAN::FTP->localize + ( + "authors/id/@$chksumfile", + $lc_want, + $force, + ); + }; + unless ($lc_file) { + $CPAN::Frontend->myprint("Trying $lc_want.gz\n"); + $chksumfile->[-1] .= ".gz"; + $lc_file = eval { + CPAN::FTP->localize + ("authors/id/@$chksumfile", + "$lc_want.gz", + 1, + ); + }; + if ($lc_file) { + $lc_file =~ s{\.gz(?!\n)\Z}{}; #}; + eval{CPAN::Tarzip->new("$lc_file.gz")->gunzip($lc_file)}; + } else { + return; + } + } + } else { + $lc_file = $lc_want; + # we *could* second-guess and if the user has a file: URL, + # then we could look there. But on the other hand, if they do + # have a file: URL, why did they choose to set + # $CPAN::Config->{show_upload_date} to false? + } + + # adapted from CPAN::Distribution::CHECKSUM_check_file ; + $fh = FileHandle->new; + my($cksum); + if (open $fh, $lc_file) { + local($/); + my $eval = <$fh>; + $eval =~ s/\015?\012/\n/g; + close $fh; + my($compmt) = Safe->new(); + $cksum = $compmt->reval($eval); + if ($@) { + rename $lc_file, "$lc_file.bad"; + Carp::confess($@) if $@; + } + } elsif ($may_ftp) { + Carp::carp ("Could not open '$lc_file' for reading."); + } else { + # Maybe should warn: "You may want to set show_upload_date to a true value" + return; + } + my(@result,$f); + for $f (sort keys %$cksum) { + if (exists $cksum->{$f}{isdir}) { + if ($recursive) { + my(@dir) = @$chksumfile; + pop @dir; + push @dir, $f, "CHECKSUMS"; + push @result, [ 0, "-", $f ]; + push @result, map { + [$_->[0], $_->[1], "$f/$_->[2]"] + } $self->dir_listing(\@dir,1,$may_ftp); + } else { + push @result, [ 0, "-", $f ]; + } + } else { + push @result, [ + ($cksum->{$f}{"size"}||0), + $cksum->{$f}{"mtime"}||"---", + $f + ]; + } + } + @result; +} + +#-> sub CPAN::Author::reports +sub reports { + $CPAN::Frontend->mywarn("reports on authors not implemented. +Please file a bugreport if you need this.\n"); +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Bundle.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Bundle.pm new file mode 100644 index 0000000000..99c95ac4d6 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Bundle.pm @@ -0,0 +1,306 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Bundle; +use strict; +use CPAN::Module; +@CPAN::Bundle::ISA = qw(CPAN::Module); + +use vars qw( + $VERSION +); +$VERSION = "5.5005"; + +sub look { + my $self = shift; + $CPAN::Frontend->myprint($self->as_string); +} + +#-> CPAN::Bundle::undelay +sub undelay { + my $self = shift; + delete $self->{later}; + for my $c ( $self->contains ) { + my $obj = CPAN::Shell->expandany($c) or next; + if ($obj->id eq $self->id){ + my $id = $obj->id; + $CPAN::Frontend->mywarn("$id seems to contain itself, skipping\n"); + next; + } + $obj->undelay; + } +} + +# mark as dirty/clean +#-> sub CPAN::Bundle::color_cmd_tmps ; +sub color_cmd_tmps { + my($self) = shift; + my($depth) = shift || 0; + my($color) = shift || 0; + my($ancestors) = shift || []; + # a module needs to recurse to its cpan_file, a distribution needs + # to recurse into its prereq_pms, a bundle needs to recurse into its modules + + return if exists $self->{incommandcolor} + && $color==1 + && $self->{incommandcolor}==$color; + if ($depth>=$CPAN::MAX_RECURSION) { + my $e = CPAN::Exception::RecursiveDependency->new($ancestors); + if ($e->is_resolvable) { + return $self->{incommandcolor}=2; + } else { + die $e; + } + } + # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; + + for my $c ( $self->contains ) { + my $obj = CPAN::Shell->expandany($c) or next; + CPAN->debug("c[$c]obj[$obj]") if $CPAN::DEBUG; + $obj->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); + } + # never reached code? + #if ($color==0) { + #delete $self->{badtestcnt}; + #} + $self->{incommandcolor} = $color; +} + +#-> sub CPAN::Bundle::as_string ; +sub as_string { + my($self) = @_; + $self->contains; + # following line must be "=", not "||=" because we have a moving target + $self->{INST_VERSION} = $self->inst_version; + return $self->SUPER::as_string; +} + +#-> sub CPAN::Bundle::contains ; +sub contains { + my($self) = @_; + my($inst_file) = $self->inst_file || ""; + my($id) = $self->id; + $self->debug("inst_file[$inst_file]id[$id]") if $CPAN::DEBUG; + if ($inst_file && CPAN::Version->vlt($self->inst_version,$self->cpan_version)) { + undef $inst_file; + } + unless ($inst_file) { + # Try to get at it in the cpan directory + $self->debug("no inst_file") if $CPAN::DEBUG; + my $cpan_file; + $CPAN::Frontend->mydie("I don't know a bundle with ID '$id'\n") unless + $cpan_file = $self->cpan_file; + if ($cpan_file eq "N/A") { + $CPAN::Frontend->mywarn("Bundle '$id' not found on disk and not on CPAN. Maybe stale symlink? Maybe removed during session?\n"); + return; + } + my $dist = $CPAN::META->instance('CPAN::Distribution', + $self->cpan_file); + $self->debug("before get id[$dist->{ID}]") if $CPAN::DEBUG; + $dist->get; + $self->debug("after get id[$dist->{ID}]") if $CPAN::DEBUG; + my($todir) = $CPAN::Config->{'cpan_home'}; + my(@me,$from,$to,$me); + @me = split /::/, $self->id; + $me[-1] .= ".pm"; + $me = File::Spec->catfile(@me); + my $build_dir; + unless ($build_dir = $dist->{build_dir}) { + $CPAN::Frontend->mywarn("Warning: cannot determine bundle content without a build_dir.\n"); + return; + } + $from = $self->find_bundle_file($build_dir,join('/',@me)); + $to = File::Spec->catfile($todir,$me); + File::Path::mkpath(File::Basename::dirname($to)); + File::Copy::copy($from, $to) + or Carp::confess("Couldn't copy $from to $to: $!"); + $inst_file = $to; + } + my @result; + my $fh = FileHandle->new; + local $/ = "\n"; + open($fh,$inst_file) or die "Could not open '$inst_file': $!"; + my $in_cont = 0; + $self->debug("inst_file[$inst_file]") if $CPAN::DEBUG; + while (<$fh>) { + $in_cont = m/^=(?!head1\s+(?i-xsm:CONTENTS))/ ? 0 : + m/^=head1\s+(?i-xsm:CONTENTS)/ ? 1 : $in_cont; + next unless $in_cont; + next if /^=/; + s/\#.*//; + next if /^\s+$/; + chomp; + push @result, (split " ", $_, 2)[0]; + } + close $fh; + delete $self->{STATUS}; + $self->{CONTAINS} = \@result; + $self->debug("CONTAINS[@result]") if $CPAN::DEBUG; + unless (@result) { + $CPAN::Frontend->mywarn(qq{ +The bundle file "$inst_file" may be a broken +bundlefile. It seems not to contain any bundle definition. +Please check the file and if it is bogus, please delete it. +Sorry for the inconvenience. +}); + } + @result; +} + +#-> sub CPAN::Bundle::find_bundle_file +# $where is in local format, $what is in unix format +sub find_bundle_file { + my($self,$where,$what) = @_; + $self->debug("where[$where]what[$what]") if $CPAN::DEBUG; +### The following two lines let CPAN.pm become Bundle/CPAN.pm :-( +### my $bu = File::Spec->catfile($where,$what); +### return $bu if -f $bu; + my $manifest = File::Spec->catfile($where,"MANIFEST"); + unless (-f $manifest) { + require ExtUtils::Manifest; + my $cwd = CPAN::anycwd(); + $self->safe_chdir($where); + ExtUtils::Manifest::mkmanifest(); + $self->safe_chdir($cwd); + } + my $fh = FileHandle->new($manifest) + or Carp::croak("Couldn't open $manifest: $!"); + local($/) = "\n"; + my $bundle_filename = $what; + $bundle_filename =~ s|Bundle.*/||; + my $bundle_unixpath; + while (<$fh>) { + next if /^\s*\#/; + my($file) = /(\S+)/; + if ($file =~ m|\Q$what\E$|) { + $bundle_unixpath = $file; + # return File::Spec->catfile($where,$bundle_unixpath); # bad + last; + } + # retry if she managed to have no Bundle directory + $bundle_unixpath = $file if $file =~ m|\Q$bundle_filename\E$|; + } + return File::Spec->catfile($where, split /\//, $bundle_unixpath) + if $bundle_unixpath; + Carp::croak("Couldn't find a Bundle file in $where"); +} + +# needs to work quite differently from Module::inst_file because of +# cpan_home/Bundle/ directory and the possibility that we have +# shadowing effect. As it makes no sense to take the first in @INC for +# Bundles, we parse them all for $VERSION and take the newest. + +#-> sub CPAN::Bundle::inst_file ; +sub inst_file { + my($self) = @_; + my($inst_file); + my(@me); + @me = split /::/, $self->id; + $me[-1] .= ".pm"; + my($incdir,$bestv); + foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) { + my $parsefile = File::Spec->catfile($incdir, @me); + CPAN->debug("parsefile[$parsefile]") if $CPAN::DEBUG; + next unless -f $parsefile; + my $have = eval { MM->parse_version($parsefile); }; + if ($@) { + $CPAN::Frontend->mywarn("Error while parsing version number in file '$parsefile'\n"); + } + if (!$bestv || CPAN::Version->vgt($have,$bestv)) { + $self->{INST_FILE} = $parsefile; + $self->{INST_VERSION} = $bestv = $have; + } + } + $self->{INST_FILE}; +} + +#-> sub CPAN::Bundle::inst_version ; +sub inst_version { + my($self) = @_; + $self->inst_file; # finds INST_VERSION as side effect + $self->{INST_VERSION}; +} + +#-> sub CPAN::Bundle::rematein ; +sub rematein { + my($self,$meth) = @_; + $self->debug("self[$self] meth[$meth]") if $CPAN::DEBUG; + my($id) = $self->id; + Carp::croak( "Can't $meth $id, don't have an associated bundle file. :-(\n" ) + unless $self->inst_file || $self->cpan_file; + my($s,%fail); + for $s ($self->contains) { + my($type) = $s =~ m|/| ? 'CPAN::Distribution' : + $s =~ m|^Bundle::| ? 'CPAN::Bundle' : 'CPAN::Module'; + if ($type eq 'CPAN::Distribution') { + $CPAN::Frontend->mywarn(qq{ +The Bundle }.$self->id.qq{ contains +explicitly a file '$s'. +Going to $meth that. +}); + $CPAN::Frontend->mysleep(5); + } + # possibly noisy action: + $self->debug("type[$type] s[$s]") if $CPAN::DEBUG; + my $obj = $CPAN::META->instance($type,$s); + $obj->{reqtype} = $self->{reqtype}; + $obj->{viabundle} ||= { id => $id, reqtype => $self->{reqtype}, optional => !$self->{mandatory}}; + # $obj->$meth(); + # XXX should optional be based on whether bundle was optional? -- xdg, 2012-04-01 + # A: Sure, what could demand otherwise? --andk, 2013-11-25 + CPAN::Queue->queue_item(qmod => $obj->id, reqtype => $self->{reqtype}, optional => !$self->{mandatory}); + } +} + +# If a bundle contains another that contains an xs_file we have here, +# we just don't bother I suppose +#-> sub CPAN::Bundle::xs_file +sub xs_file { + return 0; +} + +#-> sub CPAN::Bundle::force ; +sub fforce { shift->rematein('fforce',@_); } +#-> sub CPAN::Bundle::force ; +sub force { shift->rematein('force',@_); } +#-> sub CPAN::Bundle::notest ; +sub notest { shift->rematein('notest',@_); } +#-> sub CPAN::Bundle::get ; +sub get { shift->rematein('get',@_); } +#-> sub CPAN::Bundle::make ; +sub make { shift->rematein('make',@_); } +#-> sub CPAN::Bundle::test ; +sub test { + my $self = shift; + # $self->{badtestcnt} ||= 0; + $self->rematein('test',@_); +} +#-> sub CPAN::Bundle::install ; +sub install { + my $self = shift; + $self->rematein('install',@_); +} +#-> sub CPAN::Bundle::clean ; +sub clean { shift->rematein('clean',@_); } + +#-> sub CPAN::Bundle::uptodate ; +sub uptodate { + my($self) = @_; + return 0 unless $self->SUPER::uptodate; # we must have the current Bundle def + my $c; + foreach $c ($self->contains) { + my $obj = CPAN::Shell->expandany($c); + return 0 unless $obj->uptodate; + } + return 1; +} + +#-> sub CPAN::Bundle::readme ; +sub readme { + my($self) = @_; + my($file) = $self->cpan_file or $CPAN::Frontend->myprint(qq{ +No File found for bundle } . $self->id . qq{\n}), return; + $self->debug("self[$self] file[$file]") if $CPAN::DEBUG; + $CPAN::META->instance('CPAN::Distribution',$file)->readme; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/CacheMgr.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/CacheMgr.pm new file mode 100644 index 0000000000..144efd62b3 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/CacheMgr.pm @@ -0,0 +1,249 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::CacheMgr; +use strict; +use CPAN::InfoObj; +@CPAN::CacheMgr::ISA = qw(CPAN::InfoObj CPAN); +use Cwd qw(chdir); +use File::Find; + +use vars qw( + $VERSION +); +$VERSION = "5.5002"; + +package CPAN::CacheMgr; +use strict; + +#-> sub CPAN::CacheMgr::as_string ; +sub as_string { + eval { require Data::Dumper }; + if ($@) { + return shift->SUPER::as_string; + } else { + return Data::Dumper::Dumper(shift); + } +} + +#-> sub CPAN::CacheMgr::cachesize ; +sub cachesize { + shift->{DU}; +} + +#-> sub CPAN::CacheMgr::tidyup ; +sub tidyup { + my($self) = @_; + return unless $CPAN::META->{LOCK}; + return unless -d $self->{ID}; + my @toremove = grep { $self->{SIZE}{$_}==0 } @{$self->{FIFO}}; + for my $current (0..$#toremove) { + my $toremove = $toremove[$current]; + $CPAN::Frontend->myprint(sprintf( + "DEL(%d/%d): %s \n", + $current+1, + scalar @toremove, + $toremove, + ) + ); + return if $CPAN::Signal; + $self->_clean_cache($toremove); + return if $CPAN::Signal; + } + $self->{FIFO} = []; +} + +#-> sub CPAN::CacheMgr::dir ; +sub dir { + shift->{ID}; +} + +#-> sub CPAN::CacheMgr::entries ; +sub entries { + my($self,$dir) = @_; + return unless defined $dir; + $self->debug("reading dir[$dir]") if $CPAN::DEBUG; + $dir ||= $self->{ID}; + my($cwd) = CPAN::anycwd(); + chdir $dir or Carp::croak("Can't chdir to $dir: $!"); + my $dh = DirHandle->new(File::Spec->curdir) + or Carp::croak("Couldn't opendir $dir: $!"); + my(@entries); + for ($dh->read) { + next if $_ eq "." || $_ eq ".."; + if (-f $_) { + push @entries, File::Spec->catfile($dir,$_); + } elsif (-d _) { + push @entries, File::Spec->catdir($dir,$_); + } else { + $CPAN::Frontend->mywarn("Warning: weird direntry in $dir: $_\n"); + } + } + chdir $cwd or Carp::croak("Can't chdir to $cwd: $!"); + sort { -M $a <=> -M $b} @entries; +} + +#-> sub CPAN::CacheMgr::disk_usage ; +sub disk_usage { + my($self,$dir,$fast) = @_; + return if exists $self->{SIZE}{$dir}; + return if $CPAN::Signal; + my($Du) = 0; + if (-e $dir) { + if (-d $dir) { + unless (-x $dir) { + unless (chmod 0755, $dir) { + $CPAN::Frontend->mywarn("I have neither the -x permission nor the ". + "permission to change the permission; cannot ". + "estimate disk usage of '$dir'\n"); + $CPAN::Frontend->mysleep(5); + return; + } + } + } elsif (-f $dir) { + # nothing to say, no matter what the permissions + } + } else { + $CPAN::Frontend->mywarn("File or directory '$dir' has gone, ignoring\n"); + return; + } + if ($fast) { + $Du = 0; # placeholder + } else { + find( + sub { + $File::Find::prune++ if $CPAN::Signal; + return if -l $_; + if ($^O eq 'MacOS') { + require Mac::Files; + my $cat = Mac::Files::FSpGetCatInfo($_); + $Du += $cat->ioFlLgLen() + $cat->ioFlRLgLen() if $cat; + } else { + if (-d _) { + unless (-x _) { + unless (chmod 0755, $_) { + $CPAN::Frontend->mywarn("I have neither the -x permission nor ". + "the permission to change the permission; ". + "can only partially estimate disk usage ". + "of '$_'\n"); + $CPAN::Frontend->mysleep(5); + return; + } + } + } else { + $Du += (-s _); + } + } + }, + $dir + ); + } + return if $CPAN::Signal; + $self->{SIZE}{$dir} = $Du/1024/1024; + unshift @{$self->{FIFO}}, $dir; + $self->debug("measured $dir is $Du") if $CPAN::DEBUG; + $self->{DU} += $Du/1024/1024; + $self->{DU}; +} + +#-> sub CPAN::CacheMgr::_clean_cache ; +sub _clean_cache { + my($self,$dir) = @_; + return unless -e $dir; + unless (File::Spec->canonpath(File::Basename::dirname($dir)) + eq File::Spec->canonpath($CPAN::Config->{build_dir})) { + $CPAN::Frontend->mywarn("Directory '$dir' not below $CPAN::Config->{build_dir}, ". + "will not remove\n"); + $CPAN::Frontend->mysleep(5); + return; + } + $self->debug("have to rmtree $dir, will free $self->{SIZE}{$dir}") + if $CPAN::DEBUG; + File::Path::rmtree($dir); + my $id_deleted = 0; + if ($dir !~ /\.yml$/ && -f "$dir.yml") { + my $yaml_module = CPAN::_yaml_module(); + if ($CPAN::META->has_inst($yaml_module)) { + my($peek_yaml) = eval { CPAN->_yaml_loadfile("$dir.yml"); }; + if ($@) { + $CPAN::Frontend->mywarn("(parse error on '$dir.yml' removing anyway)"); + unlink "$dir.yml" or + $CPAN::Frontend->mywarn("(Could not unlink '$dir.yml': $!)"); + return; + } elsif (my $id = $peek_yaml->[0]{distribution}{ID}) { + $CPAN::META->delete("CPAN::Distribution", $id); + + # XXX we should restore the state NOW, otherwise this + # distro does not exist until we read an index. BUG ALERT(?) + + # $CPAN::Frontend->mywarn (" +++\n"); + $id_deleted++; + } + } + unlink "$dir.yml"; # may fail + unless ($id_deleted) { + CPAN->debug("no distro found associated with '$dir'"); + } + } + $self->{DU} -= $self->{SIZE}{$dir}; + delete $self->{SIZE}{$dir}; +} + +#-> sub CPAN::CacheMgr::new ; +sub new { + my($class,$phase) = @_; + $phase ||= "atstart"; + my $time = time; + my($debug,$t2); + $debug = ""; + my $self = { + ID => $CPAN::Config->{build_dir}, + MAX => $CPAN::Config->{'build_cache'}, + SCAN => $CPAN::Config->{'scan_cache'} || 'atstart', + DU => 0 + }; + $CPAN::Frontend->mydie("Unknown scan_cache argument: $self->{SCAN}") + unless $self->{SCAN} =~ /never|atstart|atexit/; + File::Path::mkpath($self->{ID}); + my $dh = DirHandle->new($self->{ID}); + bless $self, $class; + $self->scan_cache($phase); + $t2 = time; + $debug .= "timing of CacheMgr->new: ".($t2 - $time); + $time = $t2; + CPAN->debug($debug) if $CPAN::DEBUG; + $self; +} + +#-> sub CPAN::CacheMgr::scan_cache ; +sub scan_cache { + my ($self, $phase) = @_; + $phase = '' unless defined $phase; + return unless $phase eq $self->{SCAN}; + return unless $CPAN::META->{LOCK}; + $CPAN::Frontend->myprint( + sprintf("Scanning cache %s for sizes\n", + $self->{ID})); + my $e; + my @entries = $self->entries($self->{ID}); + my $i = 0; + my $painted = 0; + for $e (@entries) { + my $symbol = "."; + if ($self->{DU} > $self->{MAX}) { + $symbol = "-"; + $self->disk_usage($e,1); + } else { + $self->disk_usage($e); + } + $i++; + while (($painted/76) < ($i/@entries)) { + $CPAN::Frontend->myprint($symbol); + $painted++; + } + return if $CPAN::Signal; + } + $CPAN::Frontend->myprint("DONE\n"); + $self->tidyup; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Complete.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Complete.pm new file mode 100644 index 0000000000..588e6e6c2c --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Complete.pm @@ -0,0 +1,175 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Complete; +use strict; +@CPAN::Complete::ISA = qw(CPAN::Debug); +# Q: where is the "How do I add a new command" HOWTO? +# A: git log -p -1 355c44e9caaec857e4b12f51afb96498833c3e36 where andk added the report command +@CPAN::Complete::COMMANDS = sort qw( + ? ! a b d h i m o q r u + autobundle + bye + clean + cvs_import + dump + exit + failed + force + fforce + hosts + install + install_tested + is_tested + look + ls + make + mkmyconfig + notest + perldoc + quit + readme + recent + recompile + reload + report + reports + scripts + smoke + test + upgrade +); + +use vars qw( + $VERSION +); +$VERSION = "5.5001"; + +package CPAN::Complete; +use strict; + +sub gnu_cpl { + my($text, $line, $start, $end) = @_; + my(@perlret) = cpl($text, $line, $start); + # find longest common match. Can anybody show me how to peruse + # T::R::Gnu to have this done automatically? Seems expensive. + return () unless @perlret; + my($newtext) = $text; + for (my $i = length($text)+1;;$i++) { + last unless length($perlret[0]) && length($perlret[0]) >= $i; + my $try = substr($perlret[0],0,$i); + my @tries = grep {substr($_,0,$i) eq $try} @perlret; + # warn "try[$try]tries[@tries]"; + if (@tries == @perlret) { + $newtext = $try; + } else { + last; + } + } + ($newtext,@perlret); +} + +#-> sub CPAN::Complete::cpl ; +sub cpl { + my($word,$line,$pos) = @_; + $word ||= ""; + $line ||= ""; + $pos ||= 0; + CPAN->debug("word [$word] line[$line] pos[$pos]") if $CPAN::DEBUG; + $line =~ s/^\s*//; + if ($line =~ s/^((?:notest|f?force)\s*)//) { + $pos -= length($1); + } + my @return; + if ($pos == 0 || $line =~ /^(?:h(?:elp)?|\?)\s/) { + @return = grep /^\Q$word\E/, @CPAN::Complete::COMMANDS; + } elsif ( $line !~ /^[\!abcdghimorutl]/ ) { + @return = (); + } elsif ($line =~ /^a\s/) { + @return = cplx('CPAN::Author',uc($word)); + } elsif ($line =~ /^ls\s/) { + my($author,$rest) = $word =~ m|([^/]+)/?(.*)|; + @return = $rest ? () : map {"$_/"} cplx('CPAN::Author',uc($author||"")); + if (0 && 1==@return) { # XXX too slow and even wrong when there is a * already + @return = grep /^\Q$word\E/, map {"$author/$_->[2]"} CPAN::Shell->expand("Author",$author)->ls("$rest*","2"); + } + } elsif ($line =~ /^b\s/) { + CPAN::Shell->local_bundles; + @return = cplx('CPAN::Bundle',$word); + } elsif ($line =~ /^d\s/) { + @return = cplx('CPAN::Distribution',$word); + } elsif ($line =~ m/^( + [mru]|make|clean|dump|get|test|install|readme|look|cvs_import|perldoc|recent + )\s/x ) { + if ($word =~ /^Bundle::/) { + CPAN::Shell->local_bundles; + } + @return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word)); + } elsif ($line =~ /^i\s/) { + @return = cpl_any($word); + } elsif ($line =~ /^reload\s/) { + @return = cpl_reload($word,$line,$pos); + } elsif ($line =~ /^o\s/) { + @return = cpl_option($word,$line,$pos); + } elsif ($line =~ m/^\S+\s/ ) { + # fallback for future commands and what we have forgotten above + @return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word)); + } else { + @return = (); + } + return @return; +} + +#-> sub CPAN::Complete::cplx ; +sub cplx { + my($class, $word) = @_; + if (CPAN::_sqlite_running()) { + $CPAN::SQLite->search($class, "^\Q$word\E"); + } + my $method = "id"; + $method = "pretty_id" if $class eq "CPAN::Distribution"; + sort grep /^\Q$word\E/, map { $_->$method() } $CPAN::META->all_objects($class); +} + +#-> sub CPAN::Complete::cpl_any ; +sub cpl_any { + my($word) = shift; + return ( + cplx('CPAN::Author',$word), + cplx('CPAN::Bundle',$word), + cplx('CPAN::Distribution',$word), + cplx('CPAN::Module',$word), + ); +} + +#-> sub CPAN::Complete::cpl_reload ; +sub cpl_reload { + my($word,$line,$pos) = @_; + $word ||= ""; + my(@words) = split " ", $line; + CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; + my(@ok) = qw(cpan index); + return @ok if @words == 1; + return grep /^\Q$word\E/, @ok if @words == 2 && $word; +} + +#-> sub CPAN::Complete::cpl_option ; +sub cpl_option { + my($word,$line,$pos) = @_; + $word ||= ""; + my(@words) = split " ", $line; + CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; + my(@ok) = qw(conf debug); + return @ok if @words == 1; + return grep /^\Q$word\E/, @ok if @words == 2 && length($word); + if (0) { + } elsif ($words[1] eq 'index') { + return (); + } elsif ($words[1] eq 'conf') { + return CPAN::HandleConfig::cpl(@_); + } elsif ($words[1] eq 'debug') { + return sort grep /^\Q$word\E/i, + sort keys %CPAN::DEBUG, 'all'; + } +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Debug.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Debug.pm new file mode 100644 index 0000000000..48e394bd41 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Debug.pm @@ -0,0 +1,83 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +package CPAN::Debug; +use strict; +use vars qw($VERSION); + +$VERSION = "5.5001"; +# module is internal to CPAN.pm + +%CPAN::DEBUG = qw[ + CPAN 1 + Index 2 + InfoObj 4 + Author 8 + Distribution 16 + Bundle 32 + Module 64 + CacheMgr 128 + Complete 256 + FTP 512 + Shell 1024 + Eval 2048 + HandleConfig 4096 + Tarzip 8192 + Version 16384 + Queue 32768 + FirstTime 65536 +]; + +$CPAN::DEBUG ||= 0; + +#-> sub CPAN::Debug::debug ; +sub debug { + my($self,$arg) = @_; + + my @caller; + my $i = 0; + while () { + my(@c) = (caller($i))[0 .. ($i ? 3 : 2)]; + last unless defined $c[0]; + push @caller, \@c; + for (0,3) { + last if $_ > $#c; + $c[$_] =~ s/.*:://; + } + for (1) { + $c[$_] =~ s|.*/||; + } + last if ++$i>=3; + } + pop @caller; + if ($CPAN::DEBUG{$caller[0][0]} & $CPAN::DEBUG) { + if ($arg and ref $arg) { + eval { require Data::Dumper }; + if ($@) { + $CPAN::Frontend->myprint("Debug(\n" . $arg->as_string . ")\n"); + } else { + $CPAN::Frontend->myprint("Debug(\n" . Data::Dumper::Dumper($arg) . ")\n"); + } + } else { + my $outer = ""; + local $" = ","; + if (@caller>1) { + $outer = ",[@{$caller[1]}]"; + } + $CPAN::Frontend->myprint("Debug(@{$caller[0]}$outer): $arg\n"); + } + } +} + +1; + +__END__ + +=head1 NAME + +CPAN::Debug - internal debugging for CPAN.pm + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/DeferredCode.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/DeferredCode.pm new file mode 100644 index 0000000000..0db37a6485 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/DeferredCode.pm @@ -0,0 +1,16 @@ +package CPAN::DeferredCode; + +use strict; +use vars qw/$VERSION/; + +use overload fallback => 1, map { ($_ => 'run') } qw/ + bool "" 0+ +/; + +$VERSION = "5.50"; + +sub run { + $_[0]->(); +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Distribution.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Distribution.pm new file mode 100644 index 0000000000..3412108539 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Distribution.pm @@ -0,0 +1,4862 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Distribution; +use strict; +use Cwd qw(chdir); +use CPAN::Distroprefs; +use CPAN::InfoObj; +use File::Path (); +use POSIX ":sys_wait_h"; +@CPAN::Distribution::ISA = qw(CPAN::InfoObj); +use vars qw($VERSION); +$VERSION = "2.27"; + +my $run_allow_installing_within_test = 1; # boolean; either in test or in install, there is no third option + +# no prepare, because prepare is not a command on the shell command line +# TODO: clear instance cache on reload +my %instance; +for my $method (qw(get make test install)) { + no strict 'refs'; + for my $prefix (qw(pre post)) { + my $hookname = sprintf "%s_%s", $prefix, $method; + *$hookname = sub { + my($self) = @_; + for my $plugin (@{$CPAN::Config->{plugin_list}}) { + my($plugin_proper,$args) = split /=/, $plugin, 2; + $args = "" unless defined $args; + if ($CPAN::META->has_inst($plugin_proper)){ + my @args = split /,/, $args; + $instance{$plugin} ||= $plugin_proper->new(@args); + if ($instance{$plugin}->can($hookname)) { + $instance{$plugin}->$hookname($self); + } + } else { + $CPAN::Frontend->mydie("Plugin '$plugin_proper' not found for hook '$hookname'"); + } + } + }; + } +} + +# Accessors +sub cpan_comment { + my $self = shift; + my $ro = $self->ro or return; + $ro->{CPAN_COMMENT} +} + +#-> CPAN::Distribution::undelay +sub undelay { + my $self = shift; + for my $delayer ( + "configure_requires_later", + "configure_requires_later_for", + "later", + "later_for", + ) { + delete $self->{$delayer}; + } +} + +#-> CPAN::Distribution::is_dot_dist +sub is_dot_dist { + my($self) = @_; + return substr($self->id,-1,1) eq "."; +} + +# add the A/AN/ stuff +#-> CPAN::Distribution::normalize +sub normalize { + my($self,$s) = @_; + $s = $self->id unless defined $s; + if (substr($s,-1,1) eq ".") { + # using a global because we are sometimes called as static method + if (!$CPAN::META->{LOCK} + && !$CPAN::Have_warned->{"$s is unlocked"}++ + ) { + $CPAN::Frontend->mywarn("You are visiting the local directory + '$s' + without lock, take care that concurrent processes do not do likewise.\n"); + $CPAN::Frontend->mysleep(1); + } + if ($s eq ".") { + $s = "$CPAN::iCwd/."; + } elsif (File::Spec->file_name_is_absolute($s)) { + } elsif (File::Spec->can("rel2abs")) { + $s = File::Spec->rel2abs($s); + } else { + $CPAN::Frontend->mydie("Your File::Spec is too old, please upgrade File::Spec"); + } + CPAN->debug("s[$s]") if $CPAN::DEBUG; + unless ($CPAN::META->exists("CPAN::Distribution", $s)) { + for ($CPAN::META->instance("CPAN::Distribution", $s)) { + $_->{build_dir} = $s; + $_->{archived} = "local_directory"; + $_->{unwrapped} = CPAN::Distrostatus->new("YES -- local_directory"); + } + } + } elsif ( + $s =~ tr|/|| == 1 + or + $s !~ m|[A-Z]/[A-Z-0-9]{2}/[A-Z-0-9]{2,}/| + ) { + return $s if $s =~ m:^N/A|^Contact Author: ; + $s =~ s|^(.)(.)([^/]*/)(.+)$|$1/$1$2/$1$2$3$4|; + CPAN->debug("s[$s]") if $CPAN::DEBUG; + } + $s; +} + +#-> sub CPAN::Distribution::author ; +sub author { + my($self) = @_; + my($authorid); + if (substr($self->id,-1,1) eq ".") { + $authorid = "LOCAL"; + } else { + ($authorid) = $self->pretty_id =~ /^([\w\-]+)/; + } + CPAN::Shell->expand("Author",$authorid); +} + +# tries to get the yaml from CPAN instead of the distro itself: +# EXPERIMENTAL, UNDOCUMENTED AND UNTESTED, for Tels +sub fast_yaml { + my($self) = @_; + my $meta = $self->pretty_id; + $meta =~ s/\.(tar.gz|tgz|zip|tar.bz2)/.meta/; + my(@ls) = CPAN::Shell->globls($meta); + my $norm = $self->normalize($meta); + + my($local_file); + my($local_wanted) = + File::Spec->catfile( + $CPAN::Config->{keep_source_where}, + "authors", + "id", + split(/\//,$norm) + ); + $self->debug("Doing localize") if $CPAN::DEBUG; + unless ($local_file = + CPAN::FTP->localize("authors/id/$norm", + $local_wanted)) { + $CPAN::Frontend->mydie("Giving up on downloading yaml file '$local_wanted'\n"); + } + my $yaml = CPAN->_yaml_loadfile($local_file)->[0]; +} + +#-> sub CPAN::Distribution::cpan_userid +sub cpan_userid { + my $self = shift; + if ($self->{ID} =~ m{[A-Z]/[A-Z\-]{2}/([A-Z\-]+)/}) { + return $1; + } + return $self->SUPER::cpan_userid; +} + +#-> sub CPAN::Distribution::pretty_id +sub pretty_id { + my $self = shift; + my $id = $self->id; + return $id unless $id =~ m|^./../|; + substr($id,5); +} + +#-> sub CPAN::Distribution::base_id +sub base_id { + my $self = shift; + my $id = $self->pretty_id(); + my $base_id = File::Basename::basename($id); + $base_id =~ s{\.(?:tar\.(bz2|gz|Z)|t(?:gz|bz)|zip)$}{}i; + return $base_id; +} + +#-> sub CPAN::Distribution::tested_ok_but_not_installed +sub tested_ok_but_not_installed { + my $self = shift; + return ( + $self->{make_test} + && $self->{build_dir} + && (UNIVERSAL::can($self->{make_test},"failed") ? + ! $self->{make_test}->failed : + $self->{make_test} =~ /^YES/ + ) + && ( + !$self->{install} + || + $self->{install}->failed + ) + ); +} + + +# mark as dirty/clean for the sake of recursion detection. $color=1 +# means "in use", $color=0 means "not in use anymore". $color=2 means +# we have determined prereqs now and thus insist on passing this +# through (at least) once again. + +#-> sub CPAN::Distribution::color_cmd_tmps ; +sub color_cmd_tmps { + my($self) = shift; + my($depth) = shift || 0; + my($color) = shift || 0; + my($ancestors) = shift || []; + # a distribution needs to recurse into its prereq_pms + $self->debug("color_cmd_tmps[$depth,$color,@$ancestors]") if $CPAN::DEBUG; + + return if exists $self->{incommandcolor} + && $color==1 + && $self->{incommandcolor}==$color; + $CPAN::MAX_RECURSION||=0; # silence 'once' warnings + if ($depth>=$CPAN::MAX_RECURSION) { + my $e = CPAN::Exception::RecursiveDependency->new($ancestors); + if ($e->is_resolvable) { + return $self->{incommandcolor}=2; + } else { + die $e; + } + } + # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; + my $prereq_pm = $self->prereq_pm; + if (defined $prereq_pm) { + # XXX also optional_req & optional_breq? -- xdg, 2012-04-01 + # A: no, optional deps may recurse -- ak, 2014-05-07 + PREREQ: for my $pre (sort( + keys %{$prereq_pm->{requires}||{}}, + keys %{$prereq_pm->{build_requires}||{}}, + )) { + next PREREQ if $pre eq "perl"; + my $premo; + unless ($premo = CPAN::Shell->expand("Module",$pre)) { + $CPAN::Frontend->mywarn("prerequisite module[$pre] not known\n"); + $CPAN::Frontend->mysleep(0.2); + next PREREQ; + } + $premo->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); + } + } + if ($color==0) { + delete $self->{sponsored_mods}; + + # as we are at the end of a command, we'll give up this + # reminder of a broken test. Other commands may test this guy + # again. Maybe 'badtestcnt' should be renamed to + # 'make_test_failed_within_command'? + delete $self->{badtestcnt}; + } + $self->{incommandcolor} = $color; +} + +#-> sub CPAN::Distribution::as_string ; +sub as_string { + my $self = shift; + $self->containsmods; + $self->upload_date; + $self->SUPER::as_string(@_); +} + +#-> sub CPAN::Distribution::containsmods ; +sub containsmods { + my $self = shift; + return sort keys %{$self->{CONTAINSMODS}} if exists $self->{CONTAINSMODS}; + my $dist_id = $self->{ID}; + for my $mod ($CPAN::META->all_objects("CPAN::Module")) { + my $mod_file = $mod->cpan_file or next; + my $mod_id = $mod->{ID} or next; + # warn "mod_file[$mod_file] dist_id[$dist_id] mod_id[$mod_id]"; + # sleep 1; + if ($CPAN::Signal) { + delete $self->{CONTAINSMODS}; + return; + } + $self->{CONTAINSMODS}{$mod_id} = undef if $mod_file eq $dist_id; + } + sort keys %{$self->{CONTAINSMODS}||={}}; +} + +#-> sub CPAN::Distribution::upload_date ; +sub upload_date { + my $self = shift; + return $self->{UPLOAD_DATE} if exists $self->{UPLOAD_DATE}; + my(@local_wanted) = split(/\//,$self->id); + my $filename = pop @local_wanted; + push @local_wanted, "CHECKSUMS"; + my $author = CPAN::Shell->expand("Author",$self->cpan_userid); + return unless $author; + my @dl = $author->dir_listing(\@local_wanted,0,$CPAN::Config->{show_upload_date}); + return unless @dl; + my($dirent) = grep { $_->[2] eq $filename } @dl; + # warn sprintf "dirent[%s]id[%s]", $dirent, $self->id; + return unless $dirent->[1]; + return $self->{UPLOAD_DATE} = $dirent->[1]; +} + +#-> sub CPAN::Distribution::uptodate ; +sub uptodate { + my($self) = @_; + my $c; + foreach $c ($self->containsmods) { + my $obj = CPAN::Shell->expandany($c); + unless ($obj->uptodate) { + my $id = $self->pretty_id; + $self->debug("$id not uptodate due to $c") if $CPAN::DEBUG; + return 0; + } + } + return 1; +} + +#-> sub CPAN::Distribution::called_for ; +sub called_for { + my($self,$id) = @_; + $self->{CALLED_FOR} = $id if defined $id; + return $self->{CALLED_FOR}; +} + +#-> sub CPAN::Distribution::shortcut_get ; +# return values: undef means don't shortcut; 0 means shortcut as fail; +# and 1 means shortcut as success +sub shortcut_get { + my ($self) = @_; + + if (exists $self->{cleanup_after_install_done}) { + if ($self->{force_update}) { + delete $self->{cleanup_after_install_done}; + } else { + my $id = $self->{CALLED_FOR} || $self->pretty_id; + return $self->success( + "Has already been *installed and cleaned up in the staging area* within this session, will not work on it again; if you really want to start over, try something like `force get $id`" + ); + } + } + + if (my $why = $self->check_disabled) { + $self->{unwrapped} = CPAN::Distrostatus->new("NO $why"); + # XXX why is this goodbye() instead of just print/warn? + # Alternatively, should other print/warns here be goodbye()? + # -- xdg, 2012-04-05 + return $self->goodbye("[disabled] -- NA $why"); + } + + $self->debug("checking already unwrapped[$self->{ID}]") if $CPAN::DEBUG; + if (exists $self->{build_dir} && -d $self->{build_dir}) { + # this deserves print, not warn: + return $self->success("Has already been unwrapped into directory ". + "$self->{build_dir}" + ); + } + + # XXX I'm not sure this should be here because it's not really + # a test for whether get should continue or return; this is + # a side effect -- xdg, 2012-04-05 + $self->debug("checking missing build_dir[$self->{ID}]") if $CPAN::DEBUG; + if (exists $self->{build_dir} && ! -d $self->{build_dir}){ + # we have lost it. + $self->fforce(""); # no method to reset all phases but not set force (dodge) + return undef; # no shortcut + } + + # although we talk about 'force' we shall not test on + # force directly. New model of force tries to refrain from + # direct checking of force. + $self->debug("checking unwrapping error[$self->{ID}]") if $CPAN::DEBUG; + if ( exists $self->{unwrapped} and ( + UNIVERSAL::can($self->{unwrapped},"failed") ? + $self->{unwrapped}->failed : + $self->{unwrapped} =~ /^NO/ ) + ) { + return $self->goodbye("Unwrapping had some problem, won't try again without force"); + } + + return undef; # no shortcut +} + +#-> sub CPAN::Distribution::get ; +sub get { + my($self) = @_; + + $self->pre_get(); + + $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; + if (my $goto = $self->prefs->{goto}) { + $self->post_get(); + return $self->goto($goto); + } + + if ( defined( my $sc = $self->shortcut_get) ) { + $self->post_get(); + return $sc; + } + + local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) + ? $ENV{PERL5LIB} + : ($ENV{PERLLIB} || ""); + local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; + # local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # get + $CPAN::META->set_perl5lib; + local $ENV{MAKEFLAGS}; # protect us from outer make calls + + my $sub_wd = CPAN::anycwd(); # for cleaning up as good as possible + + my($local_file); + # XXX I don't think this check needs to be here, as it + # is already checked in shortcut_get() -- xdg, 2012-04-05 + unless ($self->{build_dir} && -d $self->{build_dir}) { + $self->get_file_onto_local_disk; + if ($CPAN::Signal){ + $self->post_get(); + return; + } + $self->check_integrity; + if ($CPAN::Signal){ + $self->post_get(); + return; + } + (my $packagedir,$local_file) = $self->run_preps_on_packagedir; + # XXX why is this check here? -- xdg, 2012-04-08 + if (exists $self->{writemakefile} && ref $self->{writemakefile} + && $self->{writemakefile}->can("failed") && + $self->{writemakefile}->failed) { + # + $self->post_get(); + return; + } + $packagedir ||= $self->{build_dir}; + $self->{build_dir} = $packagedir; + } + + # XXX should this move up to after run_preps_on_packagedir? + # Otherwise, failing writemakefile can return without + # a $CPAN::Signal check -- xdg, 2012-04-05 + if ($CPAN::Signal) { + $self->safe_chdir($sub_wd); + $self->post_get(); + return; + } + unless ($self->patch){ + $self->post_get(); + return; + } + $self->store_persistent_state; + + $self->post_get(); + + return 1; # success +} + +#-> CPAN::Distribution::get_file_onto_local_disk +sub get_file_onto_local_disk { + my($self) = @_; + + return if $self->is_dot_dist; + my($local_file); + my($local_wanted) = + File::Spec->catfile( + $CPAN::Config->{keep_source_where}, + "authors", + "id", + split(/\//,$self->id) + ); + + $self->debug("Doing localize") if $CPAN::DEBUG; + unless ($local_file = + CPAN::FTP->localize("authors/id/$self->{ID}", + $local_wanted)) { + my $note = ""; + if ($CPAN::Index::DATE_OF_02) { + $note = "Note: Current database in memory was generated ". + "on $CPAN::Index::DATE_OF_02\n"; + } + $CPAN::Frontend->mydie("Giving up on '$local_wanted'\n$note"); + } + + $self->debug("local_wanted[$local_wanted]local_file[$local_file]") if $CPAN::DEBUG; + $self->{localfile} = $local_file; +} + + +#-> CPAN::Distribution::check_integrity +sub check_integrity { + my($self) = @_; + + return if $self->is_dot_dist; + if ($CPAN::META->has_inst("Digest::SHA")) { + $self->debug("Digest::SHA is installed, verifying"); + $self->verifyCHECKSUM; + } else { + $self->debug("Digest::SHA is NOT installed"); + } +} + +#-> CPAN::Distribution::run_preps_on_packagedir +sub run_preps_on_packagedir { + my($self) = @_; + return if $self->is_dot_dist; + + $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); # unsafe meta access, ok + my $builddir = $CPAN::META->{cachemgr}->dir; # unsafe meta access, ok + $self->safe_chdir($builddir); + $self->debug("Removing tmp-$$") if $CPAN::DEBUG; + File::Path::rmtree("tmp-$$"); + unless (mkdir "tmp-$$", 0755) { + $CPAN::Frontend->unrecoverable_error(<<EOF); +Couldn't mkdir '$builddir/tmp-$$': $! + +Cannot continue: Please find the reason why I cannot make the +directory +$builddir/tmp-$$ +and fix the problem, then retry. + +EOF + } + if ($CPAN::Signal) { + return; + } + $self->safe_chdir("tmp-$$"); + + # + # Unpack the goods + # + my $local_file = $self->{localfile}; + my $ct = eval{CPAN::Tarzip->new($local_file)}; + unless ($ct) { + $self->{unwrapped} = CPAN::Distrostatus->new("NO"); + delete $self->{build_dir}; + return; + } + if ($local_file =~ /(\.tar\.(bz2|gz|Z)|\.tgz)(?!\n)\Z/i) { + $self->{was_uncompressed}++ unless eval{$ct->gtest()}; + $self->untar_me($ct); + } elsif ( $local_file =~ /\.zip(?!\n)\Z/i ) { + $self->unzip_me($ct); + } else { + $self->{was_uncompressed}++ unless $ct->gtest(); + $local_file = $self->handle_singlefile($local_file); + } + + # we are still in the tmp directory! + # Let's check if the package has its own directory. + my $dh = DirHandle->new(File::Spec->curdir) + or Carp::croak("Couldn't opendir .: $!"); + my @readdir = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh->read; ### MAC?? + if (grep { $_ eq "pax_global_header" } @readdir) { + $CPAN::Frontend->mywarn("Your (un)tar seems to have extracted a file named 'pax_global_header' +from the tarball '$local_file'. +This is almost certainly an error. Please upgrade your tar. +I'll ignore this file for now. +See also http://rt.cpan.org/Ticket/Display.html?id=38932\n"); + $CPAN::Frontend->mysleep(5); + @readdir = grep { $_ ne "pax_global_header" } @readdir; + } + $dh->close; + my $tdir_base; + my $from_dir; + my @dirents; + if (@readdir == 1 && -d $readdir[0]) { + $tdir_base = $readdir[0]; + $from_dir = File::Spec->catdir(File::Spec->curdir,$readdir[0]); + my($mode) = (stat $from_dir)[2]; + chmod $mode | 00755, $from_dir; # JONATHAN/Math-Calculus-TaylorSeries-0.1.tar.gz has 0644 + my $dh2; + unless ($dh2 = DirHandle->new($from_dir)) { + my $why = sprintf + ( + "Couldn't opendir '%s', mode '%o': %s", + $from_dir, + $mode, + $!, + ); + $CPAN::Frontend->mywarn("$why\n"); + $self->{writemakefile} = CPAN::Distrostatus->new("NO -- $why"); + return; + } + @dirents = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh2->read; ### MAC?? + } else { + my $userid = $self->cpan_userid; + CPAN->debug("userid[$userid]"); + if (!$userid or $userid eq "N/A") { + $userid = "anon"; + } + $tdir_base = $userid; + $from_dir = File::Spec->curdir; + @dirents = @readdir; + } + my $packagedir; + my $eexist = ($CPAN::META->has_usable("Errno") && defined &Errno::EEXIST) + ? &Errno::EEXIST : undef; + for(my $suffix = 0; ; $suffix++) { + $packagedir = File::Spec->catdir($builddir, "$tdir_base-$suffix"); + my $parent = $builddir; + mkdir($packagedir, 0777) and last; + if((defined($eexist) && $! != $eexist) || $suffix == 999) { + $CPAN::Frontend->mydie("Cannot create directory $packagedir: $!\n"); + } + } + my $f; + for $f (@dirents) { # is already without "." and ".." + my $from = File::Spec->catfile($from_dir,$f); + my($mode) = (stat $from)[2]; + chmod $mode | 00755, $from if -d $from; # OTTO/Pod-Trial-LinkImg-0.005.tgz + my $to = File::Spec->catfile($packagedir,$f); + unless (File::Copy::move($from,$to)) { + my $err = $!; + $from = File::Spec->rel2abs($from); + $CPAN::Frontend->mydie( + "Couldn't move $from to $to: $err; #82295? ". + "CPAN::VERSION=$CPAN::VERSION; ". + "File::Copy::VERSION=$File::Copy::VERSION; ". + "$from " . (-e $from ? "exists; " : "does not exist; "). + "$to " . (-e $to ? "exists; " : "does not exist; "). + "cwd=" . CPAN::anycwd() . ";" + ); + } + } + $self->{build_dir} = $packagedir; + $self->safe_chdir($builddir); + File::Path::rmtree("tmp-$$"); + + $self->safe_chdir($packagedir); + $self->_signature_business(); + $self->safe_chdir($builddir); + + return($packagedir,$local_file); +} + +#-> sub CPAN::Distribution::pick_meta_file ; +sub pick_meta_file { + my($self, $filter) = @_; + $filter = '.' unless defined $filter; + + my $build_dir; + unless ($build_dir = $self->{build_dir}) { + # maybe permission on build_dir was missing + $CPAN::Frontend->mywarn("Warning: cannot determine META.yml without a build_dir.\n"); + return; + } + + my $has_cm = $CPAN::META->has_usable("CPAN::Meta"); + my $has_pcm = $CPAN::META->has_usable("Parse::CPAN::Meta"); + + my @choices; + push @choices, 'MYMETA.json' if $has_cm; + push @choices, 'MYMETA.yml' if $has_cm || $has_pcm; + push @choices, 'META.json' if $has_cm; + push @choices, 'META.yml' if $has_cm || $has_pcm; + + for my $file ( grep { /$filter/ } @choices ) { + my $path = File::Spec->catfile( $build_dir, $file ); + return $path if -f $path + } + + return; +} + +#-> sub CPAN::Distribution::parse_meta_yml ; +sub parse_meta_yml { + my($self, $yaml) = @_; + $self->debug(sprintf("parse_meta_yml[%s]",$yaml||'undef')) if $CPAN::DEBUG; + my $build_dir = $self->{build_dir} or die "PANIC: cannot parse yaml without a build_dir"; + $yaml ||= File::Spec->catfile($build_dir,"META.yml"); + $self->debug("meta[$yaml]") if $CPAN::DEBUG; + return unless -f $yaml; + my $early_yaml; + eval { + $CPAN::META->has_inst("Parse::CPAN::Meta") or die; + die "Parse::CPAN::Meta yaml too old" unless $Parse::CPAN::Meta::VERSION >= "1.40"; + # P::C::M returns last document in scalar context + $early_yaml = Parse::CPAN::Meta::LoadFile($yaml); + }; + unless ($early_yaml) { + eval { $early_yaml = CPAN->_yaml_loadfile($yaml)->[0]; }; + } + $self->debug(sprintf("yaml[%s]", $early_yaml || 'UNDEF')) if $CPAN::DEBUG; + $self->debug($early_yaml) if $CPAN::DEBUG && $early_yaml; + if (!ref $early_yaml or ref $early_yaml ne "HASH"){ + # fix rt.cpan.org #95271 + $CPAN::Frontend->mywarn("The content of '$yaml' is not a HASH reference. Cannot use it.\n"); + return {}; + } + return $early_yaml || undef; +} + +#-> sub CPAN::Distribution::satisfy_requires ; +# return values: 1 means requirements are satisfied; +# and 0 means not satisfied (and maybe queued) +sub satisfy_requires { + my ($self) = @_; + $self->debug("Entering satisfy_requires") if $CPAN::DEBUG; + if (my @prereq = $self->unsat_prereq("later")) { + if ($CPAN::DEBUG){ + require Data::Dumper; + my $prereq = Data::Dumper->new(\@prereq)->Terse(1)->Indent(0)->Dump; + $self->debug("unsatisfied[$prereq]"); + } + if ($prereq[0][0] eq "perl") { + my $need = "requires perl '$prereq[0][1]'"; + my $id = $self->pretty_id; + $CPAN::Frontend->mywarn("$id $need; you have only $]; giving up\n"); + $self->{make} = CPAN::Distrostatus->new("NO $need"); + $self->store_persistent_state; + die "[prereq] -- NOT OK\n"; + } else { + my $follow = eval { $self->follow_prereqs("later",@prereq); }; + if (0) { + } elsif ($follow) { + return; # we need deps + } elsif ($@ && ref $@ && $@->isa("CPAN::Exception::RecursiveDependency")) { + $CPAN::Frontend->mywarn($@); + die "[depend] -- NOT OK\n"; + } + } + } + return 1; +} + +#-> sub CPAN::Distribution::satisfy_configure_requires ; +# return values: 1 means configure_require is satisfied; +# and 0 means not satisfied (and maybe queued) +sub satisfy_configure_requires { + my($self) = @_; + $self->debug("Entering satisfy_configure_requires") if $CPAN::DEBUG; + my $enable_configure_requires = 1; + if (!$enable_configure_requires) { + return 1; + # if we return 1 here, everything is as before we introduced + # configure_requires that means, things with + # configure_requires simply fail, all others succeed + } + my @prereq = $self->unsat_prereq("configure_requires_later"); + $self->debug(sprintf "configure_requires[%s]", join(",",map {join "/",@$_} @prereq)) if $CPAN::DEBUG; + return 1 unless @prereq; + $self->debug(\@prereq) if $CPAN::DEBUG; + if ($self->{configure_requires_later}) { + for my $k (sort keys %{$self->{configure_requires_later_for}||{}}) { + if ($self->{configure_requires_later_for}{$k}>1) { + my $type = ""; + for my $p (@prereq) { + if ($p->[0] eq $k) { + $type = $p->[1]; + } + } + $type = " $type" if $type; + $CPAN::Frontend->mywarn("Warning: unmanageable(?) prerequisite $k$type"); + sleep 1; + } + } + } + if ($prereq[0][0] eq "perl") { + my $need = "requires perl '$prereq[0][1]'"; + my $id = $self->pretty_id; + $CPAN::Frontend->mywarn("$id $need; you have only $]; giving up\n"); + $self->{make} = CPAN::Distrostatus->new("NO $need"); + $self->store_persistent_state; + return $self->goodbye("[prereq] -- NOT OK"); + } else { + my $follow = eval { + $self->follow_prereqs("configure_requires_later", @prereq); + }; + if (0) { + } elsif ($follow) { + return; # we need deps + } elsif ($@ && ref $@ && $@->isa("CPAN::Exception::RecursiveDependency")) { + $CPAN::Frontend->mywarn($@); + return $self->goodbye("[depend] -- NOT OK"); + } + else { + return $self->goodbye("[configure_requires] -- NOT OK"); + } + } + die "never reached"; +} + +#-> sub CPAN::Distribution::choose_MM_or_MB ; +sub choose_MM_or_MB { + my($self) = @_; + $self->satisfy_configure_requires() or return; + my $local_file = $self->{localfile}; + my($mpl) = File::Spec->catfile($self->{build_dir},"Makefile.PL"); + my($mpl_exists) = -f $mpl; + unless ($mpl_exists) { + # NFS has been reported to have racing problems after the + # renaming of a directory in some environments. + # This trick helps. + $CPAN::Frontend->mysleep(1); + my $mpldh = DirHandle->new($self->{build_dir}) + or Carp::croak("Couldn't opendir $self->{build_dir}: $!"); + $mpl_exists = grep /^Makefile\.PL$/, $mpldh->read; + $mpldh->close; + } + my $prefer_installer = "eumm"; # eumm|mb + if (-f File::Spec->catfile($self->{build_dir},"Build.PL")) { + if ($mpl_exists) { # they *can* choose + if ($CPAN::META->has_inst("Module::Build")) { + $prefer_installer = CPAN::HandleConfig->prefs_lookup( + $self, q{prefer_installer} + ); + # M::B <= 0.35 left a DATA handle open that + # causes problems upgrading M::B on Windows + close *Module::Build::Version::DATA + if fileno *Module::Build::Version::DATA; + } + } else { + $prefer_installer = "mb"; + } + } + if (lc($prefer_installer) eq "rand") { + $prefer_installer = rand()<.5 ? "eumm" : "mb"; + } + if (lc($prefer_installer) eq "mb") { + $self->{modulebuild} = 1; + } elsif ($self->{archived} eq "patch") { + # not an edge case, nothing to install for sure + my $why = "A patch file cannot be installed"; + $CPAN::Frontend->mywarn("Refusing to handle this file: $why\n"); + $self->{writemakefile} = CPAN::Distrostatus->new("NO $why"); + } elsif (! $mpl_exists) { + $self->_edge_cases($mpl,$local_file); + } + if ($self->{build_dir} + && + $CPAN::Config->{build_dir_reuse} + ) { + $self->store_persistent_state; + } + return $self; +} + +# see also reanimate_build_dir +#-> CPAN::Distribution::store_persistent_state +sub store_persistent_state { + my($self) = @_; + my $dir = $self->{build_dir}; + unless (defined $dir && length $dir) { + my $id = $self->id; + $CPAN::Frontend->mywarnonce("build_dir of $id is not known, ". + "will not store persistent state\n"); + return; + } + # self-build-dir + my $sbd = Cwd::realpath( + File::Spec->catdir($dir, File::Spec->updir ()) + ); + # config-build-dir + my $cbd = Cwd::realpath( + # the catdir is a workaround for bug https://rt.cpan.org/Ticket/Display.html?id=101283 + File::Spec->catdir($CPAN::Config->{build_dir}, File::Spec->curdir()) + ); + unless ($sbd eq $cbd) { + $CPAN::Frontend->mywarnonce("Directory '$dir' not below $CPAN::Config->{build_dir}, ". + "will not store persistent state\n"); + return; + } + my $file = sprintf "%s.yml", $dir; + my $yaml_module = CPAN::_yaml_module(); + if ($CPAN::META->has_inst($yaml_module)) { + CPAN->_yaml_dumpfile( + $file, + { + time => time, + perl => CPAN::_perl_fingerprint(), + distribution => $self, + } + ); + } else { + $CPAN::Frontend->myprintonce("'$yaml_module' not installed, ". + "will not store persistent state\n"); + } +} + +#-> CPAN::Distribution::try_download +sub try_download { + my($self,$patch) = @_; + my $norm = $self->normalize($patch); + my($local_wanted) = + File::Spec->catfile( + $CPAN::Config->{keep_source_where}, + "authors", + "id", + split(/\//,$norm), + ); + $self->debug("Doing localize") if $CPAN::DEBUG; + return CPAN::FTP->localize("authors/id/$norm", + $local_wanted); +} + +{ + my $stdpatchargs = ""; + #-> CPAN::Distribution::patch + sub patch { + my($self) = @_; + $self->debug("checking patches id[$self->{ID}]") if $CPAN::DEBUG; + my $patches = $self->prefs->{patches}; + $patches ||= ""; + $self->debug("patches[$patches]") if $CPAN::DEBUG; + if ($patches) { + return unless @$patches; + $self->safe_chdir($self->{build_dir}); + CPAN->debug("patches[$patches]") if $CPAN::DEBUG; + my $patchbin = $CPAN::Config->{patch}; + unless ($patchbin && length $patchbin) { + $CPAN::Frontend->mydie("No external patch command configured\n\n". + "Please run 'o conf init /patch/'\n\n"); + } + unless (MM->maybe_command($patchbin)) { + $CPAN::Frontend->mydie("No external patch command available\n\n". + "Please run 'o conf init /patch/'\n\n"); + } + $patchbin = CPAN::HandleConfig->safe_quote($patchbin); + local $ENV{PATCH_GET} = 0; # formerly known as -g0 + unless ($stdpatchargs) { + my $system = "$patchbin --version |"; + local *FH; + open FH, $system or die "Could not fork '$system': $!"; + local $/ = "\n"; + my $pversion; + PARSEVERSION: while (<FH>) { + if (/^patch\s+([\d\.]+)/) { + $pversion = $1; + last PARSEVERSION; + } + } + if ($pversion) { + $stdpatchargs = "-N --fuzz=3"; + } else { + $stdpatchargs = "-N"; + } + } + my $countedpatches = @$patches == 1 ? "1 patch" : (scalar @$patches . " patches"); + $CPAN::Frontend->myprint("Applying $countedpatches:\n"); + my $patches_dir = $CPAN::Config->{patches_dir}; + for my $patch (@$patches) { + if ($patches_dir && !File::Spec->file_name_is_absolute($patch)) { + my $f = File::Spec->catfile($patches_dir, $patch); + $patch = $f if -f $f; + } + unless (-f $patch) { + CPAN->debug("not on disk: patch[$patch]") if $CPAN::DEBUG; + if (my $trydl = $self->try_download($patch)) { + $patch = $trydl; + } else { + my $fail = "Could not find patch '$patch'"; + $CPAN::Frontend->mywarn("$fail; cannot continue\n"); + $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); + delete $self->{build_dir}; + return; + } + } + $CPAN::Frontend->myprint(" $patch\n"); + my $readfh = CPAN::Tarzip->TIEHANDLE($patch); + + my $pcommand; + my($ppp,$pfiles) = $self->_patch_p_parameter($readfh); + if ($ppp eq "applypatch") { + $pcommand = "$CPAN::Config->{applypatch} -verbose"; + } else { + my $thispatchargs = join " ", $stdpatchargs, $ppp; + $pcommand = "$patchbin $thispatchargs"; + require Config; # usually loaded from CPAN.pm + if ($Config::Config{osname} eq "solaris") { + # native solaris patch cannot patch readonly files + for my $file (@{$pfiles||[]}) { + my @stat = stat $file or next; + chmod $stat[2] | 0600, $file; # may fail + } + } + } + + $readfh = CPAN::Tarzip->TIEHANDLE($patch); # open again + my $writefh = FileHandle->new; + $CPAN::Frontend->myprint(" $pcommand\n"); + unless (open $writefh, "|$pcommand") { + my $fail = "Could not fork '$pcommand'"; + $CPAN::Frontend->mywarn("$fail; cannot continue\n"); + $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); + delete $self->{build_dir}; + return; + } + binmode($writefh); + while (my $x = $readfh->READLINE) { + print $writefh $x; + } + unless (close $writefh) { + my $fail = "Could not apply patch '$patch'"; + $CPAN::Frontend->mywarn("$fail; cannot continue\n"); + $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); + delete $self->{build_dir}; + return; + } + } + $self->{patched}++; + } + return 1; + } +} + +# may return +# - "applypatch" +# - ("-p0"|"-p1", $files) +sub _patch_p_parameter { + my($self,$fh) = @_; + my $cnt_files = 0; + my $cnt_p0files = 0; + my @files; + local($_); + while ($_ = $fh->READLINE) { + if ( + $CPAN::Config->{applypatch} + && + /\#\#\#\# ApplyPatch data follows \#\#\#\#/ + ) { + return "applypatch" + } + next unless /^[\*\+]{3}\s(\S+)/; + my $file = $1; + push @files, $file; + $cnt_files++; + $cnt_p0files++ if -f $file; + CPAN->debug("file[$file]cnt_files[$cnt_files]cnt_p0files[$cnt_p0files]") + if $CPAN::DEBUG; + } + return "-p1" unless $cnt_files; + my $opt_p = $cnt_files==$cnt_p0files ? "-p0" : "-p1"; + return ($opt_p, \@files); +} + +#-> sub CPAN::Distribution::_edge_cases +# with "configure" or "Makefile" or single file scripts +sub _edge_cases { + my($self,$mpl,$local_file) = @_; + $self->debug(sprintf("makefilepl[%s]anycwd[%s]", + $mpl, + CPAN::anycwd(), + )) if $CPAN::DEBUG; + my $build_dir = $self->{build_dir}; + my($configure) = File::Spec->catfile($build_dir,"Configure"); + if (-f $configure) { + # do we have anything to do? + $self->{configure} = $configure; + } elsif (-f File::Spec->catfile($build_dir,"Makefile")) { + $CPAN::Frontend->mywarn(qq{ +Package comes with a Makefile and without a Makefile.PL. +We\'ll try to build it with that Makefile then. +}); + $self->{writemakefile} = CPAN::Distrostatus->new("YES"); + $CPAN::Frontend->mysleep(2); + } else { + my $cf = $self->called_for || "unknown"; + if ($cf =~ m|/|) { + $cf =~ s|.*/||; + $cf =~ s|\W.*||; + } + $cf =~ s|[/\\:]||g; # risk of filesystem damage + $cf = "unknown" unless length($cf); + if (my $crud = $self->_contains_crud($build_dir)) { + my $why = qq{Package contains $crud; not recognized as a perl package, giving up}; + $CPAN::Frontend->mywarn("$why\n"); + $self->{writemakefile} = CPAN::Distrostatus->new(qq{NO -- $why}); + return; + } + $CPAN::Frontend->mywarn(qq{Package seems to come without Makefile.PL. + (The test -f "$mpl" returned false.) + Writing one on our own (setting NAME to $cf)\a\n}); + $self->{had_no_makefile_pl}++; + $CPAN::Frontend->mysleep(3); + + # Writing our own Makefile.PL + + my $exefile_stanza = ""; + if ($self->{archived} eq "maybe_pl") { + $exefile_stanza = $self->_exefile_stanza($build_dir,$local_file); + } + + my $fh = FileHandle->new; + $fh->open(">$mpl") + or Carp::croak("Could not open >$mpl: $!"); + $fh->print( + qq{# This Makefile.PL has been autogenerated by the module CPAN.pm +# because there was no Makefile.PL supplied. +# Autogenerated on: }.scalar localtime().qq{ + +use ExtUtils::MakeMaker; +WriteMakefile( + NAME => q[$cf],$exefile_stanza + ); +}); + $fh->close; + } +} + +#-> CPAN;:Distribution::_contains_crud +sub _contains_crud { + my($self,$dir) = @_; + my(@dirs, $dh, @files); + opendir $dh, $dir or return; + my $dirent; + for $dirent (readdir $dh) { + next if $dirent =~ /^\.\.?$/; + my $path = File::Spec->catdir($dir,$dirent); + if (-d $path) { + push @dirs, $dirent; + } elsif (-f $path) { + push @files, $dirent; + } + } + if (@dirs && @files) { + return "both files[@files] and directories[@dirs]"; + } elsif (@files > 2) { + return "several files[@files] but no Makefile.PL or Build.PL"; + } + return; +} + +#-> CPAN;:Distribution::_exefile_stanza +sub _exefile_stanza { + my($self,$build_dir,$local_file) = @_; + + my $fh = FileHandle->new; + my $script_file = File::Spec->catfile($build_dir,$local_file); + $fh->open($script_file) + or Carp::croak("Could not open script '$script_file': $!"); + local $/ = "\n"; + # parse name and prereq + my($state) = "poddir"; + my($name, $prereq) = ("", ""); + while (<$fh>) { + if ($state eq "poddir" && /^=head\d\s+(\S+)/) { + if ($1 eq 'NAME') { + $state = "name"; + } elsif ($1 eq 'PREREQUISITES') { + $state = "prereq"; + } + } elsif ($state =~ m{^(name|prereq)$}) { + if (/^=/) { + $state = "poddir"; + } elsif (/^\s*$/) { + # nop + } elsif ($state eq "name") { + if ($name eq "") { + ($name) = /^(\S+)/; + $state = "poddir"; + } + } elsif ($state eq "prereq") { + $prereq .= $_; + } + } elsif (/^=cut\b/) { + last; + } + } + $fh->close; + + for ($name) { + s{.*<}{}; # strip X<...> + s{>.*}{}; + } + chomp $prereq; + $prereq = join " ", split /\s+/, $prereq; + my($PREREQ_PM) = join("\n", map { + s{.*<}{}; # strip X<...> + s{>.*}{}; + if (/[\s\'\"]/) { # prose? + } else { + s/[^\w:]$//; # period? + " "x28 . "'$_' => 0,"; + } + } split /\s*,\s*/, $prereq); + + if ($name) { + my $to_file = File::Spec->catfile($build_dir, $name); + rename $script_file, $to_file + or die "Can't rename $script_file to $to_file: $!"; + } + + return " + EXE_FILES => ['$name'], + PREREQ_PM => { +$PREREQ_PM + }, +"; +} + +#-> CPAN::Distribution::_signature_business +sub _signature_business { + my($self) = @_; + my $check_sigs = CPAN::HandleConfig->prefs_lookup($self, + q{check_sigs}); + if ($check_sigs) { + if ($CPAN::META->has_inst("Module::Signature")) { + if (-f "SIGNATURE") { + $self->debug("Module::Signature is installed, verifying") if $CPAN::DEBUG; + my $rv = Module::Signature::verify(); + if ($rv != Module::Signature::SIGNATURE_OK() and + $rv != Module::Signature::SIGNATURE_MISSING()) { + $CPAN::Frontend->mywarn( + qq{\nSignature invalid for }. + qq{distribution file. }. + qq{Please investigate.\n\n} + ); + + my $wrap = + sprintf(qq{I'd recommend removing %s. Some error occurred }. + qq{while checking its signature, so it could }. + qq{be invalid. Maybe you have configured }. + qq{your 'urllist' with a bad URL. Please check this }. + qq{array with 'o conf urllist' and retry. Or }. + qq{examine the distribution in a subshell. Try + look %s +and run + cpansign -v +}, + $self->{localfile}, + $self->pretty_id, + ); + $self->{signature_verify} = CPAN::Distrostatus->new("NO"); + $CPAN::Frontend->mywarn(Text::Wrap::wrap("","",$wrap)); + $CPAN::Frontend->mysleep(5) if $CPAN::Frontend->can("mysleep"); + } else { + $self->{signature_verify} = CPAN::Distrostatus->new("YES"); + $self->debug("Module::Signature has verified") if $CPAN::DEBUG; + } + } else { + $CPAN::Frontend->mywarn(qq{Package came without SIGNATURE\n\n}); + } + } else { + $self->debug("Module::Signature is NOT installed") if $CPAN::DEBUG; + } + } +} + +#-> CPAN::Distribution::untar_me ; +sub untar_me { + my($self,$ct) = @_; + $self->{archived} = "tar"; + my $result = eval { $ct->untar() }; + if ($result) { + $self->{unwrapped} = CPAN::Distrostatus->new("YES"); + } else { + # unfortunately we have no $@ here, Tarzip is using mydie which dies with "\n" + $self->{unwrapped} = CPAN::Distrostatus->new("NO -- untar failed"); + } +} + +# CPAN::Distribution::unzip_me ; +sub unzip_me { + my($self,$ct) = @_; + $self->{archived} = "zip"; + if (eval { $ct->unzip() }) { + $self->{unwrapped} = CPAN::Distrostatus->new("YES"); + } else { + $self->{unwrapped} = CPAN::Distrostatus->new("NO -- unzip failed during unzip"); + } + return; +} + +sub handle_singlefile { + my($self,$local_file) = @_; + + if ( $local_file =~ /\.pm(\.(gz|Z))?(?!\n)\Z/ ) { + $self->{archived} = "pm"; + } elsif ( $local_file =~ /\.patch(\.(gz|bz2))?(?!\n)\Z/ ) { + $self->{archived} = "patch"; + } else { + $self->{archived} = "maybe_pl"; + } + + my $to = File::Basename::basename($local_file); + if ($to =~ s/\.(gz|Z)(?!\n)\Z//) { + if (eval{CPAN::Tarzip->new($local_file)->gunzip($to)}) { + $self->{unwrapped} = CPAN::Distrostatus->new("YES"); + } else { + $self->{unwrapped} = CPAN::Distrostatus->new("NO -- uncompressing failed"); + } + } else { + if (File::Copy::cp($local_file,".")) { + $self->{unwrapped} = CPAN::Distrostatus->new("YES"); + } else { + $self->{unwrapped} = CPAN::Distrostatus->new("NO -- copying failed"); + } + } + return $to; +} + +#-> sub CPAN::Distribution::new ; +sub new { + my($class,%att) = @_; + + # $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); + + my $this = { %att }; + return bless $this, $class; +} + +#-> sub CPAN::Distribution::look ; +sub look { + my($self) = @_; + + if ($^O eq 'MacOS') { + $self->Mac::BuildTools::look; + return; + } + + if ( $CPAN::Config->{'shell'} ) { + $CPAN::Frontend->myprint(qq{ +Trying to open a subshell in the build directory... +}); + } else { + $CPAN::Frontend->myprint(qq{ +Your configuration does not define a value for subshells. +Please define it with "o conf shell <your shell>" +}); + return; + } + my $dist = $self->id; + my $dir; + unless ($dir = $self->dir) { + $self->get; + } + unless ($dir ||= $self->dir) { + $CPAN::Frontend->mywarn(qq{ +Could not determine which directory to use for looking at $dist. +}); + return; + } + my $pwd = CPAN::anycwd(); + $self->safe_chdir($dir); + $CPAN::Frontend->myprint(qq{Working directory is $dir\n}); + { + local $ENV{CPAN_SHELL_LEVEL} = $ENV{CPAN_SHELL_LEVEL}||0; + $ENV{CPAN_SHELL_LEVEL} += 1; + my $shell = CPAN::HandleConfig->safe_quote($CPAN::Config->{'shell'}); + + local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) + ? $ENV{PERL5LIB} + : ($ENV{PERLLIB} || ""); + + local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; + # local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # look + $CPAN::META->set_perl5lib; + local $ENV{MAKEFLAGS}; # protect us from outer make calls + + unless (system($shell) == 0) { + my $code = $? >> 8; + $CPAN::Frontend->mywarn("Subprocess shell exit code $code\n"); + } + } + $self->safe_chdir($pwd); +} + +# CPAN::Distribution::cvs_import ; +sub cvs_import { + my($self) = @_; + $self->get; + my $dir = $self->dir; + + my $package = $self->called_for; + my $module = $CPAN::META->instance('CPAN::Module', $package); + my $version = $module->cpan_version; + + my $userid = $self->cpan_userid; + + my $cvs_dir = (split /\//, $dir)[-1]; + $cvs_dir =~ s/-\d+[^-]+(?!\n)\Z//; + my $cvs_root = + $CPAN::Config->{cvsroot} || $ENV{CVSROOT}; + my $cvs_site_perl = + $CPAN::Config->{cvs_site_perl} || $ENV{CVS_SITE_PERL}; + if ($cvs_site_perl) { + $cvs_dir = "$cvs_site_perl/$cvs_dir"; + } + my $cvs_log = qq{"imported $package $version sources"}; + $version =~ s/\./_/g; + # XXX cvs: undocumented and unclear how it was meant to work + my @cmd = ('cvs', '-d', $cvs_root, 'import', '-m', $cvs_log, + "$cvs_dir", $userid, "v$version"); + + my $pwd = CPAN::anycwd(); + chdir($dir) or $CPAN::Frontend->mydie(qq{Could not chdir to "$dir": $!}); + + $CPAN::Frontend->myprint(qq{Working directory is $dir\n}); + + $CPAN::Frontend->myprint(qq{@cmd\n}); + system(@cmd) == 0 or + # XXX cvs + $CPAN::Frontend->mydie("cvs import failed"); + chdir($pwd) or $CPAN::Frontend->mydie(qq{Could not chdir to "$pwd": $!}); +} + +#-> sub CPAN::Distribution::readme ; +sub readme { + my($self) = @_; + my($dist) = $self->id; + my($sans,$suffix) = $dist =~ /(.+)\.(tgz|tar[\._-]gz|tar\.Z|zip)$/; + $self->debug("sans[$sans] suffix[$suffix]\n") if $CPAN::DEBUG; + my($local_file); + my($local_wanted) = + File::Spec->catfile( + $CPAN::Config->{keep_source_where}, + "authors", + "id", + split(/\//,"$sans.readme"), + ); + my $readme = "authors/id/$sans.readme"; + $self->debug("Doing localize for '$readme'") if $CPAN::DEBUG; + $local_file = CPAN::FTP->localize($readme, + $local_wanted) + or $CPAN::Frontend->mydie(qq{No $sans.readme found}); + + if ($^O eq 'MacOS') { + Mac::BuildTools::launch_file($local_file); + return; + } + + my $fh_pager = FileHandle->new; + local($SIG{PIPE}) = "IGNORE"; + my $pager = $CPAN::Config->{'pager'} || "cat"; + $fh_pager->open("|$pager") + or die "Could not open pager $pager\: $!"; + my $fh_readme = FileHandle->new; + $fh_readme->open($local_file) + or $CPAN::Frontend->mydie(qq{Could not open "$local_file": $!}); + $CPAN::Frontend->myprint(qq{ +Displaying file + $local_file +with pager "$pager" +}); + $fh_pager->print(<$fh_readme>); + $fh_pager->close; +} + +#-> sub CPAN::Distribution::verifyCHECKSUM ; +sub verifyCHECKSUM { + my($self) = @_; + EXCUSE: { + my @e; + $self->{CHECKSUM_STATUS} ||= ""; + $self->{CHECKSUM_STATUS} eq "OK" and push @e, "Checksum was ok"; + $CPAN::Frontend->myprint(join "", map {" $_\n"} @e) and return if @e; + } + my($lc_want,$lc_file,@local,$basename); + @local = split(/\//,$self->id); + pop @local; + push @local, "CHECKSUMS"; + $lc_want = + File::Spec->catfile($CPAN::Config->{keep_source_where}, + "authors", "id", @local); + local($") = "/"; + if (my $size = -s $lc_want) { + $self->debug("lc_want[$lc_want]size[$size]") if $CPAN::DEBUG; + if ($self->CHECKSUM_check_file($lc_want,1)) { + return $self->{CHECKSUM_STATUS} = "OK"; + } + } + $lc_file = CPAN::FTP->localize("authors/id/@local", + $lc_want,1); + unless ($lc_file) { + $CPAN::Frontend->myprint("Trying $lc_want.gz\n"); + $local[-1] .= ".gz"; + $lc_file = CPAN::FTP->localize("authors/id/@local", + "$lc_want.gz",1); + if ($lc_file) { + $lc_file =~ s/\.gz(?!\n)\Z//; + eval{CPAN::Tarzip->new("$lc_file.gz")->gunzip($lc_file)}; + } else { + return; + } + } + if ($self->CHECKSUM_check_file($lc_file)) { + return $self->{CHECKSUM_STATUS} = "OK"; + } +} + +#-> sub CPAN::Distribution::SIG_check_file ; +sub SIG_check_file { + my($self,$chk_file) = @_; + my $rv = eval { Module::Signature::_verify($chk_file) }; + + if ($rv == Module::Signature::SIGNATURE_OK()) { + $CPAN::Frontend->myprint("Signature for $chk_file ok\n"); + return $self->{SIG_STATUS} = "OK"; + } else { + $CPAN::Frontend->myprint(qq{\nSignature invalid for }. + qq{distribution file. }. + qq{Please investigate.\n\n}. + $self->as_string, + $CPAN::META->instance( + 'CPAN::Author', + $self->cpan_userid + )->as_string); + + my $wrap = qq{I\'d recommend removing $chk_file. Its signature +is invalid. Maybe you have configured your 'urllist' with +a bad URL. Please check this array with 'o conf urllist', and +retry.}; + + $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap)); + } +} + +#-> sub CPAN::Distribution::CHECKSUM_check_file ; + +# sloppy is 1 when we have an old checksums file that maybe is good +# enough + +sub CHECKSUM_check_file { + my($self,$chk_file,$sloppy) = @_; + my($cksum,$file,$basename); + + $sloppy ||= 0; + $self->debug("chk_file[$chk_file]sloppy[$sloppy]") if $CPAN::DEBUG; + my $check_sigs = CPAN::HandleConfig->prefs_lookup($self, + q{check_sigs}); + if ($check_sigs) { + if ($CPAN::META->has_inst("Module::Signature")) { + $self->debug("Module::Signature is installed, verifying") if $CPAN::DEBUG; + $self->SIG_check_file($chk_file); + } else { + $self->debug("Module::Signature is NOT installed") if $CPAN::DEBUG; + } + } + + $file = $self->{localfile}; + $basename = File::Basename::basename($file); + my $fh = FileHandle->new; + if (open $fh, $chk_file) { + local($/); + my $eval = <$fh>; + $eval =~ s/\015?\012/\n/g; + close $fh; + my($compmt) = Safe->new(); + $cksum = $compmt->reval($eval); + if ($@) { + rename $chk_file, "$chk_file.bad"; + Carp::confess($@) if $@; + } + } else { + Carp::carp "Could not open $chk_file for reading"; + } + + if (! ref $cksum or ref $cksum ne "HASH") { + $CPAN::Frontend->mywarn(qq{ +Warning: checksum file '$chk_file' broken. + +When trying to read that file I expected to get a hash reference +for further processing, but got garbage instead. +}); + my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed nonetheless?", "no"); + $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); + $self->{CHECKSUM_STATUS} = "NIL -- CHECKSUMS file broken"; + return; + } elsif (exists $cksum->{$basename}{sha256}) { + $self->debug("Found checksum for $basename:" . + "$cksum->{$basename}{sha256}\n") if $CPAN::DEBUG; + + open($fh, $file); + binmode $fh; + my $eq = $self->eq_CHECKSUM($fh,$cksum->{$basename}{sha256}); + $fh->close; + $fh = CPAN::Tarzip->TIEHANDLE($file); + + unless ($eq) { + my $dg = Digest::SHA->new(256); + my($data,$ref); + $ref = \$data; + while ($fh->READ($ref, 4096) > 0) { + $dg->add($data); + } + my $hexdigest = $dg->hexdigest; + $eq += $hexdigest eq $cksum->{$basename}{'sha256-ungz'}; + } + + if ($eq) { + $CPAN::Frontend->myprint("Checksum for $file ok\n"); + return $self->{CHECKSUM_STATUS} = "OK"; + } else { + $CPAN::Frontend->myprint(qq{\nChecksum mismatch for }. + qq{distribution file. }. + qq{Please investigate.\n\n}. + $self->as_string, + $CPAN::META->instance( + 'CPAN::Author', + $self->cpan_userid + )->as_string); + + my $wrap = qq{I\'d recommend removing $file. Its +checksum is incorrect. Maybe you have configured your 'urllist' with +a bad URL. Please check this array with 'o conf urllist', and +retry.}; + + $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap)); + + # former versions just returned here but this seems a + # serious threat that deserves a die + + # $CPAN::Frontend->myprint("\n\n"); + # sleep 3; + # return; + } + # close $fh if fileno($fh); + } else { + return if $sloppy; + unless ($self->{CHECKSUM_STATUS}) { + $CPAN::Frontend->mywarn(qq{ +Warning: No checksum for $basename in $chk_file. + +The cause for this may be that the file is very new and the checksum +has not yet been calculated, but it may also be that something is +going awry right now. +}); + my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed?", "yes"); + $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); + } + $self->{CHECKSUM_STATUS} = "NIL -- distro not in CHECKSUMS file"; + return; + } +} + +#-> sub CPAN::Distribution::eq_CHECKSUM ; +sub eq_CHECKSUM { + my($self,$fh,$expect) = @_; + if ($CPAN::META->has_inst("Digest::SHA")) { + my $dg = Digest::SHA->new(256); + my($data); + while (read($fh, $data, 4096)) { + $dg->add($data); + } + my $hexdigest = $dg->hexdigest; + # warn "fh[$fh] hex[$hexdigest] aexp[$expectMD5]"; + return $hexdigest eq $expect; + } + return 1; +} + +#-> sub CPAN::Distribution::force ; + +# Both CPAN::Modules and CPAN::Distributions know if "force" is in +# effect by autoinspection, not by inspecting a global variable. One +# of the reason why this was chosen to work that way was the treatment +# of dependencies. They should not automatically inherit the force +# status. But this has the downside that ^C and die() will return to +# the prompt but will not be able to reset the force_update +# attributes. We try to correct for it currently in the read_metadata +# routine, and immediately before we check for a Signal. I hope this +# works out in one of v1.57_53ff + +# "Force get forgets previous error conditions" + +#-> sub CPAN::Distribution::fforce ; +sub fforce { + my($self, $method) = @_; + $self->force($method,1); +} + +#-> sub CPAN::Distribution::force ; +sub force { + my($self, $method,$fforce) = @_; + my %phase_map = ( + get => [ + "unwrapped", + "build_dir", + "archived", + "localfile", + "CHECKSUM_STATUS", + "signature_verify", + "prefs", + "prefs_file", + "prefs_file_doc", + "cleanup_after_install_done", + ], + make => [ + "writemakefile", + "make", + "modulebuild", + "prereq_pm", + "cleanup_after_install_done", + ], + test => [ + "badtestcnt", + "make_test", + "cleanup_after_install_done", + ], + install => [ + "install", + "cleanup_after_install_done", + ], + unknown => [ + "reqtype", + "yaml_content", + "cleanup_after_install_done", + ], + ); + my $methodmatch = 0; + my $ldebug = 0; + PHASE: for my $phase (qw(unknown get make test install)) { # order matters + $methodmatch = 1 if $fforce || ($method && $phase eq $method); + next unless $methodmatch; + ATTRIBUTE: for my $att (@{$phase_map{$phase}}) { + if ($phase eq "get") { + if (substr($self->id,-1,1) eq "." + && $att =~ /(unwrapped|build_dir|archived)/ ) { + # cannot be undone for local distros + next ATTRIBUTE; + } + if ($att eq "build_dir" + && $self->{build_dir} + && $CPAN::META->{is_tested} + ) { + delete $CPAN::META->{is_tested}{$self->{build_dir}}; + } + } elsif ($phase eq "test") { + if ($att eq "make_test" + && $self->{make_test} + && $self->{make_test}{COMMANDID} + && $self->{make_test}{COMMANDID} == $CPAN::CurrentCommandId + ) { + # endless loop too likely + next ATTRIBUTE; + } + } + delete $self->{$att}; + if ($ldebug || $CPAN::DEBUG) { + # local $CPAN::DEBUG = 16; # Distribution + CPAN->debug(sprintf "id[%s]phase[%s]att[%s]", $self->id, $phase, $att); + } + } + } + if ($method && $method =~ /make|test|install/) { + $self->{force_update} = 1; # name should probably have been force_install + } +} + +#-> sub CPAN::Distribution::notest ; +sub notest { + my($self, $method) = @_; + # $CPAN::Frontend->mywarn("XDEBUG: set notest for $self $method"); + $self->{"notest"}++; # name should probably have been force_install +} + +#-> sub CPAN::Distribution::unnotest ; +sub unnotest { + my($self) = @_; + # warn "XDEBUG: deleting notest"; + delete $self->{notest}; +} + +#-> sub CPAN::Distribution::unforce ; +sub unforce { + my($self) = @_; + delete $self->{force_update}; +} + +#-> sub CPAN::Distribution::isa_perl ; +sub isa_perl { + my($self) = @_; + my $file = File::Basename::basename($self->id); + if ($file =~ m{ ^ perl + ( + -(5\.\d+\.\d+) + | + (5)[._-](00[0-5](?:_[0-4][0-9])?) + ) + \.tar[._-](?:gz|bz2) + (?!\n)\Z + }xs) { + my $perl_version; + if ($2) { + $perl_version = $2; + } else { + $perl_version = "$3.$4"; + } + return $perl_version; + } elsif ($self->cpan_comment + && + $self->cpan_comment =~ /isa_perl\(.+?\)/) { + return $1; + } +} + + +#-> sub CPAN::Distribution::perl ; +sub perl { + my ($self) = @_; + if (! $self) { + use Carp qw(carp); + carp __PACKAGE__ . "::perl was called without parameters."; + } + return CPAN::HandleConfig->safe_quote($CPAN::Perl); +} + +#-> sub CPAN::Distribution::shortcut_prepare ; +# return values: undef means don't shortcut; 0 means shortcut as fail; +# and 1 means shortcut as success + +sub shortcut_prepare { + my ($self) = @_; + + $self->debug("checking archive type[$self->{ID}]") if $CPAN::DEBUG; + if (!$self->{archived} || $self->{archived} eq "NO") { + return $self->goodbye("Is neither a tar nor a zip archive."); + } + + $self->debug("checking unwrapping[$self->{ID}]") if $CPAN::DEBUG; + if (!$self->{unwrapped} + || ( + UNIVERSAL::can($self->{unwrapped},"failed") ? + $self->{unwrapped}->failed : + $self->{unwrapped} =~ /^NO/ + )) { + return $self->goodbye("Had problems unarchiving. Please build manually"); + } + + $self->debug("checking signature[$self->{ID}]") if $CPAN::DEBUG; + if ( ! $self->{force_update} + && exists $self->{signature_verify} + && ( + UNIVERSAL::can($self->{signature_verify},"failed") ? + $self->{signature_verify}->failed : + $self->{signature_verify} =~ /^NO/ + ) + ) { + return $self->goodbye("Did not pass the signature test."); + } + + $self->debug("checking writemakefile[$self->{ID}]") if $CPAN::DEBUG; + if ($self->{writemakefile}) { + if ( + UNIVERSAL::can($self->{writemakefile},"failed") ? + $self->{writemakefile}->failed : + $self->{writemakefile} =~ /^NO/ + ) { + # XXX maybe a retry would be in order? + my $err = UNIVERSAL::can($self->{writemakefile},"text") ? + $self->{writemakefile}->text : + $self->{writemakefile}; + $err =~ s/^NO\s*(--\s+)?//; + $err ||= "Had some problem writing Makefile"; + $err .= ", not re-running"; + return $self->goodbye($err); + } else { + return $self->success("Has already been prepared"); + } + } + + $self->debug("checking configure_requires_later[$self->{ID}]") if $CPAN::DEBUG; + if( my $later = $self->{configure_requires_later} ) { # see also undelay + return $self->goodbye($later); + } + + return undef; # no shortcut +} + +sub prepare { + my ($self) = @_; + + $self->get + or return; + + if ( defined( my $sc = $self->shortcut_prepare) ) { + return $sc; + } + + local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) + ? $ENV{PERL5LIB} + : ($ENV{PERLLIB} || ""); + local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; + local $ENV{PERL_USE_UNSAFE_INC} = + exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} + ? $ENV{PERL_USE_UNSAFE_INC} : 1; # prepare + $CPAN::META->set_perl5lib; + local $ENV{MAKEFLAGS}; # protect us from outer make calls + + if ($CPAN::Signal) { + delete $self->{force_update}; + return; + } + + my $builddir = $self->dir or + $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); + + unless (chdir $builddir) { + $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); + return; + } + + if ($CPAN::Signal) { + delete $self->{force_update}; + return; + } + + $self->debug("Changed directory to $builddir") if $CPAN::DEBUG; + + local $ENV{PERL_AUTOINSTALL} = $ENV{PERL_AUTOINSTALL} || ''; + local $ENV{PERL_EXTUTILS_AUTOINSTALL} = $ENV{PERL_EXTUTILS_AUTOINSTALL} || ''; + $self->choose_MM_or_MB + or return; + + my $configurator = $self->{configure} ? "Configure" + : $self->{modulebuild} ? "Build.PL" + : "Makefile.PL"; + + $CPAN::Frontend->myprint("Configuring ".$self->id." with $configurator\n"); + + if ($CPAN::Config->{prerequisites_policy} eq "follow") { + $ENV{PERL_AUTOINSTALL} ||= "--defaultdeps"; + $ENV{PERL_EXTUTILS_AUTOINSTALL} ||= "--defaultdeps"; + } + + my $system; + my $pl_commandline; + if ($self->prefs->{pl}) { + $pl_commandline = $self->prefs->{pl}{commandline}; + } + local $ENV{PERL} = defined $ENV{PERL}? $ENV{PERL} : $^X; + local $ENV{PERL5_CPAN_IS_EXECUTING} = $ENV{PERL5_CPAN_IS_EXECUTING} || ''; + local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; + local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; + if ($pl_commandline) { + $system = $pl_commandline; + $ENV{PERL} = $^X; + } elsif ($self->{'configure'}) { + $system = $self->{'configure'}; + } elsif ($self->{modulebuild}) { + my($perl) = $self->perl or die "Couldn\'t find executable perl\n"; + my $mbuildpl_arg = $self->_make_phase_arg("pl"); + $system = sprintf("%s Build.PL%s", + $perl, + $mbuildpl_arg ? " $mbuildpl_arg" : "", + ); + } else { + my($perl) = $self->perl or die "Couldn\'t find executable perl\n"; + my $switch = ""; +# This needs a handler that can be turned on or off: +# $switch = "-MExtUtils::MakeMaker ". +# "-Mops=:default,:filesys_read,:filesys_open,require,chdir" +# if $] > 5.00310; + my $makepl_arg = $self->_make_phase_arg("pl"); + $ENV{PERL5_CPAN_IS_EXECUTING} = File::Spec->catfile($self->{build_dir}, + "Makefile.PL"); + $system = sprintf("%s%s Makefile.PL%s", + $perl, + $switch ? " $switch" : "", + $makepl_arg ? " $makepl_arg" : "", + ); + } + my $pl_env; + if ($self->prefs->{pl}) { + $pl_env = $self->prefs->{pl}{env}; + } + local @ENV{keys %$pl_env} = values %$pl_env if $pl_env; + if (exists $self->{writemakefile}) { + } else { + local($SIG{ALRM}) = sub { die "inactivity_timeout reached\n" }; + my($ret,$pid,$output); + $@ = ""; + my $go_via_alarm; + if ($CPAN::Config->{inactivity_timeout}) { + require Config; + if ($Config::Config{d_alarm} + && + $Config::Config{d_alarm} eq "define" + ) { + $go_via_alarm++ + } else { + $CPAN::Frontend->mywarn("Warning: you have configured the config ". + "variable 'inactivity_timeout' to ". + "'$CPAN::Config->{inactivity_timeout}'. But ". + "on this machine the system call 'alarm' ". + "isn't available. This means that we cannot ". + "provide the feature of intercepting long ". + "waiting code and will turn this feature off.\n" + ); + $CPAN::Config->{inactivity_timeout} = 0; + } + } + if ($go_via_alarm) { + if ( $self->_should_report('pl') ) { + ($output, $ret) = CPAN::Reporter::record_command( + $system, + $CPAN::Config->{inactivity_timeout}, + ); + CPAN::Reporter::grade_PL( $self, $system, $output, $ret ); + } + else { + eval { + alarm $CPAN::Config->{inactivity_timeout}; + local $SIG{CHLD}; # = sub { wait }; + if (defined($pid = fork)) { + if ($pid) { #parent + # wait; + waitpid $pid, 0; + } else { #child + # note, this exec isn't necessary if + # inactivity_timeout is 0. On the Mac I'd + # suggest, we set it always to 0. + exec $system; + } + } else { + $CPAN::Frontend->myprint("Cannot fork: $!"); + return; + } + }; + alarm 0; + if ($@) { + kill 9, $pid; + waitpid $pid, 0; + my $err = "$@"; + $CPAN::Frontend->myprint($err); + $self->{writemakefile} = CPAN::Distrostatus->new("NO $err"); + $@ = ""; + $self->store_persistent_state; + return $self->goodbye("$system -- TIMED OUT"); + } + } + } else { + if (my $expect_model = $self->_prefs_with_expect("pl")) { + # XXX probably want to check _should_report here and warn + # about not being able to use CPAN::Reporter with expect + $ret = $self->_run_via_expect($system,'writemakefile',$expect_model); + if (! defined $ret + && $self->{writemakefile} + && $self->{writemakefile}->failed) { + # timeout + return; + } + } + elsif ( $self->_should_report('pl') ) { + ($output, $ret) = eval { CPAN::Reporter::record_command($system) }; + if (! defined $output or $@) { + my $err = $@ || "Unknown error"; + $CPAN::Frontend->mywarn("Error while running PL phase: $err\n"); + $self->{writemakefile} = CPAN::Distrostatus + ->new("NO '$system' returned status $ret and no output"); + return $self->goodbye("$system -- NOT OK"); + } + CPAN::Reporter::grade_PL( $self, $system, $output, $ret ); + } + else { + $ret = system($system); + } + if ($ret != 0) { + $self->{writemakefile} = CPAN::Distrostatus + ->new("NO '$system' returned status $ret"); + $CPAN::Frontend->mywarn("Warning: No success on command[$system]\n"); + $self->store_persistent_state; + return $self->goodbye("$system -- NOT OK"); + } + } + if (-f "Makefile" || -f "Build" || ($^O eq 'VMS' && (-f 'descrip.mms' || -f 'Build.com'))) { + $self->{writemakefile} = CPAN::Distrostatus->new("YES"); + delete $self->{make_clean}; # if cleaned before, enable next + $self->store_persistent_state; + return $self->success("$system -- OK"); + } else { + my $makefile = $self->{modulebuild} ? "Build" : "Makefile"; + my $why = "No '$makefile' created"; + $CPAN::Frontend->mywarn($why); + $self->{writemakefile} = CPAN::Distrostatus + ->new(qq{NO -- $why\n}); + $self->store_persistent_state; + return $self->goodbye("$system -- NOT OK"); + } + } + $self->store_persistent_state; + return 1; # success +} + +#-> sub CPAN::Distribution::shortcut_make ; +# return values: undef means don't shortcut; 0 means shortcut as fail; +# and 1 means shortcut as success +sub shortcut_make { + my ($self) = @_; + + $self->debug("checking make/build results[$self->{ID}]") if $CPAN::DEBUG; + if (defined $self->{make}) { + if (UNIVERSAL::can($self->{make},"failed") ? + $self->{make}->failed : + $self->{make} =~ /^NO/ + ) { + if ($self->{force_update}) { + # Trying an already failed 'make' (unless somebody else blocks) + return undef; # no shortcut + } else { + # introduced for turning recursion detection into a distrostatus + my $error = length $self->{make}>3 + ? substr($self->{make},3) : "Unknown error"; + $self->store_persistent_state; + return $self->goodbye("Could not make: $error\n"); + } + } else { + return $self->success("Has already been made") + } + } + return undef; # no shortcut +} + +#-> sub CPAN::Distribution::make ; +sub make { + my($self) = @_; + + $self->pre_make(); + + if (exists $self->{cleanup_after_install_done}) { + $self->post_make(); + return $self->get; + } + + $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; + if (my $goto = $self->prefs->{goto}) { + $self->post_make(); + return $self->goto($goto); + } + # Emergency brake if they said install Pippi and get newest perl + + # XXX Would this make more sense in shortcut_prepare, since + # that doesn't make sense on a perl dist either? Broader + # question: what is the purpose of suggesting force install + # on a perl distribution? That seems unlikely to result in + # such a dependency being satisfied, even if the perl is + # successfully installed. This situation is tantamount to + # a prereq on a version of perl greater than the current one + # so I think we should just abort. -- xdg, 2012-04-06 + if ($self->isa_perl) { + if ( + $self->called_for ne $self->id && + ! $self->{force_update} + ) { + # if we die here, we break bundles + $CPAN::Frontend + ->mywarn(sprintf( + qq{The most recent version "%s" of the module "%s" +is part of the perl-%s distribution. To install that, you need to run + force install %s --or-- + install %s +}, + $CPAN::META->instance( + 'CPAN::Module', + $self->called_for + )->cpan_version, + $self->called_for, + $self->isa_perl, + $self->called_for, + $self->pretty_id, + )); + $self->{make} = CPAN::Distrostatus->new("NO isa perl"); + $CPAN::Frontend->mysleep(1); + $self->post_make(); + return; + } + } + + unless ($self->prepare){ + $self->post_make(); + return; + } + + if ( defined( my $sc = $self->shortcut_make) ) { + $self->post_make(); + return $sc; + } + + if ($CPAN::Signal) { + delete $self->{force_update}; + $self->post_make(); + return; + } + + my $builddir = $self->dir or + $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); + + unless (chdir $builddir) { + $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); + $self->post_make(); + return; + } + + my $make = $self->{modulebuild} ? "Build" : "make"; + $CPAN::Frontend->myprint(sprintf "Running %s for %s\n", $make, $self->id); + local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) + ? $ENV{PERL5LIB} + : ($ENV{PERLLIB} || ""); + local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; + local $ENV{PERL_USE_UNSAFE_INC} = + exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} + ? $ENV{PERL_USE_UNSAFE_INC} : 1; # make + $CPAN::META->set_perl5lib; + local $ENV{MAKEFLAGS}; # protect us from outer make calls + + if ($CPAN::Signal) { + delete $self->{force_update}; + $self->post_make(); + return; + } + + if ($^O eq 'MacOS') { + Mac::BuildTools::make($self); + $self->post_make(); + return; + } + + my %env; + while (my($k,$v) = each %ENV) { + next if defined $v; + $env{$k} = ''; + } + local @ENV{keys %env} = values %env; + my $satisfied = eval { $self->satisfy_requires }; + if ($@) { + return $self->goodbye($@); + } + unless ($satisfied){ + $self->post_make(); + return; + } + if ($CPAN::Signal) { + delete $self->{force_update}; + $self->post_make(); + return; + } + + # need to chdir again, because $self->satisfy_requires might change the directory + unless (chdir $builddir) { + $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); + $self->post_make(); + return; + } + + my $system; + my $make_commandline; + if ($self->prefs->{make}) { + $make_commandline = $self->prefs->{make}{commandline}; + } + local $ENV{PERL} = defined $ENV{PERL}? $ENV{PERL} : $^X; + local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; + local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; + if ($make_commandline) { + $system = $make_commandline; + $ENV{PERL} = CPAN::find_perl(); + } else { + if ($self->{modulebuild}) { + unless (-f "Build" || ($^O eq 'VMS' && -f 'Build.com')) { + my $cwd = CPAN::anycwd(); + $CPAN::Frontend->mywarn("Alert: no Build file available for 'make $self->{id}'". + " in cwd[$cwd]. Danger, Will Robinson!\n"); + $CPAN::Frontend->mysleep(5); + } + $system = join " ", $self->_build_command(), $CPAN::Config->{mbuild_arg}; + } else { + $system = join " ", $self->_make_command(), $CPAN::Config->{make_arg}; + } + $system =~ s/\s+$//; + my $make_arg = $self->_make_phase_arg("make"); + $system = sprintf("%s%s", + $system, + $make_arg ? " $make_arg" : "", + ); + } + my $make_env; + if ($self->prefs->{make}) { + $make_env = $self->prefs->{make}{env}; + } + local @ENV{keys %$make_env} = values %$make_env if $make_env; + my $expect_model = $self->_prefs_with_expect("make"); + my $want_expect = 0; + if ( $expect_model && @{$expect_model->{talk}} ) { + my $can_expect = $CPAN::META->has_inst("Expect"); + if ($can_expect) { + $want_expect = 1; + } else { + $CPAN::Frontend->mywarn("Expect not installed, falling back to ". + "system()\n"); + } + } + my ($system_ok, $system_err); + if ($want_expect) { + # XXX probably want to check _should_report here and + # warn about not being able to use CPAN::Reporter with expect + $system_ok = $self->_run_via_expect($system,'make',$expect_model) == 0; + } + elsif ( $self->_should_report('make') ) { + my ($output, $ret) = CPAN::Reporter::record_command($system); + CPAN::Reporter::grade_make( $self, $system, $output, $ret ); + $system_ok = ! $ret; + } + else { + my $rc = system($system); + $system_ok = $rc == 0; + $system_err = $! if $rc == -1; + } + $self->introduce_myself; + if ( $system_ok ) { + $CPAN::Frontend->myprint(" $system -- OK\n"); + $self->{make} = CPAN::Distrostatus->new("YES"); + } else { + $self->{writemakefile} ||= CPAN::Distrostatus->new("YES"); + $self->{make} = CPAN::Distrostatus->new("NO"); + $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); + $CPAN::Frontend->mywarn(" $system_err\n") if defined $system_err; + } + $self->store_persistent_state; + + $self->post_make(); + + return !! $system_ok; +} + +# CPAN::Distribution::goodbye ; +sub goodbye { + my($self,$goodbye) = @_; + my $id = $self->pretty_id; + $CPAN::Frontend->mywarn(" $id\n $goodbye\n"); + return 0; # must be explicit false, not undef +} + +sub success { + my($self,$why) = @_; + my $id = $self->pretty_id; + $CPAN::Frontend->myprint(" $id\n $why\n"); + return 1; +} + +# CPAN::Distribution::_run_via_expect ; +sub _run_via_expect { + my($self,$system,$phase,$expect_model) = @_; + CPAN->debug("system[$system]expect_model[$expect_model]") if $CPAN::DEBUG; + if ($CPAN::META->has_inst("Expect")) { + my $expo = Expect->new; # expo Expect object; + $expo->spawn($system); + $expect_model->{mode} ||= "deterministic"; + if ($expect_model->{mode} eq "deterministic") { + return $self->_run_via_expect_deterministic($expo,$phase,$expect_model); + } elsif ($expect_model->{mode} eq "anyorder") { + return $self->_run_via_expect_anyorder($expo,$phase,$expect_model); + } else { + die "Panic: Illegal expect mode: $expect_model->{mode}"; + } + } else { + $CPAN::Frontend->mywarn("Expect not installed, falling back to system()\n"); + return system($system); + } +} + +sub _run_via_expect_anyorder { + my($self,$expo,$phase,$expect_model) = @_; + my $timeout = $expect_model->{timeout} || 5; + my $reuse = $expect_model->{reuse}; + my @expectacopy = @{$expect_model->{talk}}; # we trash it! + my $but = ""; + my $timeout_start = time; + EXPECT: while () { + my($eof,$ran_into_timeout); + # XXX not up to the full power of expect. one could certainly + # wrap all of the talk pairs into a single expect call and on + # success tweak it and step ahead to the next question. The + # current implementation unnecessarily limits itself to a + # single match. + my @match = $expo->expect(1, + [ eof => sub { + $eof++; + } ], + [ timeout => sub { + $ran_into_timeout++; + } ], + -re => eval"qr{.}", + ); + if ($match[2]) { + $but .= $match[2]; + } + $but .= $expo->clear_accum; + if ($eof) { + $expo->soft_close; + return $expo->exitstatus(); + } elsif ($ran_into_timeout) { + # warn "DEBUG: they are asking a question, but[$but]"; + for (my $i = 0; $i <= $#expectacopy; $i+=2) { + my($next,$send) = @expectacopy[$i,$i+1]; + my $regex = eval "qr{$next}"; + # warn "DEBUG: will compare with regex[$regex]."; + if ($but =~ /$regex/) { + # warn "DEBUG: will send send[$send]"; + $expo->send($send); + # never allow reusing an QA pair unless they told us + splice @expectacopy, $i, 2 unless $reuse; + $but =~ s/(?s:^.*?)$regex//; + $timeout_start = time; + next EXPECT; + } + } + my $have_waited = time - $timeout_start; + if ($have_waited < $timeout) { + # warn "DEBUG: have_waited[$have_waited]timeout[$timeout]"; + next EXPECT; + } + my $why = "could not answer a question during the dialog"; + $CPAN::Frontend->mywarn("Failing: $why\n"); + $self->{$phase} = + CPAN::Distrostatus->new("NO $why"); + return 0; + } + } +} + +sub _run_via_expect_deterministic { + my($self,$expo,$phase,$expect_model) = @_; + my $ran_into_timeout; + my $ran_into_eof; + my $timeout = $expect_model->{timeout} || 15; # currently unsettable + my $expecta = $expect_model->{talk}; + EXPECT: for (my $i = 0; $i <= $#$expecta; $i+=2) { + my($re,$send) = @$expecta[$i,$i+1]; + CPAN->debug("timeout[$timeout]re[$re]") if $CPAN::DEBUG; + my $regex = eval "qr{$re}"; + $expo->expect($timeout, + [ eof => sub { + my $but = $expo->clear_accum; + $CPAN::Frontend->mywarn("EOF (maybe harmless) +expected[$regex]\nbut[$but]\n\n"); + $ran_into_eof++; + } ], + [ timeout => sub { + my $but = $expo->clear_accum; + $CPAN::Frontend->mywarn("TIMEOUT +expected[$regex]\nbut[$but]\n\n"); + $ran_into_timeout++; + } ], + -re => $regex); + if ($ran_into_timeout) { + # note that the caller expects 0 for success + $self->{$phase} = + CPAN::Distrostatus->new("NO timeout during expect dialog"); + return 0; + } elsif ($ran_into_eof) { + last EXPECT; + } + $expo->send($send); + } + $expo->soft_close; + return $expo->exitstatus(); +} + +#-> CPAN::Distribution::_validate_distropref +sub _validate_distropref { + my($self,@args) = @_; + if ( + $CPAN::META->has_inst("CPAN::Kwalify") + && + $CPAN::META->has_inst("Kwalify") + ) { + eval {CPAN::Kwalify::_validate("distroprefs",@args);}; + if ($@) { + $CPAN::Frontend->mywarn($@); + } + } else { + CPAN->debug("not validating '@args'") if $CPAN::DEBUG; + } +} + +#-> CPAN::Distribution::_find_prefs +sub _find_prefs { + my($self) = @_; + my $distroid = $self->pretty_id; + #CPAN->debug("distroid[$distroid]") if $CPAN::DEBUG; + my $prefs_dir = $CPAN::Config->{prefs_dir}; + return if $prefs_dir =~ /^\s*$/; + eval { File::Path::mkpath($prefs_dir); }; + if ($@) { + $CPAN::Frontend->mydie("Cannot create directory $prefs_dir"); + } + # shortcut if there are no distroprefs files + { + my $dh = DirHandle->new($prefs_dir) or $CPAN::Frontend->mydie("Couldn't open '$prefs_dir': $!"); + my @files = map { /\.(yml|dd|st)\z/i } $dh->read; + return unless @files; + } + my $yaml_module = CPAN::_yaml_module(); + my $ext_map = {}; + my @extensions; + if ($CPAN::META->has_inst($yaml_module)) { + $ext_map->{yml} = 'CPAN'; + } else { + my @fallbacks; + if ($CPAN::META->has_inst("Data::Dumper")) { + push @fallbacks, $ext_map->{dd} = 'Data::Dumper'; + } + if ($CPAN::META->has_inst("Storable")) { + push @fallbacks, $ext_map->{st} = 'Storable'; + } + if (@fallbacks) { + local $" = " and "; + unless ($self->{have_complained_about_missing_yaml}++) { + $CPAN::Frontend->mywarnonce("'$yaml_module' not installed, falling back ". + "to @fallbacks to read prefs '$prefs_dir'\n"); + } + } else { + unless ($self->{have_complained_about_missing_yaml}++) { + $CPAN::Frontend->mywarnonce("'$yaml_module' not installed, cannot ". + "read prefs '$prefs_dir'\n"); + } + } + } + my $finder = CPAN::Distroprefs->find($prefs_dir, $ext_map); + DIRENT: while (my $result = $finder->next) { + if ($result->is_warning) { + $CPAN::Frontend->mywarn($result->as_string); + $CPAN::Frontend->mysleep(1); + next DIRENT; + } elsif ($result->is_fatal) { + $CPAN::Frontend->mydie($result->as_string); + } + + my @prefs = @{ $result->prefs }; + + ELEMENT: for my $y (0..$#prefs) { + my $pref = $prefs[$y]; + $self->_validate_distropref($pref->data, $result->abs, $y); + + # I don't know why we silently skip when there's no match, but + # complain if there's an empty match hashref, and there's no + # comment explaining why -- hdp, 2008-03-18 + unless ($pref->has_any_match) { + next ELEMENT; + } + + unless ($pref->has_valid_subkeys) { + $CPAN::Frontend->mydie(sprintf + "Nonconforming .%s file '%s': " . + "missing match/* subattribute. " . + "Please remove, cannot continue.", + $result->ext, $result->abs, + ); + } + + my $arg = { + env => \%ENV, + distribution => $distroid, + perl => \&CPAN::find_perl, + perlconfig => \%Config::Config, + module => sub { [ $self->containsmods ] }, + }; + + if ($pref->matches($arg)) { + return { + prefs => $pref->data, + prefs_file => $result->abs, + prefs_file_doc => $y, + }; + } + + } + } + return; +} + +# CPAN::Distribution::prefs +sub prefs { + my($self) = @_; + if (exists $self->{negative_prefs_cache} + && + $self->{negative_prefs_cache} != $CPAN::CurrentCommandId + ) { + delete $self->{negative_prefs_cache}; + delete $self->{prefs}; + } + if (exists $self->{prefs}) { + return $self->{prefs}; # XXX comment out during debugging + } + if ($CPAN::Config->{prefs_dir}) { + CPAN->debug("prefs_dir[$CPAN::Config->{prefs_dir}]") if $CPAN::DEBUG; + my $prefs = $self->_find_prefs(); + $prefs ||= ""; # avoid warning next line + CPAN->debug("prefs[$prefs]") if $CPAN::DEBUG; + if ($prefs) { + for my $x (qw(prefs prefs_file prefs_file_doc)) { + $self->{$x} = $prefs->{$x}; + } + my $bs = sprintf( + "%s[%s]", + File::Basename::basename($self->{prefs_file}), + $self->{prefs_file_doc}, + ); + my $filler1 = "_" x 22; + my $filler2 = int(66 - length($bs))/2; + $filler2 = 0 if $filler2 < 0; + $filler2 = " " x $filler2; + $CPAN::Frontend->myprint(" +$filler1 D i s t r o P r e f s $filler1 +$filler2 $bs $filler2 +"); + $CPAN::Frontend->mysleep(1); + return $self->{prefs}; + } + } + $self->{negative_prefs_cache} = $CPAN::CurrentCommandId; + return $self->{prefs} = +{}; +} + +# CPAN::Distribution::_make_phase_arg +sub _make_phase_arg { + my($self, $phase) = @_; + my $_make_phase_arg; + my $prefs = $self->prefs; + if ( + $prefs + && exists $prefs->{$phase} + && exists $prefs->{$phase}{args} + && $prefs->{$phase}{args} + ) { + $_make_phase_arg = join(" ", + map {CPAN::HandleConfig + ->safe_quote($_)} @{$prefs->{$phase}{args}}, + ); + } + +# cpan[2]> o conf make[TAB] +# make make_install_make_command +# make_arg makepl_arg +# make_install_arg +# cpan[2]> o conf mbuild[TAB] +# mbuild_arg mbuild_install_build_command +# mbuild_install_arg mbuildpl_arg + + my $mantra; # must switch make/mbuild here + if ($self->{modulebuild}) { + $mantra = "mbuild"; + } else { + $mantra = "make"; + } + my %map = ( + pl => "pl_arg", + make => "_arg", + test => "_test_arg", # does not really exist but maybe + # will some day and now protects + # us from unini warnings + install => "_install_arg", + ); + my $phase_underscore_meshup = $map{$phase}; + my $what = sprintf "%s%s", $mantra, $phase_underscore_meshup; + + $_make_phase_arg ||= $CPAN::Config->{$what}; + return $_make_phase_arg; +} + +# CPAN::Distribution::_make_command +sub _make_command { + my ($self) = @_; + if ($self) { + return + CPAN::HandleConfig + ->safe_quote( + CPAN::HandleConfig->prefs_lookup($self, + q{make}) + || $Config::Config{make} + || 'make' + ); + } else { + # Old style call, without object. Deprecated + Carp::confess("CPAN::_make_command() used as function. Don't Do That."); + return + safe_quote(undef, + CPAN::HandleConfig->prefs_lookup($self,q{make}) + || $CPAN::Config->{make} + || $Config::Config{make} + || 'make'); + } +} + +sub _make_install_make_command { + my ($self) = @_; + my $mimc = + CPAN::HandleConfig->prefs_lookup($self, q{make_install_make_command}); + return $self->_make_command() unless $mimc; + + # Quote the "make install" make command on Windows, where it is commonly + # found in, e.g., C:\Program Files\... and therefore needs quoting. We can't + # do this in general because the command maybe "sudo make..." (i.e. a + # program with arguments), but that is unlikely to be the case on Windows. + $mimc = CPAN::HandleConfig->safe_quote($mimc) if $^O eq 'MSWin32'; + + return $mimc; +} + +#-> sub CPAN::Distribution::is_locally_optional +sub is_locally_optional { + my($self, $prereq_pm, $prereq) = @_; + $prereq_pm ||= $self->{prereq_pm}; + my($nmo,$opt); + for my $rt (qw(requires build_requires)) { + if (exists $prereq_pm->{$rt}{$prereq}) { + # rt 121914 + $nmo ||= $CPAN::META->instance("CPAN::Module",$prereq); + my $av = $nmo->available_version; + return 0 if !$av || CPAN::Version->vlt($av,$prereq_pm->{$rt}{$prereq}); + } + if (exists $prereq_pm->{"opt_$rt"}{$prereq}) { + $opt = 1; + } + } + return $opt||0; +} + +#-> sub CPAN::Distribution::follow_prereqs ; +sub follow_prereqs { + my($self) = shift; + my($slot) = shift; + my(@prereq_tuples) = grep {$_->[0] ne "perl"} @_; + return unless @prereq_tuples; + my(@good_prereq_tuples); + for my $p (@prereq_tuples) { + # e.g. $p = ['Devel::PartialDump', 'r', 1] + # promote if possible + if ($p->[1] =~ /^(r|c)$/) { + push @good_prereq_tuples, $p; + } elsif ($p->[1] =~ /^(b)$/) { + my $reqtype = CPAN::Queue->reqtype_of($p->[0]); + if ($reqtype =~ /^(r|c)$/) { + push @good_prereq_tuples, [$p->[0], $reqtype, $p->[2]]; + } else { + push @good_prereq_tuples, $p; + } + } else { + die "Panic: in follow_prereqs: reqtype[$p->[1]] seen, should never happen"; + } + } + my $pretty_id = $self->pretty_id; + my %map = ( + b => "build_requires", + r => "requires", + c => "commandline", + ); + my($filler1,$filler2,$filler3,$filler4); + my $unsat = "Unsatisfied dependencies detected during"; + my $w = length($unsat) > length($pretty_id) ? length($unsat) : length($pretty_id); + { + my $r = int(($w - length($unsat))/2); + my $l = $w - length($unsat) - $r; + $filler1 = "-"x4 . " "x$l; + $filler2 = " "x$r . "-"x4 . "\n"; + } + { + my $r = int(($w - length($pretty_id))/2); + my $l = $w - length($pretty_id) - $r; + $filler3 = "-"x4 . " "x$l; + $filler4 = " "x$r . "-"x4 . "\n"; + } + $CPAN::Frontend-> + myprint("$filler1 $unsat $filler2". + "$filler3 $pretty_id $filler4". + join("", map {sprintf " %s \[%s%s]\n", $_->[0], $map{$_->[1]}, $self->is_locally_optional(undef,$_->[0]) ? ",optional" : ""} @good_prereq_tuples), + ); + my $follow = 0; + if ($CPAN::Config->{prerequisites_policy} eq "follow") { + $follow = 1; + } elsif ($CPAN::Config->{prerequisites_policy} eq "ask") { + my $answer = CPAN::Shell::colorable_makemaker_prompt( +"Shall I follow them and prepend them to the queue +of modules we are processing right now?", "yes"); + $follow = $answer =~ /^\s*y/i; + } else { + my @prereq = map { $_->[0] } @good_prereq_tuples; + local($") = ", "; + $CPAN::Frontend-> + myprint(" Ignoring dependencies on modules @prereq\n"); + } + if ($follow) { + my $id = $self->id; + my(@to_queue_mand,@to_queue_opt); + for my $gp (@good_prereq_tuples) { + my($prereq,$reqtype,$optional) = @$gp; + my $qthing = +{qmod=>$prereq,reqtype=>$reqtype,optional=>$optional}; + if ($optional && + $self->is_locally_optional(undef,$prereq) + ){ + # Since we do not depend on this one, we do not need + # this in a mandatory arrangement: + push @to_queue_opt, $qthing; + } else { + my $any = CPAN::Shell->expandany($prereq); + $self->{$slot . "_for"}{$any->id}++; + if ($any) { + unless ($optional) { + # No recursion check in an optional area of the tree + $any->color_cmd_tmps(0,2); + } + } else { + $CPAN::Frontend->mywarn("Warning (maybe a bug): Cannot expand prereq '$prereq'\n"); + $CPAN::Frontend->mysleep(2); + } + # order everything that is not locally_optional just + # like mandatory items: this keeps leaves before + # branches + unshift @to_queue_mand, $qthing; + } + } + if (@to_queue_mand) { + unshift @to_queue_mand, {qmod => $id, reqtype => $self->{reqtype}, optional=> !$self->{mandatory}}; + CPAN::Queue->jumpqueue(@to_queue_opt,@to_queue_mand); + $self->{$slot} = "Delayed until after prerequisites"; + return 1; # signal we need dependencies + } elsif (@to_queue_opt) { + CPAN::Queue->jumpqueue(@to_queue_opt); + } + } + return; +} + +sub _feature_depends { + my($self) = @_; + my $meta_yml = $self->parse_meta_yml(); + my $optf = $meta_yml->{optional_features} or return; + if (!ref $optf or ref $optf ne "HASH"){ + $CPAN::Frontend->mywarn("The content of optional_features is not a HASH reference. Cannot use it.\n"); + $optf = {}; + } + my $wantf = $self->prefs->{features} or return; + if (!ref $wantf or ref $wantf ne "ARRAY"){ + $CPAN::Frontend->mywarn("The content of 'features' is not an ARRAY reference. Cannot use it.\n"); + $wantf = []; + } + my $dep = +{}; + for my $wf (@$wantf) { + if (my $f = $optf->{$wf}) { + $CPAN::Frontend->myprint("Found the demanded feature '$wf' that ". + "is accompanied by this description:\n". + $f->{description}. + "\n\n" + ); + # configure_requires currently not in the spec, unlikely to be useful anyway + for my $reqtype (qw(configure_requires build_requires requires)) { + my $reqhash = $f->{$reqtype} or next; + while (my($k,$v) = each %$reqhash) { + $dep->{$reqtype}{$k} = $v; + } + } + } else { + $CPAN::Frontend->mywarn("The demanded feature '$wf' was not ". + "found in the META.yml file". + "\n\n" + ); + } + } + $dep; +} + +sub prereqs_for_slot { + my($self,$slot) = @_; + my($prereq_pm); + unless ($CPAN::META->has_usable("CPAN::Meta::Requirements")) { + my $whynot = "not available"; + if (defined $CPAN::Meta::Requirements::VERSION) { + $whynot = "version $CPAN::Meta::Requirements::VERSION not sufficient"; + } + $CPAN::Frontend->mywarn("CPAN::Meta::Requirements $whynot\n"); + my $before = ""; + if ($self->{CALLED_FOR}){ + if ($self->{CALLED_FOR} =~ + /^( + CPAN::Meta::Requirements + |CPAN::DistnameInfo + |version + |parent + |ExtUtils::MakeMaker + |Test::Harness + )$/x) { + $CPAN::Frontend->mywarn("Please install CPAN::Meta::Requirements ". + "as soon as possible; it is needed for a reliable operation of ". + "the cpan shell; setting requirements to nil for '$1' for now ". + "to prevent deadlock during bootstrapping\n"); + return; + } + $before = " before $self->{CALLED_FOR}"; + } + $CPAN::Frontend->mydie("Please install CPAN::Meta::Requirements manually$before"); + } + my $merged = CPAN::Meta::Requirements->new; + my $prefs_depends = $self->prefs->{depends}||{}; + my $feature_depends = $self->_feature_depends(); + if ($slot eq "configure_requires_later") { + for my $hash ( $self->configure_requires, + $prefs_depends->{configure_requires}, + $feature_depends->{configure_requires}, + ) { + $merged->add_requirements( + CPAN::Meta::Requirements->from_string_hash($hash) + ); + } + if (-f "Build.PL" + && ! -f File::Spec->catfile($self->{build_dir},"Makefile.PL") + && ! $merged->requirements_for_module("Module::Build") + && ! $CPAN::META->has_inst("Module::Build") + ) { + $CPAN::Frontend->mywarn( + " Warning: CPAN.pm discovered Module::Build as undeclared prerequisite.\n". + " Adding it now as such.\n" + ); + $CPAN::Frontend->mysleep(5); + $merged->add_minimum( "Module::Build" => 0 ); + delete $self->{writemakefile}; + } + $prereq_pm = {}; # configure_requires defined as "b" + } elsif ($slot eq "later") { + my $prereq_pm_0 = $self->prereq_pm || {}; + for my $reqtype (qw(requires build_requires opt_requires opt_build_requires)) { + $prereq_pm->{$reqtype} = {%{$prereq_pm_0->{$reqtype}||{}}}; # copy to not pollute it + for my $dep ($prefs_depends,$feature_depends) { + for my $k (keys %{$dep->{$reqtype}||{}}) { + $prereq_pm->{$reqtype}{$k} = $dep->{$reqtype}{$k}; + } + } + } + # XXX what about optional_req|breq? -- xdg, 2012-04-01 + for my $hash ( + $prereq_pm->{requires}, + $prereq_pm->{build_requires}, + $prereq_pm->{opt_requires}, + $prereq_pm->{opt_build_requires}, + + ) { + $merged->add_requirements( + CPAN::Meta::Requirements->from_string_hash($hash) + ); + } + } else { + die "Panic: illegal slot '$slot'"; + } + return ($merged->as_string_hash, $prereq_pm); +} + +#-> sub CPAN::Distribution::unsat_prereq ; +# return ([Foo,"r"],[Bar,"b"]) for normal modules +# return ([perl=>5.008]) if we need a newer perl than we are running under +# (sorry for the inconsistency, it was an accident) +sub unsat_prereq { + my($self,$slot) = @_; + my($merged_hash,$prereq_pm) = $self->prereqs_for_slot($slot); + my(@need); + unless ($CPAN::META->has_usable("CPAN::Meta::Requirements")) { + $CPAN::Frontend->mywarn("CPAN::Meta::Requirements not available, please install as soon as possible, trying to continue with severly limited capabilities\n"); + return; + } + my $merged = CPAN::Meta::Requirements->from_string_hash($merged_hash); + my @merged = sort $merged->required_modules; + CPAN->debug("all merged_prereqs[@merged]") if $CPAN::DEBUG; + NEED: for my $need_module ( @merged ) { + my $need_version = $merged->requirements_for_module($need_module); + my($available_version,$inst_file,$available_file,$nmo); + if ($need_module eq "perl") { + $available_version = $]; + $available_file = CPAN::find_perl(); + } else { + if (CPAN::_sqlite_running()) { + CPAN::Index->reload; + $CPAN::SQLite->search("CPAN::Module",$need_module); + } + $nmo = $CPAN::META->instance("CPAN::Module",$need_module); + $inst_file = $nmo->inst_file || ''; + $available_file = $nmo->available_file || ''; + $available_version = $nmo->available_version; + if ($nmo->uptodate) { + my $accepts = eval { + $merged->accepts_module($need_module, $available_version); + }; + unless ($accepts) { + my $rq = $merged->requirements_for_module( $need_module ); + $CPAN::Frontend->mywarn( + "Warning: Version '$available_version' of ". + "'$need_module' is up to date but does not ". + "fulfill requirements ($rq). I will continue, ". + "but chances to succeed are low.\n"); + } + next NEED; + } + + # if they have not specified a version, we accept any + # installed one; in that case inst_file is always + # sufficient and available_file is sufficient on + # both build_requires and configure_requires + my $sufficient = $inst_file || + ( exists $prereq_pm->{requires}{$need_module} ? 0 : $available_file ); + if ( $sufficient + and ( # a few quick short circuits + not defined $need_version + or $need_version eq '0' # "==" would trigger warning when not numeric + or $need_version eq "undef" + )) { + unless ($nmo->inst_deprecated) { + next NEED; + } + } + } + + # We only want to install prereqs if either they're not installed + # or if the installed version is too old. We cannot omit this + # check, because if 'force' is in effect, nobody else will check. + # But we don't want to accept a deprecated module installed as part + # of the Perl core, so we continue if the available file is the installed + # one and is deprecated + + if ( $available_file ) { + my $fulfills_all_version_rqs = $self->_fulfills_all_version_rqs + ( + $need_module, + $available_file, + $available_version, + $need_version, + ); + if ( $inst_file + && $available_file eq $inst_file + && $nmo->inst_deprecated + ) { + # continue installing as a prereq. we really want that + # because the deprecated module may spit out warnings + # and third party did not know until today. Only one + # exception is OK, because CPANPLUS is special after + # all: + if ( $fulfills_all_version_rqs and + $nmo->id =~ /^CPANPLUS(?:::Dist::Build)$/ + ) { + # here we have an available version that is good + # enough although deprecated (preventing circular + # loop CPANPLUS => CPANPLUS::Dist::Build RT#83042) + next NEED; + } + } elsif ( + $self->{reqtype} # e.g. maybe we came via goto? + && $self->{reqtype} =~ /^(r|c)$/ + && ( exists $prereq_pm->{requires}{$need_module} + || exists $prereq_pm->{opt_requires}{$need_module} ) + && $nmo + && !$inst_file + ) { + # continue installing as a prereq; this may be a + # distro we already used when it was a build_requires + # so we did not install it. But suddenly somebody + # wants it as a requires + my $need_distro = $nmo->distribution; + if ($need_distro->{install} && $need_distro->{install}->failed && $need_distro->{install}->text =~ /is only/) { + my $id = $need_distro->pretty_id; + $CPAN::Frontend->myprint("Promoting $id from build_requires to requires due $need_module\n"); + delete $need_distro->{install}; # promote to another installation attempt + $need_distro->{reqtype} = "r"; + $need_distro->install; + next NEED; + } + } + else { + next NEED if $fulfills_all_version_rqs; + } + } + + if ($need_module eq "perl") { + return ["perl", $need_version]; + } + $self->{sponsored_mods}{$need_module} ||= 0; + CPAN->debug("need_module[$need_module]s/s/n[$self->{sponsored_mods}{$need_module}]") if $CPAN::DEBUG; + if (my $sponsoring = $self->{sponsored_mods}{$need_module}++) { + # We have already sponsored it and for some reason it's still + # not available. So we do ... what?? + + # if we push it again, we have a potential infinite loop + + # The following "next" was a very problematic construct. + # It helped a lot but broke some day and had to be + # replaced. + + # We must be able to deal with modules that come again and + # again as a prereq and have themselves prereqs and the + # queue becomes long but finally we would find the correct + # order. The RecursiveDependency check should trigger a + # die when it's becoming too weird. Unfortunately removing + # this next breaks many other things. + + # The bug that brought this up is described in Todo under + # "5.8.9 cannot install Compress::Zlib" + + # next; # this is the next that had to go away + + # The following "next NEED" are fine and the error message + # explains well what is going on. For example when the DBI + # fails and consequently DBD::SQLite fails and now we are + # processing CPAN::SQLite. Then we must have a "next" for + # DBD::SQLite. How can we get it and how can we identify + # all other cases we must identify? + + my $do = $nmo->distribution; + next NEED unless $do; # not on CPAN + if (CPAN::Version->vcmp($need_version, $nmo->ro->{CPAN_VERSION}) > 0){ + $CPAN::Frontend->mywarn("Warning: Prerequisite ". + "'$need_module => $need_version' ". + "for '$self->{ID}' seems ". + "not available according to the indices\n" + ); + next NEED; + } + NOSAYER: for my $nosayer ( + "unwrapped", + "writemakefile", + "signature_verify", + "make", + "make_test", + "install", + "make_clean", + ) { + if ($do->{$nosayer}) { + my $selfid = $self->pretty_id; + my $did = $do->pretty_id; + if (UNIVERSAL::can($do->{$nosayer},"failed") ? + $do->{$nosayer}->failed : + $do->{$nosayer} =~ /^NO/) { + if ($nosayer eq "make_test" + && + $do->{make_test}{COMMANDID} != $CPAN::CurrentCommandId + ) { + next NOSAYER; + } + ### XXX don't complain about missing optional deps -- xdg, 2012-04-01 + if ($self->is_locally_optional($prereq_pm, $need_module)) { + # don't complain about failing optional prereqs + } + else { + $CPAN::Frontend->mywarn("Warning: Prerequisite ". + "'$need_module => $need_version' ". + "for '$selfid' failed when ". + "processing '$did' with ". + "'$nosayer => $do->{$nosayer}'. Continuing, ". + "but chances to succeed are limited.\n" + ); + $CPAN::Frontend->mysleep($sponsoring/10); + } + next NEED; + } else { # the other guy succeeded + if ($nosayer =~ /^(install|make_test)$/) { + # we had this with + # DMAKI/DateTime-Calendar-Chinese-0.05.tar.gz + # in 2007-03 for 'make install' + # and 2008-04: #30464 (for 'make test') + # $CPAN::Frontend->mywarn("Warning: Prerequisite ". + # "'$need_module => $need_version' ". + # "for '$selfid' already built ". + # "but the result looks suspicious. ". + # "Skipping another build attempt, ". + # "to prevent looping endlessly.\n" + # ); + next NEED; + } + } + } + } + } + my $needed_as; + if (0) { + } elsif (exists $prereq_pm->{requires}{$need_module} + || exists $prereq_pm->{opt_requires}{$need_module} + ) { + $needed_as = "r"; + } elsif ($slot eq "configure_requires_later") { + # in ae872487d5 we said: C< we have not yet run the + # {Build,Makefile}.PL, we must presume "r" >; but the + # meta.yml standard says C< These dependencies are not + # required after the distribution is installed. >; so now + # we change it back to "b" and care for the proper + # promotion later. + $needed_as = "b"; + } else { + $needed_as = "b"; + } + # here need to flag as optional for recommends/suggests + # -- xdg, 2012-04-01 + $self->debug(sprintf "%s manadory?[%s]", + $self->pretty_id, + $self->{mandatory}) + if $CPAN::DEBUG; + my $optional = !$self->{mandatory} + || $self->is_locally_optional($prereq_pm, $need_module); + push @need, [$need_module,$needed_as,$optional]; + } + my @unfolded = map { "[".join(",",@$_)."]" } @need; + CPAN->debug("returning from unsat_prereq[@unfolded]") if $CPAN::DEBUG; + @need; +} + +sub _fulfills_all_version_rqs { + my($self,$need_module,$available_file,$available_version,$need_version) = @_; + my(@all_requirements) = split /\s*,\s*/, $need_version; + local($^W) = 0; + my $ok = 0; + RQ: for my $rq (@all_requirements) { + if ($rq =~ s|>=\s*||) { + } elsif ($rq =~ s|>\s*||) { + # 2005-12: one user + if (CPAN::Version->vgt($available_version,$rq)) { + $ok++; + } + next RQ; + } elsif ($rq =~ s|!=\s*||) { + # 2005-12: no user + if (CPAN::Version->vcmp($available_version,$rq)) { + $ok++; + next RQ; + } else { + $ok=0; + last RQ; + } + } elsif ($rq =~ m|<=?\s*|) { + # 2005-12: no user + $CPAN::Frontend->mywarn("Downgrading not supported (rq[$rq])\n"); + $ok++; + next RQ; + } elsif ($rq =~ s|==\s*||) { + # 2009-07: ELLIOTJS/Perl-Critic-1.099_002.tar.gz + if (CPAN::Version->vcmp($available_version,$rq)) { + $ok=0; + last RQ; + } else { + $ok++; + next RQ; + } + } + if (! CPAN::Version->vgt($rq, $available_version)) { + $ok++; + } + CPAN->debug(sprintf("need_module[%s]available_file[%s]". + "available_version[%s]rq[%s]ok[%d]", + $need_module, + $available_file, + $available_version, + CPAN::Version->readable($rq), + $ok, + )) if $CPAN::DEBUG; + } + my $ret = $ok == @all_requirements; + CPAN->debug(sprintf("need_module[%s]ok[%s]all_requirements[%d]",$need_module, $ok, scalar @all_requirements)) if $CPAN::DEBUG; + return $ret; +} + +#-> sub CPAN::Distribution::read_meta +# read any sort of meta files, return CPAN::Meta object if no errors +sub read_meta { + my($self) = @_; + my $meta_file = $self->pick_meta_file + or return; + + return unless $CPAN::META->has_usable("CPAN::Meta"); + my $meta = eval { CPAN::Meta->load_file($meta_file)} + or return; + + # Very old EU::MM could have wrong META + if ($meta_file eq 'META.yml' + && $meta->generated_by =~ /ExtUtils::MakeMaker version ([\d\._]+)/ + ) { + my $eummv = do { local $^W = 0; $1+0; }; + return if $eummv < 6.2501; + } + + return $meta; +} + +#-> sub CPAN::Distribution::read_yaml ; +# XXX This should be DEPRECATED -- dagolden, 2011-02-05 +sub read_yaml { + my($self) = @_; + my $meta_file = $self->pick_meta_file('\.yml$'); + $self->debug("meta_file[$meta_file]") if $CPAN::DEBUG; + return unless $meta_file; + my $yaml; + eval { $yaml = $self->parse_meta_yml($meta_file) }; + if ($@ or ! $yaml) { + return undef; # if we die, then we cannot read YAML's own META.yml + } + # not "authoritative" + if (defined $yaml && (! ref $yaml || ref $yaml ne "HASH")) { + $CPAN::Frontend->mywarn("META.yml does not seem to be conforming, cannot use it.\n"); + $yaml = undef; + } + $self->debug(sprintf "yaml[%s]", $yaml || "UNDEF") + if $CPAN::DEBUG; + $self->debug($yaml) if $CPAN::DEBUG && $yaml; + # MYMETA.yml is static and authoritative by definition + if ( $meta_file =~ /MYMETA\.yml/ ) { + return $yaml; + } + # META.yml is authoritative only if dynamic_config is defined and false + if ( defined $yaml->{dynamic_config} && ! $yaml->{dynamic_config} ) { + return $yaml; + } + # otherwise, we can't use what we found + return undef; +} + +#-> sub CPAN::Distribution::configure_requires ; +sub configure_requires { + my($self) = @_; + return unless my $meta_file = $self->pick_meta_file('^META'); + if (my $meta_obj = $self->read_meta) { + my $prereqs = $meta_obj->effective_prereqs; + my $cr = $prereqs->requirements_for(qw/configure requires/); + return $cr ? $cr->as_string_hash : undef; + } + else { + my $yaml = eval { $self->parse_meta_yml($meta_file) }; + return $yaml->{configure_requires}; + } +} + +#-> sub CPAN::Distribution::prereq_pm ; +sub prereq_pm { + my($self) = @_; + return unless $self->{writemakefile} # no need to have succeeded + # but we must have run it + || $self->{modulebuild}; + unless ($self->{build_dir}) { + return; + } + # no Makefile/Build means configuration aborted, so don't look for prereqs + my $makefile = File::Spec->catfile($self->{build_dir}, $^O eq 'VMS' ? 'descrip.mms' : 'Makefile'); + my $buildfile = File::Spec->catfile($self->{build_dir}, $^O eq 'VMS' ? 'Build.com' : 'Build'); + return unless -f $makefile || -f $buildfile; + CPAN->debug(sprintf "writemakefile[%s]modulebuild[%s]", + $self->{writemakefile}||"", + $self->{modulebuild}||"", + ) if $CPAN::DEBUG; + my($req,$breq, $opt_req, $opt_breq); + my $meta_obj = $self->read_meta; + # META/MYMETA is only authoritative if dynamic_config is false + if ($meta_obj && ! $meta_obj->dynamic_config) { + my $prereqs = $meta_obj->effective_prereqs; + my $requires = $prereqs->requirements_for(qw/runtime requires/); + my $build_requires = $prereqs->requirements_for(qw/build requires/); + my $test_requires = $prereqs->requirements_for(qw/test requires/); + # XXX we don't yet distinguish build vs test, so merge them for now + $build_requires->add_requirements($test_requires); + $req = $requires->as_string_hash; + $breq = $build_requires->as_string_hash; + + # XXX assemble optional_req && optional_breq from recommends/suggests + # depending on corresponding policies -- xdg, 2012-04-01 + CPAN->use_inst("CPAN::Meta::Requirements"); + my $opt_runtime = CPAN::Meta::Requirements->new; + my $opt_build = CPAN::Meta::Requirements->new; + if ( $CPAN::Config->{recommends_policy} ) { + $opt_runtime->add_requirements( $prereqs->requirements_for(qw/runtime recommends/)); + $opt_build->add_requirements( $prereqs->requirements_for(qw/build recommends/)); + $opt_build->add_requirements( $prereqs->requirements_for(qw/test recommends/)); + + } + if ( $CPAN::Config->{suggests_policy} ) { + $opt_runtime->add_requirements( $prereqs->requirements_for(qw/runtime suggests/)); + $opt_build->add_requirements( $prereqs->requirements_for(qw/build suggests/)); + $opt_build->add_requirements( $prereqs->requirements_for(qw/test suggests/)); + } + $opt_req = $opt_runtime->as_string_hash; + $opt_breq = $opt_build->as_string_hash; + } + elsif (my $yaml = $self->read_yaml) { # often dynamic_config prevents a result here + $req = $yaml->{requires} || {}; + $breq = $yaml->{build_requires} || {}; + if ( $CPAN::Config->{recommends_policy} ) { + $opt_req = $yaml->{recommends} || {}; + } + undef $req unless ref $req eq "HASH" && %$req; + if ($req) { + if ($yaml->{generated_by} && + $yaml->{generated_by} =~ /ExtUtils::MakeMaker version ([\d\._]+)/) { + my $eummv = do { local $^W = 0; $1+0; }; + if ($eummv < 6.2501) { + # thanks to Slaven for digging that out: MM before + # that could be wrong because it could reflect a + # previous release + undef $req; + } + } + my $areq; + my $do_replace; + foreach my $k (sort keys %{$req||{}}) { + my $v = $req->{$k}; + next unless defined $v; + if ($v =~ /\d/) { + $areq->{$k} = $v; + } elsif ($k =~ /[A-Za-z]/ && + $v =~ /[A-Za-z]/ && + $CPAN::META->exists("CPAN::Module",$v) + ) { + $CPAN::Frontend->mywarn("Suspicious key-value pair in META.yml's ". + "requires hash: $k => $v; I'll take both ". + "key and value as a module name\n"); + $CPAN::Frontend->mysleep(1); + $areq->{$k} = 0; + $areq->{$v} = 0; + $do_replace++; + } + } + $req = $areq if $do_replace; + } + } + else { + $CPAN::Frontend->mywarnonce("Could not read metadata file. Falling back to other ". + "methods to determine prerequisites\n"); + } + + unless ($req || $breq) { + my $build_dir; + unless ( $build_dir = $self->{build_dir} ) { + return; + } + my $makefile = File::Spec->catfile($build_dir,"Makefile"); + my $fh; + if (-f $makefile + and + $fh = FileHandle->new("<$makefile\0")) { + CPAN->debug("Getting prereq from Makefile") if $CPAN::DEBUG; + local($/) = "\n"; + while (<$fh>) { + last if /MakeMaker post_initialize section/; + my($p) = m{^[\#] + \s+PREREQ_PM\s+=>\s+(.+) + }x; + next unless $p; + # warn "Found prereq expr[$p]"; + + # Regexp modified by A.Speer to remember actual version of file + # PREREQ_PM hash key wants, then add to + while ( $p =~ m/(?:\s)([\w\:]+)=>(q\[.*?\]|undef),?/g ) { + my($m,$n) = ($1,$2); + # When a prereq is mentioned twice: let the bigger + # win; usual culprit is that they declared + # build_requires separately from requires; see + # rt.cpan.org #47774 + my($prevn); + if ( defined $req->{$m} ) { + $prevn = $req->{$m}; + } + if ($n =~ /^q\[(.*?)\]$/) { + $n = $1; + } + if (!$prevn || CPAN::Version->vlt($prevn, $n)){ + $req->{$m} = $n; + } + } + last; + } + } + } + unless ($req || $breq) { + my $build_dir = $self->{build_dir} or die "Panic: no build_dir?"; + my $buildfile = File::Spec->catfile($build_dir,"Build"); + if (-f $buildfile) { + CPAN->debug("Found '$buildfile'") if $CPAN::DEBUG; + my $build_prereqs = File::Spec->catfile($build_dir,"_build","prereqs"); + if (-f $build_prereqs) { + CPAN->debug("Getting prerequisites from '$build_prereqs'") if $CPAN::DEBUG; + my $content = do { local *FH; + open FH, $build_prereqs + or $CPAN::Frontend->mydie("Could not open ". + "'$build_prereqs': $!"); + local $/; + <FH>; + }; + my $bphash = eval $content; + if ($@) { + } else { + $req = $bphash->{requires} || +{}; + $breq = $bphash->{build_requires} || +{}; + } + } + } + } + # XXX needs to be adapted for optional_req & optional_breq -- xdg, 2012-04-01 + if ($req || $breq || $opt_req || $opt_breq ) { + return $self->{prereq_pm} = { + requires => $req, + build_requires => $breq, + opt_requires => $opt_req, + opt_build_requires => $opt_breq, + }; + } +} + +#-> sub CPAN::Distribution::shortcut_test ; +# return values: undef means don't shortcut; 0 means shortcut as fail; +# and 1 means shortcut as success +sub shortcut_test { + my ($self) = @_; + + $self->debug("checking badtestcnt[$self->{ID}]") if $CPAN::DEBUG; + $self->{badtestcnt} ||= 0; + if ($self->{badtestcnt} > 0) { + require Data::Dumper; + CPAN->debug(sprintf "NOREPEAT[%s]", Data::Dumper::Dumper($self)) if $CPAN::DEBUG; + return $self->goodbye("Won't repeat unsuccessful test during this command"); + } + + for my $slot ( qw/later configure_requires_later/ ) { + $self->debug("checking $slot slot[$self->{ID}]") if $CPAN::DEBUG; + return $self->success($self->{$slot}) + if $self->{$slot}; + } + + $self->debug("checking if tests passed[$self->{ID}]") if $CPAN::DEBUG; + if ( $self->{make_test} ) { + if ( + UNIVERSAL::can($self->{make_test},"failed") ? + $self->{make_test}->failed : + $self->{make_test} =~ /^NO/ + ) { + if ( + UNIVERSAL::can($self->{make_test},"commandid") + && + $self->{make_test}->commandid == $CPAN::CurrentCommandId + ) { + return $self->goodbye("Has already been tested within this command"); + } + } else { + # if global "is_tested" has been cleared, we need to mark this to + # be added to PERL5LIB if not already installed + if ($self->tested_ok_but_not_installed) { + $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); + } + return $self->success("Has already been tested successfully"); + } + } + + if ($self->{notest}) { + $self->{make_test} = CPAN::Distrostatus->new("YES"); + return $self->success("Skipping test because of notest pragma"); + } + + return undef; # no shortcut +} + +#-> sub CPAN::Distribution::_exe_files ; +sub _exe_files { + my($self) = @_; + return unless $self->{writemakefile} # no need to have succeeded + # but we must have run it + || $self->{modulebuild}; + unless ($self->{build_dir}) { + return; + } + CPAN->debug(sprintf "writemakefile[%s]modulebuild[%s]", + $self->{writemakefile}||"", + $self->{modulebuild}||"", + ) if $CPAN::DEBUG; + my $build_dir; + unless ( $build_dir = $self->{build_dir} ) { + return; + } + my $makefile = File::Spec->catfile($build_dir,"Makefile"); + my $fh; + my @exe_files; + if (-f $makefile + and + $fh = FileHandle->new("<$makefile\0")) { + CPAN->debug("Getting exefiles from Makefile") if $CPAN::DEBUG; + local($/) = "\n"; + while (<$fh>) { + last if /MakeMaker post_initialize section/; + my($p) = m{^[\#] + \s+EXE_FILES\s+=>\s+\[(.+)\] + }x; + next unless $p; + # warn "Found exefiles expr[$p]"; + my @p = split /,\s*/, $p; + for my $p2 (@p) { + if ($p2 =~ /^q\[(.+)\]/) { + push @exe_files, $1; + } + } + } + } + return \@exe_files if @exe_files; + my $buildparams = File::Spec->catfile($build_dir,"_build","build_params"); + if (-f $buildparams) { + CPAN->debug("Found '$buildparams'") if $CPAN::DEBUG; + my $x = do $buildparams; + for my $sf ($x->[2]{script_files}) { + if (my $reftype = ref $sf) { + if ($reftype eq "ARRAY") { + push @exe_files, @$sf; + } + elsif ($reftype eq "HASH") { + push @exe_files, keys %$sf; + } + else { + $CPAN::Frontend->mywarn("Invalid reftype $reftype for Build.PL 'script_files'\n"); + } + } + elsif (defined $sf) { + push @exe_files, $sf; + } + } + } + return \@exe_files; +} + +#-> sub CPAN::Distribution::test ; +sub test { + my($self) = @_; + + $self->pre_test(); + + if (exists $self->{cleanup_after_install_done}) { + $self->post_test(); + return $self->make; + } + + $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; + if (my $goto = $self->prefs->{goto}) { + $self->post_test(); + return $self->goto($goto); + } + + unless ($self->make){ + $self->post_test(); + return; + } + + if ( defined( my $sc = $self->shortcut_test ) ) { + $self->post_test(); + return $sc; + } + + if ($CPAN::Signal) { + delete $self->{force_update}; + $self->post_test(); + return; + } + # warn "XDEBUG: checking for notest: $self->{notest} $self"; + my $make = $self->{modulebuild} ? "Build" : "make"; + + local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) + ? $ENV{PERL5LIB} + : ($ENV{PERLLIB} || ""); + + local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; + local $ENV{PERL_USE_UNSAFE_INC} = + exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} + ? $ENV{PERL_USE_UNSAFE_INC} : 1; # test + $CPAN::META->set_perl5lib; + local $ENV{MAKEFLAGS}; # protect us from outer make calls + local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; + local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; + + if ($run_allow_installing_within_test) { + my($allow_installing, $why) = $self->_allow_installing; + if (! $allow_installing) { + $CPAN::Frontend->mywarn("Testing/Installation stopped: $why\n"); + $self->introduce_myself; + $self->{make_test} = CPAN::Distrostatus->new("NO -- testing/installation stopped due $why"); + $CPAN::Frontend->mywarn(" [testing] -- NOT OK\n"); + delete $self->{force_update}; + $self->post_test(); + return; + } + } + $CPAN::Frontend->myprint(sprintf "Running %s test for %s\n", $make, $self->pretty_id); + + my $builddir = $self->dir or + $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); + + unless (chdir $builddir) { + $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); + $self->post_test(); + return; + } + + $self->debug("Changed directory to $self->{build_dir}") + if $CPAN::DEBUG; + + if ($^O eq 'MacOS') { + Mac::BuildTools::make_test($self); + $self->post_test(); + return; + } + + if ($self->{modulebuild}) { + my $thm = CPAN::Shell->expand("Module","Test::Harness"); + my $v = $thm->inst_version; + if (CPAN::Version->vlt($v,2.62)) { + # XXX Eric Wilhelm reported this as a bug: klapperl: + # Test::Harness 3.0 self-tests, so that should be 'unless + # installing Test::Harness' + unless ($self->id eq $thm->distribution->id) { + $CPAN::Frontend->mywarn(qq{The version of your Test::Harness is only + '$v', you need at least '2.62'. Please upgrade your Test::Harness.\n}); + $self->{make_test} = CPAN::Distrostatus->new("NO Test::Harness too old"); + $self->post_test(); + return; + } + } + } + + if ( ! $self->{force_update} ) { + # bypass actual tests if "trust_test_report_history" and have a report + my $have_tested_fcn; + if ( $CPAN::Config->{trust_test_report_history} + && $CPAN::META->has_inst("CPAN::Reporter::History") + && ( $have_tested_fcn = CPAN::Reporter::History->can("have_tested" ))) { + if ( my @reports = $have_tested_fcn->( dist => $self->base_id ) ) { + # Do nothing if grade was DISCARD + if ( $reports[-1]->{grade} =~ /^(?:PASS|UNKNOWN)$/ ) { + $self->{make_test} = CPAN::Distrostatus->new("YES"); + # if global "is_tested" has been cleared, we need to mark this to + # be added to PERL5LIB if not already installed + if ($self->tested_ok_but_not_installed) { + $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); + } + $CPAN::Frontend->myprint("Found prior test report -- OK\n"); + $self->post_test(); + return; + } + elsif ( $reports[-1]->{grade} =~ /^(?:FAIL|NA)$/ ) { + $self->{make_test} = CPAN::Distrostatus->new("NO"); + $self->{badtestcnt}++; + $CPAN::Frontend->mywarn("Found prior test report -- NOT OK\n"); + $self->post_test(); + return; + } + } + } + } + + my $system; + my $prefs_test = $self->prefs->{test}; + if (my $commandline + = exists $prefs_test->{commandline} ? $prefs_test->{commandline} : "") { + $system = $commandline; + $ENV{PERL} = CPAN::find_perl(); + } elsif ($self->{modulebuild}) { + $system = sprintf "%s test", $self->_build_command(); + unless (-e "Build" || ($^O eq 'VMS' && -e "Build.com")) { + my $id = $self->pretty_id; + $CPAN::Frontend->mywarn("Alert: no 'Build' file found while trying to test '$id'"); + } + } else { + $system = join " ", $self->_make_command(), "test"; + } + my $make_test_arg = $self->_make_phase_arg("test"); + $system = sprintf("%s%s", + $system, + $make_test_arg ? " $make_test_arg" : "", + ); + my($tests_ok); + my $test_env; + if ($self->prefs->{test}) { + $test_env = $self->prefs->{test}{env}; + } + local @ENV{keys %$test_env} = values %$test_env if $test_env; + my $expect_model = $self->_prefs_with_expect("test"); + my $want_expect = 0; + if ( $expect_model && @{$expect_model->{talk}} ) { + my $can_expect = $CPAN::META->has_inst("Expect"); + if ($can_expect) { + $want_expect = 1; + } else { + $CPAN::Frontend->mywarn("Expect not installed, falling back to ". + "testing without\n"); + } + } + + FORK: { + my $pid = fork; + if (! defined $pid) { # contention + warn "Contention '$!', sleeping 2"; + sleep 2; + redo FORK; + } elsif ($pid) { # parent + if ($^O eq "MSWin32") { + wait; + } else { + SUPERVISE: while (waitpid($pid, WNOHANG) <= 0) { + if ($CPAN::Signal) { + kill 9, -$pid; + } + sleep 1; + } + } + $tests_ok = !$?; + } else { # child + POSIX::setsid() unless $^O eq "MSWin32"; + my $c_ok; + $|=1; + if ($want_expect) { + if ($self->_should_report('test')) { + $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is currently ". + "not supported when distroprefs specify ". + "an interactive test\n"); + } + $c_ok = $self->_run_via_expect($system,'test',$expect_model) == 0; + } elsif ( $self->_should_report('test') ) { + $c_ok = CPAN::Reporter::test($self, $system); + } else { + $c_ok = system($system) == 0; + } + exit !$c_ok; + } + } # FORK + + $self->introduce_myself; + my $but = $self->_make_test_illuminate_prereqs(); + if ( $tests_ok ) { + if ($but) { + $CPAN::Frontend->mywarn("Tests succeeded but $but\n"); + $self->{make_test} = CPAN::Distrostatus->new("NO $but"); + $self->store_persistent_state; + $self->post_test(); + return $self->goodbye("[dependencies] -- NA"); + } + $CPAN::Frontend->myprint(" $system -- OK\n"); + $self->{make_test} = CPAN::Distrostatus->new("YES"); + $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); + # probably impossible to need the next line because badtestcnt + # has a lifespan of one command + delete $self->{badtestcnt}; + } else { + if ($but) { + $but .= "; additionally test harness failed"; + $CPAN::Frontend->mywarn("$but\n"); + $self->{make_test} = CPAN::Distrostatus->new("NO $but"); + } elsif ( $self->{force_update} ) { + $self->{make_test} = CPAN::Distrostatus->new( + "NO but failure ignored because 'force' in effect" + ); + } elsif ($CPAN::Signal) { + $self->{make_test} = CPAN::Distrostatus->new("NO -- Interrupted"); + } else { + $self->{make_test} = CPAN::Distrostatus->new("NO"); + } + $self->{badtestcnt}++; + $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); + CPAN::Shell->optprint + ("hint", + sprintf + ("//hint// to see the cpan-testers results for installing this module, try: + reports %s\n", + $self->pretty_id)); + } + $self->store_persistent_state; + + $self->post_test(); + + return $self->{force_update} ? 1 : !! $tests_ok; +} + +sub _make_test_illuminate_prereqs { + my($self) = @_; + my @prereq; + + # local $CPAN::DEBUG = 16; # Distribution + for my $m (sort keys %{$self->{sponsored_mods}}) { + next unless $self->{sponsored_mods}{$m} > 0; + my $m_obj = CPAN::Shell->expand("Module",$m) or next; + # XXX we need available_version which reflects + # $ENV{PERL5LIB} so that already tested but not yet + # installed modules are counted. + my $available_version = $m_obj->available_version; + my $available_file = $m_obj->available_file; + if ($available_version && + !CPAN::Version->vlt($available_version,$self->{prereq_pm}{$m}) + ) { + CPAN->debug("m[$m] good enough available_version[$available_version]") + if $CPAN::DEBUG; + } elsif ($available_file + && ( + !$self->{prereq_pm}{$m} + || + $self->{prereq_pm}{$m} == 0 + ) + ) { + # lex Class::Accessor::Chained::Fast which has no $VERSION + CPAN->debug("m[$m] have available_file[$available_file]") + if $CPAN::DEBUG; + } else { + push @prereq, $m + unless $self->is_locally_optional(undef, $m); + } + } + my $but; + if (@prereq) { + my $cnt = @prereq; + my $which = join ",", @prereq; + $but = $cnt == 1 ? "one dependency not OK ($which)" : + "$cnt dependencies missing ($which)"; + } + $but; +} + +sub _prefs_with_expect { + my($self,$where) = @_; + return unless my $prefs = $self->prefs; + return unless my $where_prefs = $prefs->{$where}; + if ($where_prefs->{expect}) { + return { + mode => "deterministic", + timeout => 15, + talk => $where_prefs->{expect}, + }; + } elsif ($where_prefs->{"eexpect"}) { + return $where_prefs->{"eexpect"}; + } + return; +} + +#-> sub CPAN::Distribution::clean ; +sub clean { + my($self) = @_; + my $make = $self->{modulebuild} ? "Build" : "make"; + $CPAN::Frontend->myprint(sprintf "Running %s clean for %s\n", $make, $self->pretty_id); + unless (exists $self->{archived}) { + $CPAN::Frontend->mywarn("Distribution seems to have never been unzipped". + "/untarred, nothing done\n"); + return 1; + } + unless (exists $self->{build_dir}) { + $CPAN::Frontend->mywarn("Distribution has no own directory, nothing to do.\n"); + return 1; + } + if (exists $self->{writemakefile} + and $self->{writemakefile}->failed + ) { + $CPAN::Frontend->mywarn("No Makefile, don't know how to 'make clean'\n"); + return 1; + } + EXCUSE: { + my @e; + exists $self->{make_clean} and $self->{make_clean} eq "YES" and + push @e, "make clean already called once"; + $CPAN::Frontend->myprint(join "", map {" $_\n"} @e) and return if @e; + } + chdir "$self->{build_dir}" or + Carp::confess("Couldn't chdir to $self->{build_dir}: $!"); + $self->debug("Changed directory to $self->{build_dir}") if $CPAN::DEBUG; + + if ($^O eq 'MacOS') { + Mac::BuildTools::make_clean($self); + return; + } + + my $system; + if ($self->{modulebuild}) { + unless (-f "Build") { + my $cwd = CPAN::anycwd(); + $CPAN::Frontend->mywarn("Alert: no Build file available for 'clean $self->{id}". + " in cwd[$cwd]. Danger, Will Robinson!"); + $CPAN::Frontend->mysleep(5); + } + $system = sprintf "%s clean", $self->_build_command(); + } else { + $system = join " ", $self->_make_command(), "clean"; + } + my $system_ok = system($system) == 0; + $self->introduce_myself; + if ( $system_ok ) { + $CPAN::Frontend->myprint(" $system -- OK\n"); + + # $self->force; + + # Jost Krieger pointed out that this "force" was wrong because + # it has the effect that the next "install" on this distribution + # will untar everything again. Instead we should bring the + # object's state back to where it is after untarring. + + for my $k (qw( + force_update + install + writemakefile + make + make_test + )) { + delete $self->{$k}; + } + $self->{make_clean} = CPAN::Distrostatus->new("YES"); + + } else { + # Hmmm, what to do if make clean failed? + + $self->{make_clean} = CPAN::Distrostatus->new("NO"); + $CPAN::Frontend->mywarn(qq{ $system -- NOT OK\n}); + + # 2006-02-27: seems silly to me to force a make now + # $self->force("make"); # so that this directory won't be used again + + } + $self->store_persistent_state; +} + +#-> sub CPAN::Distribution::check_disabled ; +sub check_disabled { + my ($self) = @_; + $self->debug("checking disabled id[$self->{ID}]") if $CPAN::DEBUG; + if ($self->prefs->{disabled} && ! $self->{force_update}) { + return sprintf( + "Disabled via prefs file '%s' doc %d", + $self->{prefs_file}, + $self->{prefs_file_doc}, + ); + } + return; +} + +#-> sub CPAN::Distribution::goto ; +sub goto { + my($self,$goto) = @_; + $goto = $self->normalize($goto); + my $why = sprintf( + "Goto '$goto' via prefs file '%s' doc %d", + $self->{prefs_file}, + $self->{prefs_file_doc}, + ); + $self->{unwrapped} = CPAN::Distrostatus->new("NO $why"); + # 2007-07-16 akoenig : Better than NA would be if we could inherit + # the status of the $goto distro but given the exceptional nature + # of 'goto' I feel reluctant to implement it + my $goodbye_message = "[goto] -- NA $why"; + $self->goodbye($goodbye_message); + + # inject into the queue + + CPAN::Queue->delete($self->id); + CPAN::Queue->jumpqueue({qmod => $goto, reqtype => $self->{reqtype}}); + + # and run where we left off + + my($method) = (caller(1))[3]; + my $goto_do = CPAN->instance("CPAN::Distribution",$goto); + $goto_do->called_for($self->called_for) unless $goto_do->called_for; + $goto_do->{mandatory} ||= $self->{mandatory}; + $goto_do->{reqtype} ||= $self->{reqtype}; + $goto_do->{coming_from} = $self->pretty_id; + $goto_do->$method(); + CPAN::Queue->delete_first($goto); + # XXX delete_first returns undef; is that what this should return + # up the call stack, eg. return $sefl->goto($goto) -- xdg, 2012-04-04 +} + +#-> sub CPAN::Distribution::shortcut_install ; +# return values: undef means don't shortcut; 0 means shortcut as fail; +# and 1 means shortcut as success +sub shortcut_install { + my ($self) = @_; + + $self->debug("checking previous install results[$self->{ID}]") if $CPAN::DEBUG; + if (exists $self->{install}) { + my $text = UNIVERSAL::can($self->{install},"text") ? + $self->{install}->text : + $self->{install}; + if ($text =~ /^YES/) { + $CPAN::META->is_installed($self->{build_dir}); + return $self->success("Already done"); + } elsif ($text =~ /is only/) { + # e.g. 'is only build_requires': may be overruled later + return $self->goodbye($text); + } else { + # comment in Todo on 2006-02-11; maybe retry? + return $self->goodbye("Already tried without success"); + } + } + + for my $slot ( qw/later configure_requires_later/ ) { + return $self->success($self->{$slot}) + if $self->{$slot}; + } + + return undef; +} + +#-> sub CPAN::Distribution::is_being_sponsored ; + +# returns true if we find a distro object in the queue that has +# sponsored this one +sub is_being_sponsored { + my($self) = @_; + my $iterator = CPAN::Queue->iterator; + QITEM: while (my $q = $iterator->()) { + my $s = $q->as_string; + my $obj = CPAN::Shell->expandany($s) or next QITEM; + my $type = ref $obj; + if ( $type eq 'CPAN::Distribution' ){ + for my $module (sort keys %{$obj->{sponsored_mods} || {}}) { + return 1 if grep { $_ eq $module } $self->containsmods; + } + } + } + return 0; +} + +#-> sub CPAN::Distribution::install ; +sub install { + my($self) = @_; + + $self->pre_install(); + + if (exists $self->{cleanup_after_install_done}) { + return $self->test; + } + + $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; + if (my $goto = $self->prefs->{goto}) { + $self->goto($goto); + $self->post_install(); + return; + } + + unless ($self->test) { + $self->post_install(); + return; + } + + if ( defined( my $sc = $self->shortcut_install ) ) { + $self->post_install(); + return $sc; + } + + if ($CPAN::Signal) { + delete $self->{force_update}; + $self->post_install(); + return; + } + + my $builddir = $self->dir or + $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); + + unless (chdir $builddir) { + $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); + $self->post_install(); + return; + } + + $self->debug("Changed directory to $self->{build_dir}") + if $CPAN::DEBUG; + + my $make = $self->{modulebuild} ? "Build" : "make"; + $CPAN::Frontend->myprint(sprintf "Running %s install for %s\n", $make, $self->pretty_id); + + if ($^O eq 'MacOS') { + Mac::BuildTools::make_install($self); + $self->post_install(); + return; + } + + my $system; + if (my $commandline = $self->prefs->{install}{commandline}) { + $system = $commandline; + $ENV{PERL} = CPAN::find_perl(); + } elsif ($self->{modulebuild}) { + my($mbuild_install_build_command) = + exists $CPAN::HandleConfig::keys{mbuild_install_build_command} && + $CPAN::Config->{mbuild_install_build_command} ? + $CPAN::Config->{mbuild_install_build_command} : + $self->_build_command(); + my $install_directive = $^O eq 'VMS' ? '"install"' : 'install'; + $system = sprintf("%s %s %s", + $mbuild_install_build_command, + $install_directive, + $CPAN::Config->{mbuild_install_arg}, + ); + } else { + my($make_install_make_command) = $self->_make_install_make_command(); + $system = sprintf("%s install %s", + $make_install_make_command, + $CPAN::Config->{make_install_arg}, + ); + } + + my($stderr) = $^O eq "MSWin32" || $^O eq 'VMS' ? "" : " 2>&1 "; + my $brip = CPAN::HandleConfig->prefs_lookup($self, + q{build_requires_install_policy}); + $brip ||="ask/yes"; + my $id = $self->id; + my $reqtype = $self->{reqtype} ||= "c"; # in doubt it was a command + my $want_install = "yes"; + if ($reqtype eq "b") { + if ($brip eq "no") { + $want_install = "no"; + } elsif ($brip =~ m|^ask/(.+)|) { + my $default = $1; + $default = "yes" unless $default =~ /^(y|n)/i; + $want_install = + CPAN::Shell::colorable_makemaker_prompt + ("$id is just needed temporarily during building or testing. ". + "Do you want to install it permanently?", + $default); + } + } + unless ($want_install =~ /^y/i) { + my $is_only = "is only 'build_requires'"; + $self->{install} = CPAN::Distrostatus->new("NO -- $is_only"); + delete $self->{force_update}; + $self->goodbye("Not installing because $is_only"); + $self->post_install(); + return; + } + local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) + ? $ENV{PERL5LIB} + : ($ENV{PERLLIB} || ""); + + local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; + local $ENV{PERL_USE_UNSAFE_INC} = + exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} + ? $ENV{PERL_USE_UNSAFE_INC} : 1; # install + $CPAN::META->set_perl5lib; + local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; + local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; + + my $install_env; + if ($self->prefs->{install}) { + $install_env = $self->prefs->{install}{env}; + } + local @ENV{keys %$install_env} = values %$install_env if $install_env; + + if (! $run_allow_installing_within_test) { + my($allow_installing, $why) = $self->_allow_installing; + if (! $allow_installing) { + $CPAN::Frontend->mywarn("Installation stopped: $why\n"); + $self->introduce_myself; + $self->{install} = CPAN::Distrostatus->new("NO -- installation stopped due $why"); + $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); + delete $self->{force_update}; + $self->post_install(); + return; + } + } + my($pipe) = FileHandle->new("$system $stderr |"); + unless ($pipe) { + $CPAN::Frontend->mywarn("Can't execute $system: $!"); + $self->introduce_myself; + $self->{install} = CPAN::Distrostatus->new("NO"); + $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); + delete $self->{force_update}; + $self->post_install(); + return; + } + my($makeout) = ""; + while (<$pipe>) { + print $_; # intentionally NOT use Frontend->myprint because it + # looks irritating when we markup in color what we + # just pass through from an external program + $makeout .= $_; + } + $pipe->close; + my $close_ok = $? == 0; + $self->introduce_myself; + if ( $close_ok ) { + $CPAN::Frontend->myprint(" $system -- OK\n"); + $CPAN::META->is_installed($self->{build_dir}); + $self->{install} = CPAN::Distrostatus->new("YES"); + if ($CPAN::Config->{'cleanup_after_install'} + && ! $self->is_dot_dist + && ! $self->is_being_sponsored) { + my $parent = File::Spec->catdir( $self->{build_dir}, File::Spec->updir ); + chdir $parent or $CPAN::Frontend->mydie("Couldn't chdir to $parent: $!\n"); + File::Path::rmtree($self->{build_dir}); + my $yml = "$self->{build_dir}.yml"; + if (-e $yml) { + unlink $yml or $CPAN::Frontend->mydie("Couldn't unlink $yml: $!\n"); + } + $self->{cleanup_after_install_done}=1; + } + } else { + $self->{install} = CPAN::Distrostatus->new("NO"); + $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); + my $mimc = + CPAN::HandleConfig->prefs_lookup($self, + q{make_install_make_command}); + if ( + $makeout =~ /permission/s + && $> > 0 + && ( + ! $mimc + || $mimc eq (CPAN::HandleConfig->prefs_lookup($self, + q{make})) + ) + ) { + $CPAN::Frontend->myprint( + qq{----\n}. + qq{ You may have to su }. + qq{to root to install the package\n}. + qq{ (Or you may want to run something like\n}. + qq{ o conf make_install_make_command 'sudo make'\n}. + qq{ to raise your permissions.} + ); + } + } + delete $self->{force_update}; + unless ($CPAN::Config->{'cleanup_after_install'}) { + $self->store_persistent_state; + } + + $self->post_install(); + + return !! $close_ok; +} + +sub blib_pm_walk { + my @queue = grep { -e $_ } File::Spec->catdir("blib","lib"), File::Spec->catdir("blib","arch"); + return sub { + LOOP: { + if (@queue) { + my $file = shift @queue; + if (-d $file) { + my $dh; + opendir $dh, $file or next; + my @newfiles = map { + my @ret; + my $maybedir = File::Spec->catdir($file, $_); + if (-d $maybedir) { + unless (File::Spec->catdir("blib","arch","auto") eq $maybedir) { + # prune the blib/arch/auto directory, no pm files there + @ret = $maybedir; + } + } elsif (/\.pm$/) { + my $mustbefile = File::Spec->catfile($file, $_); + if (-f $mustbefile) { + @ret = $mustbefile; + } + } + @ret; + } grep { + $_ ne "." + && $_ ne ".." + } readdir $dh; + push @queue, @newfiles; + redo LOOP; + } else { + return $file; + } + } else { + return; + } + } + }; +} + +sub _allow_installing { + my($self) = @_; + my $id = my $pretty_id = $self->pretty_id; + if ($self->{CALLED_FOR}) { + $id .= " (called for $self->{CALLED_FOR})"; + } + my $allow_down = CPAN::HandleConfig->prefs_lookup($self,q{allow_installing_module_downgrades}); + $allow_down ||= "ask/yes"; + my $allow_outdd = CPAN::HandleConfig->prefs_lookup($self,q{allow_installing_outdated_dists}); + $allow_outdd ||= "ask/yes"; + return 1 if + $allow_down eq "yes" + && $allow_outdd eq "yes"; + if (($allow_outdd ne "yes") && ! $CPAN::META->has_inst('CPAN::DistnameInfo')) { + return 1 if grep { $_ eq 'CPAN::DistnameInfo'} $self->containsmods; + if ($allow_outdd ne "yes") { + $CPAN::Frontend->mywarn("The current configuration of allow_installing_outdated_dists is '$allow_outdd', but for this option we would need 'CPAN::DistnameInfo' installed. Please install 'CPAN::DistnameInfo' as soon as possible. As long as we are not equipped with 'CPAN::DistnameInfo' this option does not take effect\n"); + $allow_outdd = "yes"; + } + } + return 1 if + $allow_down eq "yes" + && $allow_outdd eq "yes"; + my($dist_version, $dist_dist); + if ($allow_outdd ne "yes"){ + my $dni = CPAN::DistnameInfo->new($pretty_id); + $dist_version = $dni->version; + $dist_dist = $dni->dist; + } + my $iterator = blib_pm_walk(); + my(@down,@outdd); + while (my $file = $iterator->()) { + my $version = CPAN::Module->parse_version($file); + my($volume, $directories, $pmfile) = File::Spec->splitpath( $file ); + my @dirs = File::Spec->splitdir( $directories ); + my(@blib_plus1) = splice @dirs, 0, 2; + my($pmpath) = File::Spec->catfile(grep { length($_) } @dirs, $pmfile); + unless ($allow_down eq "yes") { + if (my $inst_file = $self->_file_in_path($pmpath, \@INC)) { + my $inst_version = CPAN::Module->parse_version($inst_file); + my $cmp = CPAN::Version->vcmp($version, $inst_version); + if ($cmp) { + if ($cmp < 0) { + push @down, { pmpath => $pmpath, version => $version, inst_version => $inst_version }; + } + } + if (@down) { + my $why = "allow_installing_module_downgrades: $id contains downgrading module(s) (e.g. '$down[0]{pmpath}' would downgrade installed '$down[0]{inst_version}' to '$down[0]{version}')"; + if (my($default) = $allow_down =~ m|^ask/(.+)|) { + $default = "yes" unless $default =~ /^(y|n)/i; + my $answer = CPAN::Shell::colorable_makemaker_prompt + ("$why. Do you want to allow installing it?", + $default, "colorize_warn"); + $allow_down = $answer =~ /^\s*y/i ? "yes" : "no"; + } + if ($allow_down eq "no") { + return (0, $why); + } + } + } + } + unless ($allow_outdd eq "yes") { + my @pmpath = (@dirs, $pmfile); + $pmpath[-1] =~ s/\.pm$//; + my $mo = CPAN::Shell->expand("Module",join "::", grep { length($_) } @pmpath); + if ($mo) { + my $cpan_version = $mo->cpan_version; + my $is_lower = CPAN::Version->vlt($version, $cpan_version); + my $other_dist; + if (my $mo_dist = $mo->distribution) { + $other_dist = $mo_dist->pretty_id; + my $dni = CPAN::DistnameInfo->new($other_dist); + if ($dni->dist eq $dist_dist){ + if (CPAN::Version->vgt($dni->version, $dist_version)) { + push @outdd, { + pmpath => $pmpath, + cpan_path => $dni->pathname, + dist_version => $dni->version, + dist_dist => $dni->dist, + }; + } + } + } + } + if (@outdd && $allow_outdd ne "yes") { + my $why = "allow_installing_outdated_dists: $id contains module(s) that are indexed on the CPAN with a different distro: (e.g. '$outdd[0]{pmpath}' is indexed with '$outdd[0]{cpan_path}')"; + if ($outdd[0]{dist_dist} eq $dist_dist) { + $why .= ", and this has a higher distribution-version, i.e. version '$outdd[0]{dist_version}' is higher than '$dist_version')"; + } + if (my($default) = $allow_outdd =~ m|^ask/(.+)|) { + $default = "yes" unless $default =~ /^(y|n)/i; + my $answer = CPAN::Shell::colorable_makemaker_prompt + ("$why. Do you want to allow installing it?", + $default, "colorize_warn"); + $allow_outdd = $answer =~ /^\s*y/i ? "yes" : "no"; + } + if ($allow_outdd eq "no") { + return (0, $why); + } + } + } + } + return 1; +} + +sub _file_in_path { # similar to CPAN::Module::_file_in_path + my($self,$pmpath,$incpath) = @_; + my($dir,@packpath); + foreach $dir (@$incpath) { + my $pmfile = File::Spec->catfile($dir,$pmpath); + if (-f $pmfile) { + return $pmfile; + } + } + return; +} +sub introduce_myself { + my($self) = @_; + $CPAN::Frontend->myprint(sprintf(" %s\n",$self->pretty_id)); +} + +#-> sub CPAN::Distribution::dir ; +sub dir { + shift->{build_dir}; +} + +#-> sub CPAN::Distribution::perldoc ; +sub perldoc { + my($self) = @_; + + my($dist) = $self->id; + my $package = $self->called_for; + + if ($CPAN::META->has_inst("Pod::Perldocs")) { + my($perl) = $self->perl + or $CPAN::Frontend->mydie("Couldn't find executable perl\n"); + my @args = ($perl, q{-MPod::Perldocs}, q{-e}, + q{Pod::Perldocs->run()}, $package); + my($wstatus); + unless ( ($wstatus = system(@args)) == 0 ) { + my $estatus = $wstatus >> 8; + $CPAN::Frontend->myprint(qq{ + Function system("@args") + returned status $estatus (wstat $wstatus) + }); + } + } + else { + $self->_display_url( $CPAN::Defaultdocs . $package ); + } +} + +#-> sub CPAN::Distribution::_check_binary ; +sub _check_binary { + my ($dist,$shell,$binary) = @_; + my ($pid,$out); + + $CPAN::Frontend->myprint(qq{ + _check_binary($binary)\n}) + if $CPAN::DEBUG; + + if ($CPAN::META->has_inst("File::Which")) { + return File::Which::which($binary); + } else { + local *README; + $pid = open README, "which $binary|" + or $CPAN::Frontend->mywarn(qq{Could not fork 'which $binary': $!\n}); + return unless $pid; + while (<README>) { + $out .= $_; + } + close README + or $CPAN::Frontend->mywarn("Could not run 'which $binary': $!\n") + and return; + } + + $CPAN::Frontend->myprint(qq{ + $out \n}) + if $CPAN::DEBUG && $out; + + return $out; +} + +#-> sub CPAN::Distribution::_display_url ; +sub _display_url { + my($self,$url) = @_; + my($res,$saved_file,$pid,$out); + + $CPAN::Frontend->myprint(qq{ + _display_url($url)\n}) + if $CPAN::DEBUG; + + # should we define it in the config instead? + my $html_converter = "html2text.pl"; + + my $web_browser = $CPAN::Config->{'lynx'} || undef; + my $web_browser_out = $web_browser + ? CPAN::Distribution->_check_binary($self,$web_browser) + : undef; + + if ($web_browser_out) { + # web browser found, run the action + my $browser = CPAN::HandleConfig->safe_quote($CPAN::Config->{'lynx'}); + $CPAN::Frontend->myprint(qq{system[$browser $url]}) + if $CPAN::DEBUG; + $CPAN::Frontend->myprint(qq{ +Displaying URL + $url +with browser $browser +}); + $CPAN::Frontend->mysleep(1); + system("$browser $url"); + if ($saved_file) { 1 while unlink($saved_file) } + } else { + # web browser not found, let's try text only + my $html_converter_out = + CPAN::Distribution->_check_binary($self,$html_converter); + $html_converter_out = CPAN::HandleConfig->safe_quote($html_converter_out); + + if ($html_converter_out ) { + # html2text found, run it + $saved_file = CPAN::Distribution->_getsave_url( $self, $url ); + $CPAN::Frontend->mydie(qq{ERROR: problems while getting $url\n}) + unless defined($saved_file); + + local *README; + $pid = open README, "$html_converter $saved_file |" + or $CPAN::Frontend->mydie(qq{ +Could not fork '$html_converter $saved_file': $!}); + my($fh,$filename); + if ($CPAN::META->has_usable("File::Temp")) { + $fh = File::Temp->new( + dir => File::Spec->tmpdir, + template => 'cpan_htmlconvert_XXXX', + suffix => '.txt', + unlink => 0, + ); + $filename = $fh->filename; + } else { + $filename = "cpan_htmlconvert_$$.txt"; + $fh = FileHandle->new(); + open $fh, ">$filename" or die; + } + while (<README>) { + $fh->print($_); + } + close README or + $CPAN::Frontend->mydie(qq{Could not run '$html_converter $saved_file': $!}); + my $tmpin = $fh->filename; + $CPAN::Frontend->myprint(sprintf(qq{ +Run '%s %s' and +saved output to %s\n}, + $html_converter, + $saved_file, + $tmpin, + )) if $CPAN::DEBUG; + close $fh; + local *FH; + open FH, $tmpin + or $CPAN::Frontend->mydie(qq{Could not open "$tmpin": $!}); + my $fh_pager = FileHandle->new; + local($SIG{PIPE}) = "IGNORE"; + my $pager = $CPAN::Config->{'pager'} || "cat"; + $fh_pager->open("|$pager") + or $CPAN::Frontend->mydie(qq{ +Could not open pager '$pager': $!}); + $CPAN::Frontend->myprint(qq{ +Displaying URL + $url +with pager "$pager" +}); + $CPAN::Frontend->mysleep(1); + $fh_pager->print(<FH>); + $fh_pager->close; + } else { + # coldn't find the web browser or html converter + $CPAN::Frontend->myprint(qq{ +You need to install lynx or $html_converter to use this feature.}); + } + } +} + +#-> sub CPAN::Distribution::_getsave_url ; +sub _getsave_url { + my($dist, $shell, $url) = @_; + + $CPAN::Frontend->myprint(qq{ + _getsave_url($url)\n}) + if $CPAN::DEBUG; + + my($fh,$filename); + if ($CPAN::META->has_usable("File::Temp")) { + $fh = File::Temp->new( + dir => File::Spec->tmpdir, + template => "cpan_getsave_url_XXXX", + suffix => ".html", + unlink => 0, + ); + $filename = $fh->filename; + } else { + $fh = FileHandle->new; + $filename = "cpan_getsave_url_$$.html"; + } + my $tmpin = $filename; + if ($CPAN::META->has_usable('LWP')) { + $CPAN::Frontend->myprint("Fetching with LWP: + $url +"); + my $Ua; + CPAN::LWP::UserAgent->config; + eval { $Ua = CPAN::LWP::UserAgent->new; }; + if ($@) { + $CPAN::Frontend->mywarn("ERROR: CPAN::LWP::UserAgent->new dies with $@\n"); + return; + } else { + my($var); + $Ua->proxy('http', $var) + if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy}; + $Ua->no_proxy($var) + if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy}; + } + + my $req = HTTP::Request->new(GET => $url); + $req->header('Accept' => 'text/html'); + my $res = $Ua->request($req); + if ($res->is_success) { + $CPAN::Frontend->myprint(" + request successful.\n") + if $CPAN::DEBUG; + print $fh $res->content; + close $fh; + $CPAN::Frontend->myprint(qq{ + saved content to $tmpin \n}) + if $CPAN::DEBUG; + return $tmpin; + } else { + $CPAN::Frontend->myprint(sprintf( + "LWP failed with code[%s], message[%s]\n", + $res->code, + $res->message, + )); + return; + } + } else { + $CPAN::Frontend->mywarn(" LWP not available\n"); + return; + } +} + +#-> sub CPAN::Distribution::_build_command +sub _build_command { + my($self) = @_; + if ($^O eq "MSWin32") { # special code needed at least up to + # Module::Build 0.2611 and 0.2706; a fix + # in M:B has been promised 2006-01-30 + my($perl) = $self->perl or $CPAN::Frontend->mydie("Couldn't find executable perl\n"); + return "$perl ./Build"; + } + elsif ($^O eq 'VMS') { + return "$^X Build.com"; + } + return "./Build"; +} + +#-> sub CPAN::Distribution::_should_report +sub _should_report { + my($self, $phase) = @_; + die "_should_report() requires a 'phase' argument" + if ! defined $phase; + + return unless $CPAN::META->has_usable("CPAN::Reporter"); + + # configured + my $test_report = CPAN::HandleConfig->prefs_lookup($self, + q{test_report}); + return unless $test_report; + + # don't repeat if we cached a result + return $self->{should_report} + if exists $self->{should_report}; + + # don't report if we generated a Makefile.PL + if ( $self->{had_no_makefile_pl} ) { + $CPAN::Frontend->mywarn( + "Will not send CPAN Testers report with generated Makefile.PL.\n" + ); + return $self->{should_report} = 0; + } + + # available + if ( ! $CPAN::META->has_inst("CPAN::Reporter")) { + $CPAN::Frontend->mywarnonce( + "CPAN::Reporter not installed. No reports will be sent.\n" + ); + return $self->{should_report} = 0; + } + + # capable + my $crv = CPAN::Reporter->VERSION; + if ( CPAN::Version->vlt( $crv, 0.99 ) ) { + # don't cache $self->{should_report} -- need to check each phase + if ( $phase eq 'test' ) { + return 1; + } + else { + $CPAN::Frontend->mywarn( + "Reporting on the '$phase' phase requires CPAN::Reporter 0.99, but \n" . + "you only have version $crv\. Only 'test' phase reports will be sent.\n" + ); + return; + } + } + + # appropriate + if ($self->is_dot_dist) { + $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is disabled ". + "for local directories\n"); + return $self->{should_report} = 0; + } + if ($self->prefs->{patches} + && + @{$self->prefs->{patches}} + && + $self->{patched} + ) { + $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is disabled ". + "when the source has been patched\n"); + return $self->{should_report} = 0; + } + + # proceed and cache success + return $self->{should_report} = 1; +} + +#-> sub CPAN::Distribution::reports +sub reports { + my($self) = @_; + my $pathname = $self->id; + $CPAN::Frontend->myprint("Distribution: $pathname\n"); + + unless ($CPAN::META->has_inst("CPAN::DistnameInfo")) { + $CPAN::Frontend->mydie("CPAN::DistnameInfo not installed; cannot continue"); + } + unless ($CPAN::META->has_usable("LWP")) { + $CPAN::Frontend->mydie("LWP not installed; cannot continue"); + } + unless ($CPAN::META->has_usable("File::Temp")) { + $CPAN::Frontend->mydie("File::Temp not installed; cannot continue"); + } + + my $format; + if ($CPAN::META->has_inst("YAML::XS") || $CPAN::META->has_inst("YAML::Syck")){ + $format = 'yaml'; + } + elsif (!$format && $CPAN::META->has_inst("JSON::PP") ) { + $format = 'json'; + } + else { + $CPAN::Frontend->mydie("JSON::PP not installed, cannot continue"); + } + + my $d = CPAN::DistnameInfo->new($pathname); + + my $dist = $d->dist; # "CPAN-DistnameInfo" + my $version = $d->version; # "0.02" + my $maturity = $d->maturity; # "released" + my $filename = $d->filename; # "CPAN-DistnameInfo-0.02.tar.gz" + my $cpanid = $d->cpanid; # "GBARR" + my $distvname = $d->distvname; # "CPAN-DistnameInfo-0.02" + + my $url = sprintf "http://www.cpantesters.org/show/%s.%s", $dist, $format; + + CPAN::LWP::UserAgent->config; + my $Ua; + eval { $Ua = CPAN::LWP::UserAgent->new; }; + if ($@) { + $CPAN::Frontend->mydie("CPAN::LWP::UserAgent->new dies with $@\n"); + } + $CPAN::Frontend->myprint("Fetching '$url'..."); + my $resp = $Ua->get($url); + unless ($resp->is_success) { + $CPAN::Frontend->mydie(sprintf "Could not download '%s': %s\n", $url, $resp->code); + } + $CPAN::Frontend->myprint("DONE\n\n"); + my $unserialized; + if ( $format eq 'yaml' ) { + my $yaml = $resp->content; + # what a long way round! + my $fh = File::Temp->new( + dir => File::Spec->tmpdir, + template => 'cpan_reports_XXXX', + suffix => '.yaml', + unlink => 0, + ); + my $tfilename = $fh->filename; + print $fh $yaml; + close $fh or $CPAN::Frontend->mydie("Could not close '$tfilename': $!"); + $unserialized = CPAN->_yaml_loadfile($tfilename)->[0]; + unlink $tfilename or $CPAN::Frontend->mydie("Could not unlink '$tfilename': $!"); + } else { + require JSON::PP; + $unserialized = JSON::PP->new->utf8->decode($resp->content); + } + my %other_versions; + my $this_version_seen; + for my $rep (@$unserialized) { + my $rversion = $rep->{version}; + if ($rversion eq $version) { + unless ($this_version_seen++) { + $CPAN::Frontend->myprint ("$rep->{version}:\n"); + } + my $arch = $rep->{archname} || $rep->{platform} || '????'; + my $grade = $rep->{action} || $rep->{status} || '????'; + my $ostext = $rep->{ostext} || ucfirst($rep->{osname}) || '????'; + $CPAN::Frontend->myprint + (sprintf("%1s%1s%-4s %s on %s %s (%s)\n", + $arch eq $Config::Config{archname}?"*":"", + $grade eq "PASS"?"+":$grade eq"FAIL"?"-":"", + $grade, + $rep->{perl}, + $ostext, + $rep->{osvers}, + $arch, + )); + } else { + $other_versions{$rep->{version}}++; + } + } + unless ($this_version_seen) { + $CPAN::Frontend->myprint("No reports found for version '$version' +Reports for other versions:\n"); + for my $v (sort keys %other_versions) { + $CPAN::Frontend->myprint(" $v\: $other_versions{$v}\n"); + } + } + $url = substr($url,0,-4) . 'html'; + $CPAN::Frontend->myprint("See $url for details\n"); +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Distroprefs.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Distroprefs.pm new file mode 100644 index 0000000000..05b19faa47 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Distroprefs.pm @@ -0,0 +1,481 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: + +use 5.006; +use strict; +package CPAN::Distroprefs; + +use vars qw($VERSION); +$VERSION = '6.0001'; + +package CPAN::Distroprefs::Result; + +use File::Spec; + +sub new { bless $_[1] || {} => $_[0] } + +sub abs { File::Spec->catfile($_[0]->dir, $_[0]->file) } + +sub __cloner { + my ($class, $name, $newclass) = @_; + $newclass = 'CPAN::Distroprefs::Result::' . $newclass; + no strict 'refs'; + *{$class . '::' . $name} = sub { + $newclass->new({ + %{ $_[0] }, + %{ $_[1] }, + }); + }; +} +BEGIN { __PACKAGE__->__cloner(as_warning => 'Warning') } +BEGIN { __PACKAGE__->__cloner(as_fatal => 'Fatal') } +BEGIN { __PACKAGE__->__cloner(as_success => 'Success') } + +sub __accessor { + my ($class, $key) = @_; + no strict 'refs'; + *{$class . '::' . $key} = sub { $_[0]->{$key} }; +} +BEGIN { __PACKAGE__->__accessor($_) for qw(type file ext dir) } + +sub is_warning { 0 } +sub is_fatal { 0 } +sub is_success { 0 } + +package CPAN::Distroprefs::Result::Error; +use vars qw(@ISA); +BEGIN { @ISA = 'CPAN::Distroprefs::Result' } ## no critic +BEGIN { __PACKAGE__->__accessor($_) for qw(msg) } + +sub as_string { + my ($self) = @_; + if ($self->msg) { + return sprintf $self->fmt_reason, $self->file, $self->msg; + } else { + return sprintf $self->fmt_unknown, $self->file; + } +} + +package CPAN::Distroprefs::Result::Warning; +use vars qw(@ISA); +BEGIN { @ISA = 'CPAN::Distroprefs::Result::Error' } ## no critic +sub is_warning { 1 } +sub fmt_reason { "Error reading distroprefs file %s, skipping: %s" } +sub fmt_unknown { "Unknown error reading distroprefs file %s, skipping." } + +package CPAN::Distroprefs::Result::Fatal; +use vars qw(@ISA); +BEGIN { @ISA = 'CPAN::Distroprefs::Result::Error' } ## no critic +sub is_fatal { 1 } +sub fmt_reason { "Error reading distroprefs file %s: %s" } +sub fmt_unknown { "Unknown error reading distroprefs file %s." } + +package CPAN::Distroprefs::Result::Success; +use vars qw(@ISA); +BEGIN { @ISA = 'CPAN::Distroprefs::Result' } ## no critic +BEGIN { __PACKAGE__->__accessor($_) for qw(prefs extension) } +sub is_success { 1 } + +package CPAN::Distroprefs::Iterator; + +sub new { bless $_[1] => $_[0] } + +sub next { $_[0]->() } + +package CPAN::Distroprefs; + +use Carp (); +use DirHandle; + +sub _load_method { + my ($self, $loader, $result) = @_; + return '_load_yaml' if $loader eq 'CPAN' or $loader =~ /^YAML(::|$)/; + return '_load_' . $result->ext; +} + +sub _load_yaml { + my ($self, $loader, $result) = @_; + my $data = eval { + $loader eq 'CPAN' + ? $loader->_yaml_loadfile($result->abs) + : [ $loader->can('LoadFile')->($result->abs) ] + }; + if (my $err = $@) { + die $result->as_warning({ + msg => $err, + }); + } elsif (!$data) { + die $result->as_warning; + } else { + return @$data; + } +} + +sub _load_dd { + my ($self, $loader, $result) = @_; + my @data; + { + package CPAN::Eval; + # this caused a die in CPAN.pm, and I am leaving it 'fatal', though I'm + # not sure why we wouldn't just skip the file as we do for all other + # errors. -- hdp + my $abs = $result->abs; + open FH, "<$abs" or die $result->as_fatal(msg => "$!"); + local $/; + my $eval = <FH>; + close FH; + no strict; + eval $eval; + if (my $err = $@) { + die $result->as_warning({ msg => $err }); + } + my $i = 1; + while (${"VAR$i"}) { + push @data, ${"VAR$i"}; + $i++; + } + } + return @data; +} + +sub _load_st { + my ($self, $loader, $result) = @_; + # eval because Storable is never forward compatible + my @data = eval { @{scalar $loader->can('retrieve')->($result->abs) } }; + if (my $err = $@) { + die $result->as_warning({ msg => $err }); + } + return @data; +} + +sub _build_file_list { + if (@_ > 3) { + die "_build_file_list should be called with 3 arguments, was called with more. First argument is '$_[0]'."; + } + my ($dir, $dir1, $ext_re) = @_; + my @list; + my $dh; + unless (opendir($dh, $dir)) { + $CPAN::Frontend->mywarn("ignoring prefs directory '$dir': $!"); + return @list; + } + while (my $fn = readdir $dh) { + next if $fn eq '.' || $fn eq '..'; + if (-d "$dir/$fn") { + next if $fn =~ /^[._]/; # prune .svn, .git, .hg, _darcs and what the user wants to hide + push @list, _build_file_list("$dir/$fn", "$dir1$fn/", $ext_re); + } else { + if ($fn =~ $ext_re) { + push @list, "$dir1$fn"; + } + } + } + return @list; +} + +sub find { + my ($self, $dir, $ext_map) = @_; + + return CPAN::Distroprefs::Iterator->new(sub { return }) unless %$ext_map; + + my $possible_ext = join "|", map { quotemeta } keys %$ext_map; + my $ext_re = qr/\.($possible_ext)$/; + + my @files = _build_file_list($dir, '', $ext_re); + @files = sort @files if @files; + + # label the block so that we can use redo in the middle + return CPAN::Distroprefs::Iterator->new(sub { LOOP: { + + my $fn = shift @files; + return unless defined $fn; + my ($ext) = $fn =~ $ext_re; + + my $loader = $ext_map->{$ext}; + + my $result = CPAN::Distroprefs::Result->new({ + file => $fn, ext => $ext, dir => $dir + }); + # copied from CPAN.pm; is this ever actually possible? + redo unless -f $result->abs; + + my $load_method = $self->_load_method($loader, $result); + my @prefs = eval { $self->$load_method($loader, $result) }; + if (my $err = $@) { + if (ref($err) && eval { $err->isa('CPAN::Distroprefs::Result') }) { + return $err; + } + # rethrow any exceptions that we did not generate + die $err; + } elsif (!@prefs) { + # the loader should have handled this, but just in case: + return $result->as_warning; + } + return $result->as_success({ + prefs => [ + map { CPAN::Distroprefs::Pref->new({ data => $_ }) } @prefs + ], + }); + } }); +} + +package CPAN::Distroprefs::Pref; + +use Carp (); + +sub new { bless $_[1] => $_[0] } + +sub data { shift->{data} } + +sub has_any_match { $_[0]->data->{match} ? 1 : 0 } + +sub has_match { + my $match = $_[0]->data->{match} || return 0; + exists $match->{$_[1]} || exists $match->{"not_$_[1]"} +} + +sub has_valid_subkeys { + grep { exists $_[0]->data->{match}{$_} } + map { $_, "not_$_" } + $_[0]->match_attributes +} + +sub _pattern { + my $re = shift; + my $p = eval sprintf 'qr{%s}', $re; + if ($@) { + $@ =~ s/\n$//; + die "Error in Distroprefs pattern qr{$re}\n$@"; + } + return $p; +} + +sub _match_scalar { + my ($match, $data) = @_; + my $qr = _pattern($match); + return $data =~ /$qr/; +} + +sub _match_hash { + my ($match, $data) = @_; + for my $mkey (keys %$match) { + (my $dkey = $mkey) =~ s/^not_//; + my $val = defined $data->{$dkey} ? $data->{$dkey} : ''; + if (_match_scalar($match->{$mkey}, $val)) { + return 0 if $mkey =~ /^not_/; + } + else { + return 0 if $mkey !~ /^not_/; + } + } + return 1; +} + +sub _match { + my ($self, $key, $data, $matcher) = @_; + my $m = $self->data->{match}; + if (exists $m->{$key}) { + return 0 unless $matcher->($m->{$key}, $data); + } + if (exists $m->{"not_$key"}) { + return 0 if $matcher->($m->{"not_$key"}, $data); + } + return 1; +} + +sub _scalar_match { + my ($self, $key, $data) = @_; + return $self->_match($key, $data, \&_match_scalar); +} + +sub _hash_match { + my ($self, $key, $data) = @_; + return $self->_match($key, $data, \&_match_hash); +} + +# do not take the order of C<keys %$match> because "module" is by far the +# slowest +sub match_attributes { qw(env distribution perl perlconfig module) } + +sub match_module { + my ($self, $modules) = @_; + return $self->_match("module", $modules, sub { + my($match, $data) = @_; + my $qr = _pattern($match); + for my $module (@$data) { + return 1 if $module =~ /$qr/; + } + return 0; + }); +} + +sub match_distribution { shift->_scalar_match(distribution => @_) } +sub match_perl { shift->_scalar_match(perl => @_) } + +sub match_perlconfig { shift->_hash_match(perlconfig => @_) } +sub match_env { shift->_hash_match(env => @_) } + +sub matches { + my ($self, $arg) = @_; + + my $default_match = 0; + for my $key (grep { $self->has_match($_) } $self->match_attributes) { + unless (exists $arg->{$key}) { + Carp::croak "Can't match pref: missing argument key $key"; + } + $default_match = 1; + my $val = $arg->{$key}; + # make it possible to avoid computing things until we have to + if (ref($val) eq 'CODE') { $val = $val->() } + my $meth = "match_$key"; + return 0 unless $self->$meth($val); + } + + return $default_match; +} + +1; + +__END__ + +=head1 NAME + +CPAN::Distroprefs -- read and match distroprefs + +=head1 SYNOPSIS + + use CPAN::Distroprefs; + + my %info = (... distribution/environment info ...); + + my $finder = CPAN::Distroprefs->find($prefs_dir, \%ext_map); + + while (my $result = $finder->next) { + + die $result->as_string if $result->is_fatal; + + warn($result->as_string), next if $result->is_warning; + + for my $pref (@{ $result->prefs }) { + if ($pref->matches(\%info)) { + return $pref; + } + } + } + + +=head1 DESCRIPTION + +This module encapsulates reading L<Distroprefs|CPAN> and matching them against CPAN distributions. + +=head1 INTERFACE + + my $finder = CPAN::Distroprefs->find($dir, \%ext_map); + + while (my $result = $finder->next) { ... } + +Build an iterator which finds distroprefs files in the tree below the +given directory. Within the tree directories matching C<m/^[._]/> are +pruned. + +C<%ext_map> is a hashref whose keys are file extensions and whose values are +modules used to load matching files: + + { + 'yml' => 'YAML::Syck', + 'dd' => 'Data::Dumper', + ... + } + +Each time C<< $finder->next >> is called, the iterator returns one of two +possible values: + +=over + +=item * a CPAN::Distroprefs::Result object + +=item * C<undef>, indicating that no prefs files remain to be found + +=back + +=head1 RESULTS + +L<C<find()>|/INTERFACE> returns CPAN::Distroprefs::Result objects to +indicate success or failure when reading a prefs file. + +=head2 Common + +All results share some common attributes: + +=head3 type + +C<success>, C<warning>, or C<fatal> + +=head3 file + +the file from which these prefs were read, or to which this error refers (relative filename) + +=head3 ext + +the file's extension, which determines how to load it + +=head3 dir + +the directory the file was read from + +=head3 abs + +the absolute path to the file + +=head2 Errors + +Error results (warning and fatal) contain: + +=head3 msg + +the error message (usually either C<$!> or a YAML error) + +=head2 Successes + +Success results contain: + +=head3 prefs + +an arrayref of CPAN::Distroprefs::Pref objects + +=head1 PREFS + +CPAN::Distroprefs::Pref objects represent individual distroprefs documents. +They are constructed automatically as part of C<success> results from C<find()>. + +=head3 data + +the pref information as a hashref, suitable for e.g. passing to Kwalify + +=head3 match_attributes + +returns a list of the valid match attributes (see the Distroprefs section in L<CPAN>) + +currently: C<env perl perlconfig distribution module> + +=head3 has_any_match + +true if this pref has a 'match' attribute at all + +=head3 has_valid_subkeys + +true if this pref has a 'match' attribute and at least one valid match attribute + +=head3 matches + + if ($pref->matches(\%arg)) { ... } + +true if this pref matches the passed-in hashref, which must have a value for +each of the C<match_attributes> (above) + +=head1 LICENSE + +This program is free software; you can redistribute it and/or modify it under +the same terms as Perl itself. + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Distrostatus.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Distrostatus.pm new file mode 100644 index 0000000000..0cc6cc9a79 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Distrostatus.pm @@ -0,0 +1,45 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Distrostatus; +use overload '""' => "as_string", + fallback => 1; +use vars qw($something_has_failed_at); +use vars qw( + $VERSION +); +$VERSION = "5.5"; + + +sub new { + my($class,$arg) = @_; + my $failed = substr($arg,0,2) eq "NO"; + if ($failed) { + $something_has_failed_at = $CPAN::CurrentCommandId; + } + bless { + TEXT => $arg, + FAILED => $failed, + COMMANDID => $CPAN::CurrentCommandId, + TIME => time, + }, $class; +} +sub something_has_just_failed () { + defined $something_has_failed_at && + $something_has_failed_at == $CPAN::CurrentCommandId; +} +sub commandid { shift->{COMMANDID} } +sub failed { shift->{FAILED} } +sub text { + my($self,$set) = @_; + if (defined $set) { + $self->{TEXT} = $set; + } + $self->{TEXT}; +} +sub as_string { + my($self) = @_; + $self->text; +} + + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/RecursiveDependency.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/RecursiveDependency.pm new file mode 100644 index 0000000000..82e82346ef --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/RecursiveDependency.pm @@ -0,0 +1,113 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Exception::RecursiveDependency; +use strict; +use overload '""' => "as_string"; + +use vars qw( + $VERSION +); +$VERSION = "5.5001"; + +{ + package CPAN::Exception::RecursiveDependency::na; + use overload '""' => "as_string"; + sub new { bless {}, shift }; + sub as_string { "N/A" }; +} + +my $NA = CPAN::Exception::RecursiveDependency::na->new; + +# a module sees its distribution (no version) +# a distribution sees its prereqs (which are module names) (usually with versions) +# a bundle sees its module names and/or its distributions (no version) + +sub new { + my($class) = shift; + my($deps_arg) = shift; + my (@deps,%seen,$loop_starts_with); + DCHAIN: for my $dep (@$deps_arg) { + push @deps, {name => $dep, display_as => $dep}; + if ($seen{$dep}++) { + $loop_starts_with = $dep; + last DCHAIN; + } + } + my $in_loop = 0; + my %mark; + DWALK: for my $i (0..$#deps) { + my $x = $deps[$i]{name}; + $in_loop ||= $loop_starts_with && $x eq $loop_starts_with; + my $xo = CPAN::Shell->expandany($x) or next; + if ($xo->isa("CPAN::Module")) { + my $have = $xo->inst_version || $NA; + my($want,$d,$want_type); + if ($i>0 and $d = $deps[$i-1]{name}) { + my $do = CPAN::Shell->expandany($d); + $want = $do->{prereq_pm}{requires}{$x}; + if (defined $want) { + $want_type = "requires: "; + } else { + $want = $do->{prereq_pm}{build_requires}{$x}; + if (defined $want) { + $want_type = "build_requires: "; + } else { + $want_type = "unknown status"; + $want = "???"; + } + } + } else { + $want = $xo->cpan_version; + $want_type = "want: "; + } + $deps[$i]{have} = $have; + $deps[$i]{want_type} = $want_type; + $deps[$i]{want} = $want; + $deps[$i]{display_as} = "$x (have: $have; $want_type$want)"; + if ((! ref $have || !$have->isa('CPAN::Exception::RecursiveDependency::na')) + && CPAN::Version->vge($have, $want)) { + # https://rt.cpan.org/Ticket/Display.html?id=115340 + undef $loop_starts_with; + last DWALK; + } + } elsif ($xo->isa("CPAN::Distribution")) { + my $pretty = $deps[$i]{display_as} = $xo->pretty_id; + my $mark_as; + if ($in_loop) { + $mark_as = CPAN::Distrostatus->new("NO cannot resolve circular dependency"); + } else { + $mark_as = CPAN::Distrostatus->new("NO one dependency ($loop_starts_with) is a circular dependency"); + } + $mark{$pretty} = { xo => $xo, mark_as => $mark_as }; + } + } + if ($loop_starts_with) { + while (my($k,$v) = each %mark) { + my $xo = $v->{xo}; + $xo->{make} = $v->{mark_as}; + $xo->store_persistent_state; # otherwise I will not reach + # all involved parties for + # the next session + } + } + bless { deps => \@deps, loop_starts_with => $loop_starts_with }, $class; +} + +sub is_resolvable { + ! defined shift->{loop_starts_with}; +} + +sub as_string { + my($self) = shift; + my $deps = $self->{deps}; + my $loop_starts_with = $self->{loop_starts_with}; + unless ($loop_starts_with) { + return "--not a recursive/circular dependency--"; + } + my $ret = "\nRecursive dependency detected:\n "; + $ret .= join("\n => ", map {$_->{display_as}} @$deps); + $ret .= ".\nCannot resolve.\n"; + $ret; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/blocked_urllist.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/blocked_urllist.pm new file mode 100644 index 0000000000..87d07d13f1 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/blocked_urllist.pm @@ -0,0 +1,46 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Exception::blocked_urllist; +use strict; +use overload '""' => "as_string"; + +use vars qw( + $VERSION +); +$VERSION = "1.001"; + + +sub new { + my($class) = @_; + bless {}, $class; +} + +sub as_string { + my($self) = shift; + if ($CPAN::Config->{connect_to_internet_ok}) { + return qq{ + +You have not configured a urllist for CPAN mirrors. Configure it with + + o conf init urllist + +}; + } else { + return qq{ + +You have not configured a urllist and do not allow connections to the +internet to get a list of mirrors. If you wish to get a list of CPAN +mirrors to pick from, use this command + + o conf init connect_to_internet_ok urllist + +If you do not wish to get a list of mirrors and would prefer to set +your urllist manually, use just this command instead + + o conf init urllist + +}; + } +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/yaml_not_installed.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/yaml_not_installed.pm new file mode 100644 index 0000000000..1e7fa83a53 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/yaml_not_installed.pm @@ -0,0 +1,23 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Exception::yaml_not_installed; +use strict; +use overload '""' => "as_string"; + +use vars qw( + $VERSION +); +$VERSION = "5.5"; + + +sub new { + my($class,$module,$file,$during) = @_; + bless { module => $module, file => $file, during => $during }, $class; +} + +sub as_string { + my($self) = shift; + "'$self->{module}' not installed, cannot $self->{during} '$self->{file}'\n"; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/yaml_process_error.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/yaml_process_error.pm new file mode 100644 index 0000000000..ae8c14ebeb --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Exception/yaml_process_error.pm @@ -0,0 +1,53 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Exception::yaml_process_error; +use strict; +use overload '""' => "as_string"; + +use vars qw( + $VERSION +); +$VERSION = "5.5"; + + +sub new { + my($class,$module,$file,$during,$error) = @_; + # my $at = Carp::longmess(""); # XXX find something more beautiful + bless { module => $module, + file => $file, + during => $during, + error => $error, + # at => $at, + }, $class; +} + +sub as_string { + my($self) = shift; + if ($self->{during}) { + if ($self->{file}) { + if ($self->{module}) { + if ($self->{error}) { + return "Alert: While trying to '$self->{during}' YAML file\n". + " '$self->{file}'\n". + "with '$self->{module}' the following error was encountered:\n". + " $self->{error}\n"; + } else { + return "Alert: While trying to '$self->{during}' YAML file\n". + " '$self->{file}'\n". + "with '$self->{module}' some unknown error was encountered\n"; + } + } else { + return "Alert: While trying to '$self->{during}' YAML file\n". + " '$self->{file}'\n". + "some unknown error was encountered\n"; + } + } else { + return "Alert: While trying to '$self->{during}' some YAML file\n". + "some unknown error was encountered\n"; + } + } else { + return "Alert: unknown error encountered\n"; + } +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/FTP.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/FTP.pm new file mode 100644 index 0000000000..268522f78f --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/FTP.pm @@ -0,0 +1,1153 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::FTP; +use strict; + +use Errno (); +use Fcntl qw(:flock); +use File::Basename qw(dirname); +use File::Path qw(mkpath); +use CPAN::FTP::netrc; +use vars qw($connect_to_internet_ok $Ua $Thesite $ThesiteURL $Themethod); + +@CPAN::FTP::ISA = qw(CPAN::Debug); + +use vars qw( + $VERSION +); +$VERSION = "5.5013"; + +sub _plus_append_open { + my($fh, $file) = @_; + my $parent_dir = dirname $file; + mkpath $parent_dir; + my($cnt); + until (open $fh, "+>>$file") { + next if exists &Errno::EAGAIN && $! == &Errno::EAGAIN; # don't increment on EAGAIN + $CPAN::Frontend->mydie("Could not open '$file' after 10000 tries: $!") if ++$cnt > 100000; + sleep 0.0001; + mkpath $parent_dir; + } +} + +#-> sub CPAN::FTP::ftp_statistics +# if they want to rewrite, they need to pass in a filehandle +sub _ftp_statistics { + my($self,$fh) = @_; + my $ftpstats_size = $CPAN::Config->{ftpstats_size}; + return if defined $ftpstats_size && $ftpstats_size <= 0; + my $locktype = $fh ? LOCK_EX : LOCK_SH; + # XXX On Windows flock() implements mandatory locking, so we can + # XXX only use shared locking to still allow _yaml_loadfile() to + # XXX read from the file using a different filehandle. + $locktype = LOCK_SH if $^O eq "MSWin32"; + + $fh ||= FileHandle->new; + my $file = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); + _plus_append_open($fh,$file); + my $sleep = 1; + my $waitstart; + while (!CPAN::_flock($fh, $locktype|LOCK_NB)) { + $waitstart ||= localtime(); + if ($sleep>3) { + my $now = localtime(); + $CPAN::Frontend->mywarn("$now: waiting for read lock on '$file' (since $waitstart)\n"); + } + sleep($sleep); # this sleep must not be overridden; + # Frontend->mysleep with AUTOMATED_TESTING has + # provoked complete lock contention on my NFS + if ($sleep <= 6) { + $sleep+=0.5; + } else { + # retry to get a fresh handle. If it is NFS and the handle is stale, we will never get an flock + _plus_append_open($fh, $file); + } + } + my $stats = eval { CPAN->_yaml_loadfile($file); }; + if ($@) { + if (ref $@) { + if (ref $@ eq "CPAN::Exception::yaml_not_installed") { + chomp $@; + $CPAN::Frontend->myprintonce("Warning (usually harmless): $@\n"); + return; + } elsif (ref $@ eq "CPAN::Exception::yaml_process_error") { + my $time = time; + my $to = "$file.$time"; + $CPAN::Frontend->mywarn("Error reading '$file': $@ + Trying to stash it away as '$to' to prevent further interruptions. + You may want to remove that file later.\n"); + # may fail because somebody else has moved it away in the meantime: + rename $file, $to or $CPAN::Frontend->mywarn("Could not rename '$file' to '$to': $!\n"); + return; + } + } else { + $CPAN::Frontend->mydie($@); + } + } + CPAN::_flock($fh, LOCK_UN); + return $stats->[0]; +} + +#-> sub CPAN::FTP::_mytime +sub _mytime () { + if (CPAN->has_inst("Time::HiRes")) { + return Time::HiRes::time(); + } else { + return time; + } +} + +#-> sub CPAN::FTP::_new_stats +sub _new_stats { + my($self,$file) = @_; + my $ret = { + file => $file, + attempts => [], + start => _mytime, + }; + $ret; +} + +#-> sub CPAN::FTP::_add_to_statistics +sub _add_to_statistics { + my($self,$stats) = @_; + my $yaml_module = CPAN::_yaml_module(); + $self->debug("yaml_module[$yaml_module]") if $CPAN::DEBUG; + if ($CPAN::META->has_inst($yaml_module)) { + $stats->{thesiteurl} = $ThesiteURL; + $stats->{end} = CPAN::FTP::_mytime(); + my $fh = FileHandle->new; + my $time = time; + my $sdebug = 0; + my @debug; + @debug = $time if $sdebug; + my $fullstats = $self->_ftp_statistics($fh); + close $fh if $fh && defined(fileno($fh)); + $fullstats->{history} ||= []; + push @debug, scalar @{$fullstats->{history}} if $sdebug; + push @debug, time if $sdebug; + push @{$fullstats->{history}}, $stats; + # YAML.pm 0.62 is unacceptably slow with 999; + # YAML::Syck 0.82 has no noticable performance problem with 999; + my $ftpstats_size = $CPAN::Config->{ftpstats_size}; + $ftpstats_size = 99 unless defined $ftpstats_size; + my $ftpstats_period = $CPAN::Config->{ftpstats_period} || 14; + while ( + @{$fullstats->{history} || []} + && + ( + @{$fullstats->{history}} > $ftpstats_size + || $time - $fullstats->{history}[0]{start} > 86400*$ftpstats_period + ) + ) { + shift @{$fullstats->{history}} + } + push @debug, scalar @{$fullstats->{history}} if $sdebug; + push @debug, time if $sdebug; + push @debug, scalar localtime($fullstats->{history}[0]{start}) if $sdebug; + # need no eval because if this fails, it is serious + my $sfile = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); + CPAN->_yaml_dumpfile("$sfile.$$",$fullstats); + if ( $sdebug ) { + local $CPAN::DEBUG = 512; # FTP + push @debug, time; + CPAN->debug(sprintf("DEBUG history: before_read[%d]before[%d]at[%d]". + "after[%d]at[%d]oldest[%s]dumped backat[%d]", + @debug, + )); + } + # Win32 cannot rename a file to an existing filename + unlink($sfile) if ($^O eq 'MSWin32' or $^O eq 'os2'); + _copy_stat($sfile, "$sfile.$$") if -e $sfile; + rename "$sfile.$$", $sfile + or $CPAN::Frontend->mywarn("Could not rename '$sfile.$$' to '$sfile': $!\nGiving up\n"); + } +} + +# Copy some stat information (owner, group, mode and) from one file to +# another. +# This is a utility function which might be moved to a utility repository. +#-> sub CPAN::FTP::_copy_stat +sub _copy_stat { + my($src, $dest) = @_; + my @stat = stat($src); + if (!@stat) { + $CPAN::Frontend->mywarn("Can't stat '$src': $!\n"); + return; + } + + eval { + chmod $stat[2], $dest + or $CPAN::Frontend->mywarn("Can't chmod '$dest' to " . sprintf("0%o", $stat[2]) . ": $!\n"); + }; + warn $@ if $@; + eval { + chown $stat[4], $stat[5], $dest + or do { + my $save_err = $!; # otherwise it's lost in the get... calls + $CPAN::Frontend->mywarn("Can't chown '$dest' to " . + (getpwuid($stat[4]))[0] . "/" . + (getgrgid($stat[5]))[0] . ": $save_err\n" + ); + }; + }; + warn $@ if $@; +} + +# if file is CHECKSUMS, suggest the place where we got the file to be +# checked from, maybe only for young files? +#-> sub CPAN::FTP::_recommend_url_for +sub _recommend_url_for { + my($self, $file, $urllist) = @_; + if ($file =~ s|/CHECKSUMS(.gz)?$||) { + my $fullstats = $self->_ftp_statistics(); + my $history = $fullstats->{history} || []; + while (my $last = pop @$history) { + last if $last->{end} - time > 3600; # only young results are interesting + next unless $last->{file}; # dirname of nothing dies! + next unless $file eq dirname($last->{file}); + return $last->{thesiteurl}; + } + } + if ($CPAN::Config->{randomize_urllist} + && + rand(1) < $CPAN::Config->{randomize_urllist} + ) { + $urllist->[int rand scalar @$urllist]; + } else { + return (); + } +} + +#-> sub CPAN::FTP::_get_urllist +sub _get_urllist { + my($self, $with_defaults) = @_; + $with_defaults ||= 0; + CPAN->debug("with_defaults[$with_defaults]") if $CPAN::DEBUG; + + $CPAN::Config->{urllist} ||= []; + unless (ref $CPAN::Config->{urllist} eq 'ARRAY') { + $CPAN::Frontend->mywarn("Malformed urllist; ignoring. Configuration file corrupt?\n"); + $CPAN::Config->{urllist} = []; + } + my @urllist = grep { defined $_ and length $_ } @{$CPAN::Config->{urllist}}; + push @urllist, @CPAN::Defaultsites if $with_defaults; + for my $u (@urllist) { + CPAN->debug("u[$u]") if $CPAN::DEBUG; + if (UNIVERSAL::can($u,"text")) { + $u->{TEXT} .= "/" unless substr($u->{TEXT},-1) eq "/"; + } else { + $u .= "/" unless substr($u,-1) eq "/"; + $u = CPAN::URL->new(TEXT => $u, FROM => "USER"); + } + } + \@urllist; +} + +#-> sub CPAN::FTP::ftp_get ; +sub ftp_get { + my($class,$host,$dir,$file,$target) = @_; + $class->debug( + qq[Going to fetch file [$file] from dir [$dir] + on host [$host] as local [$target]\n] + ) if $CPAN::DEBUG; + my $ftp = Net::FTP->new($host); + unless ($ftp) { + $CPAN::Frontend->mywarn(" Could not connect to host '$host' with Net::FTP\n"); + return; + } + return 0 unless defined $ftp; + $ftp->debug(1) if $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG; + $class->debug(qq[Going to login("anonymous","$Config::Config{cf_email}")]); + unless ( $ftp->login("anonymous",$Config::Config{'cf_email'}) ) { + my $msg = $ftp->message; + $CPAN::Frontend->mywarn(" Couldn't login on $host: $msg\n"); + return; + } + unless ( $ftp->cwd($dir) ) { + my $msg = $ftp->message; + $CPAN::Frontend->mywarn(" Couldn't cwd $dir: $msg\n"); + return; + } + $ftp->binary; + $class->debug(qq[Going to ->get("$file","$target")\n]) if $CPAN::DEBUG; + unless ( $ftp->get($file,$target) ) { + my $msg = $ftp->message; + $CPAN::Frontend->mywarn(" Couldn't fetch $file from $host: $msg\n"); + return; + } + $ftp->quit; # it's ok if this fails + return 1; +} + +# If more accuracy is wanted/needed, Chris Leach sent me this patch... + + # > *** /install/perl/live/lib/CPAN.pm- Wed Sep 24 13:08:48 1997 + # > --- /tmp/cp Wed Sep 24 13:26:40 1997 + # > *************** + # > *** 1562,1567 **** + # > --- 1562,1580 ---- + # > return 1 if substr($url,0,4) eq "file"; + # > return 1 unless $url =~ m|://([^/]+)|; + # > my $host = $1; + # > + my $proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'}; + # > + if ($proxy) { + # > + $proxy =~ m|://([^/:]+)|; + # > + $proxy = $1; + # > + my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'}; + # > + if ($noproxy) { + # > + if ($host !~ /$noproxy$/) { + # > + $host = $proxy; + # > + } + # > + } else { + # > + $host = $proxy; + # > + } + # > + } + # > require Net::Ping; + # > return 1 unless $Net::Ping::VERSION >= 2; + # > my $p; + + +#-> sub CPAN::FTP::localize ; +sub localize { + my($self,$file,$aslocal,$force,$with_defaults) = @_; + $force ||= 0; + Carp::croak( "Usage: ->localize(cpan_file,as_local_file[,\$force])" ) + unless defined $aslocal; + if ($CPAN::DEBUG){ + require Carp; + my $longmess = Carp::longmess(); + $self->debug("file[$file] aslocal[$aslocal] force[$force] carplongmess[$longmess]"); + } + if ($^O eq 'MacOS') { + # Comment by AK on 2000-09-03: Uniq short filenames would be + # available in CHECKSUMS file + my($name, $path) = File::Basename::fileparse($aslocal, ''); + if (length($name) > 31) { + $name =~ s/( + \.( + readme(\.(gz|Z))? | + (tar\.)?(gz|Z) | + tgz | + zip | + pm\.(gz|Z) + ) + )$//x; + my $suf = $1; + my $size = 31 - length($suf); + while (length($name) > $size) { + chop $name; + } + $name .= $suf; + $aslocal = File::Spec->catfile($path, $name); + } + } + + if (-f $aslocal && -r _ && !($force & 1)) { + my $size; + if ($size = -s $aslocal) { + $self->debug("aslocal[$aslocal]size[$size]") if $CPAN::DEBUG; + return $aslocal; + } else { + # empty file from a previous unsuccessful attempt to download it + unlink $aslocal or + $CPAN::Frontend->mydie("Found a zero-length '$aslocal' that I ". + "could not remove."); + } + } + my($maybe_restore) = 0; + if (-f $aslocal) { + rename $aslocal, "$aslocal.bak$$"; + $maybe_restore++; + } + + my($aslocal_dir) = dirname($aslocal); + # Inheritance is not easier to manage than a few if/else branches + if ($CPAN::META->has_usable('LWP::UserAgent')) { + unless ($Ua) { + CPAN::LWP::UserAgent->config; + eval {$Ua = CPAN::LWP::UserAgent->new;}; # Why is has_usable still not fit enough? + if ($@) { + $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n") + if $CPAN::DEBUG; + } else { + my($var); + $Ua->proxy('ftp', $var) + if $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy}; + $Ua->proxy('http', $var) + if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy}; + $Ua->no_proxy($var) + if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy}; + } + } + } + for my $prx (qw(ftp_proxy http_proxy no_proxy)) { + $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; + } + + # Try the list of urls for each single object. We keep a record + # where we did get a file from + my(@reordered,$last); + my $ccurllist = $self->_get_urllist($with_defaults); + $last = $#$ccurllist; + if ($force & 2) { # local cpans probably out of date, don't reorder + @reordered = (0..$last); + } else { + @reordered = + sort { + (substr($ccurllist->[$b],0,4) eq "file") + <=> + (substr($ccurllist->[$a],0,4) eq "file") + or + defined($ThesiteURL) + and + ($ccurllist->[$b] eq $ThesiteURL) + <=> + ($ccurllist->[$a] eq $ThesiteURL) + } 0..$last; + } + my(@levels); + $Themethod ||= ""; + $self->debug("Themethod[$Themethod]reordered[@reordered]") if $CPAN::DEBUG; + my @all_levels = ( + ["dleasy", "file"], + ["dleasy"], + ["dlhard"], + ["dlhardest"], + ["dleasy", "http","defaultsites"], + ["dlhard", "http","defaultsites"], + ["dleasy", "ftp", "defaultsites"], + ["dlhard", "ftp", "defaultsites"], + ["dlhardest","", "defaultsites"], + ); + if ($Themethod) { + @levels = grep {$_->[0] eq $Themethod} @all_levels; + push @levels, grep {$_->[0] ne $Themethod} @all_levels; + } else { + @levels = @all_levels; + } + @levels = qw/dleasy/ if $^O eq 'MacOS'; + my($levelno); + local $ENV{FTP_PASSIVE} = + exists $CPAN::Config->{ftp_passive} ? + $CPAN::Config->{ftp_passive} : 1; + my $ret; + my $stats = $self->_new_stats($file); + for ($CPAN::Config->{connect_to_internet_ok}) { + $connect_to_internet_ok = $_ if not defined $connect_to_internet_ok and defined $_; + } + LEVEL: for $levelno (0..$#levels) { + my $level_tuple = $levels[$levelno]; + my($level,$scheme,$sitetag) = @$level_tuple; + $self->mymkpath($aslocal_dir) unless $scheme && "file" eq $scheme; + my $defaultsites = $sitetag && $sitetag eq "defaultsites" && !@$ccurllist; + my @urllist; + if ($defaultsites) { + unless (defined $connect_to_internet_ok) { + $CPAN::Frontend->myprint(sprintf qq{ +I would like to connect to one of the following sites to get '%s': + +%s +}, + $file, + join("",map { " ".$_->text."\n" } @CPAN::Defaultsites), + ); + my $answer = CPAN::Shell::colorable_makemaker_prompt("Is it OK to try to connect to the Internet?", "yes"); + if ($answer =~ /^y/i) { + $connect_to_internet_ok = 1; + } else { + $connect_to_internet_ok = 0; + } + } + if ($connect_to_internet_ok) { + @urllist = @CPAN::Defaultsites; + } else { + my $sleep = 2; + # the tricky thing about dying here is that everybody + # believes that calls to exists() or all_objects() are + # safe. + require CPAN::Exception::blocked_urllist; + die CPAN::Exception::blocked_urllist->new; + } + } else { # ! $defaultsites + my @host_seq = $level =~ /dleasy/ ? + @reordered : 0..$last; # reordered has file and $Thesiteurl first + @urllist = map { $ccurllist->[$_] } @host_seq; + } + $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; + my $aslocal_tempfile = $aslocal . ".tmp" . $$; + if (my $recommend = $self->_recommend_url_for($file,\@urllist)) { + @urllist = grep { $_ ne $recommend } @urllist; + unshift @urllist, $recommend; + } + $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; + $ret = $self->hostdlxxx($level,$scheme,\@urllist,$file,$aslocal_tempfile,$stats); + if ($ret) { + CPAN->debug("ret[$ret]aslocal[$aslocal]") if $CPAN::DEBUG; + if ($ret eq $aslocal_tempfile) { + # if we got it exactly as we asked for, only then we + # want to rename + rename $aslocal_tempfile, $aslocal + or $CPAN::Frontend->mydie("Error while trying to rename ". + "'$ret' to '$aslocal': $!"); + $ret = $aslocal; + } + elsif (-f $ret && $scheme eq 'file' ) { + # it's a local file, so there's nothing left to do, we + # let them read from where it is + } + $Themethod = $level; + my $now = time; + # utime $now, $now, $aslocal; # too bad, if we do that, we + # might alter a local mirror + $self->debug("level[$level]") if $CPAN::DEBUG; + last LEVEL; + } else { + unlink $aslocal_tempfile; + last if $CPAN::Signal; # need to cleanup + } + } + if ($ret) { + $stats->{filesize} = -s $ret; + } + $self->debug("before _add_to_statistics") if $CPAN::DEBUG; + $self->_add_to_statistics($stats); + $self->debug("after _add_to_statistics") if $CPAN::DEBUG; + if ($ret) { + unlink "$aslocal.bak$$"; + return $ret; + } + unless ($CPAN::Signal) { + my(@mess); + local $" = " "; + if (@{$CPAN::Config->{urllist}}) { + push @mess, + qq{Please check, if the URLs I found in your configuration file \(}. + join(", ", @{$CPAN::Config->{urllist}}). + qq{\) are valid.}; + } else { + push @mess, qq{Your urllist is empty!}; + } + push @mess, qq{The urllist can be edited.}, + qq{E.g. with 'o conf urllist push ftp://myurl/'}; + $CPAN::Frontend->mywarn(Text::Wrap::wrap("","","@mess"). "\n\n"); + $CPAN::Frontend->mydie("Could not fetch $file\n"); + } + if ($maybe_restore) { + rename "$aslocal.bak$$", $aslocal; + $CPAN::Frontend->myprint("Trying to get away with old file:\n" . + $self->ls($aslocal) . "\n"); + return $aslocal; + } + return; +} + +sub mymkpath { + my($self, $aslocal_dir) = @_; + mkpath($aslocal_dir); + $CPAN::Frontend->mywarn(qq{Warning: You are not allowed to write into }. + qq{directory "$aslocal_dir". + I\'ll continue, but if you encounter problems, they may be due + to insufficient permissions.\n}) unless -w $aslocal_dir; +} + +sub hostdlxxx { + my $self = shift; + my $level = shift; + my $scheme = shift; + my $h = shift; + $h = [ grep /^\Q$scheme\E:/, @$h ] if $scheme; + my $method = "host$level"; + $self->$method($h, @_); +} + +sub _set_attempt { + my($self,$stats,$method,$url) = @_; + push @{$stats->{attempts}}, { + method => $method, + start => _mytime, + url => $url, + }; +} + +# package CPAN::FTP; +sub hostdleasy { #called from hostdlxxx + my($self,$host_seq,$file,$aslocal,$stats) = @_; + my($ro_url); + HOSTEASY: for $ro_url (@$host_seq) { + $self->_set_attempt($stats,"dleasy",$ro_url); + my $url = "$ro_url$file"; + $self->debug("localizing perlish[$url]") if $CPAN::DEBUG; + if ($url =~ /^file:/) { + my $l; + if ($CPAN::META->has_inst('URI::URL')) { + my $u = URI::URL->new($url); + $l = $u->file; + } else { # works only on Unix, is poorly constructed, but + # hopefully better than nothing. + # RFC 1738 says fileurl BNF is + # fileurl = "file://" [ host | "localhost" ] "/" fpath + # Thanks to "Mark D. Baushke" <mdb@cisco.com> for + # the code + ($l = $url) =~ s|^file://[^/]*/|/|; # discard the host part + $l =~ s|^file:||; # assume they + # meant + # file://localhost + $l =~ s|^/||s + if ! -f $l && $l =~ m|^/\w:|; # e.g. /P: + } + $self->debug("local file[$l]") if $CPAN::DEBUG; + if ( -f $l && -r _) { + $ThesiteURL = $ro_url; + return $l; + } + # If request is for a compressed file and we can find the + # uncompressed file also, return the path of the uncompressed file + # otherwise, decompress it and return the resulting path + if ($l =~ /(.+)\.gz$/) { + my $ungz = $1; + if ( -f $ungz && -r _) { + $ThesiteURL = $ro_url; + return $ungz; + } + elsif (-f $l && -r _) { + eval { CPAN::Tarzip->new($l)->gunzip($aslocal) }; + if ( -f $aslocal && -s _) { + $ThesiteURL = $ro_url; + return $aslocal; + } + elsif (! -s $aslocal) { + unlink $aslocal; + } + elsif (-f $l) { + $CPAN::Frontend->mywarn("Error decompressing '$l': $@\n") + if $@; + return; + } + } + } + # Otherwise, return the local file path if it exists + elsif ( -f $l && -r _) { + $ThesiteURL = $ro_url; + return $l; + } + # If we can't find it, but there is a compressed version + # of it, then decompress it + elsif (-f "$l.gz") { + $self->debug("found compressed $l.gz") if $CPAN::DEBUG; + eval { CPAN::Tarzip->new("$l.gz")->gunzip($aslocal) }; + if ( -f $aslocal) { + $ThesiteURL = $ro_url; + return $aslocal; + } + else { + $CPAN::Frontend->mywarn("Error decompressing '$l': $@\n") + if $@; + return; + } + } + $CPAN::Frontend->mywarn("Could not find '$l'\n"); + } + $self->debug("it was not a file URL") if $CPAN::DEBUG; + if ($CPAN::META->has_usable('LWP')) { + $CPAN::Frontend->myprint("Fetching with LWP:\n$url\n"); + unless ($Ua) { + CPAN::LWP::UserAgent->config; + eval { $Ua = CPAN::LWP::UserAgent->new; }; + if ($@) { + $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n"); + } + } + my $res = $Ua->mirror($url, $aslocal); + if ($res->is_success) { + $ThesiteURL = $ro_url; + my $now = time; + utime $now, $now, $aslocal; # download time is more + # important than upload + # time + return $aslocal; + } elsif ($url !~ /\.gz(?!\n)\Z/) { + my $gzurl = "$url.gz"; + $CPAN::Frontend->myprint("Fetching with LWP:\n$gzurl\n"); + $res = $Ua->mirror($gzurl, "$aslocal.gz"); + if ($res->is_success) { + if (eval {CPAN::Tarzip->new("$aslocal.gz")->gunzip($aslocal)}) { + $ThesiteURL = $ro_url; + return $aslocal; + } + } + } else { + $CPAN::Frontend->myprint(sprintf( + "LWP failed with code[%s] message[%s]\n", + $res->code, + $res->message, + )); + # Alan Burlison informed me that in firewall environments + # Net::FTP can still succeed where LWP fails. So we do not + # skip Net::FTP anymore when LWP is available. + } + } elsif ($url =~ /^http:/i && $CPAN::META->has_usable('HTTP::Tiny')) { + require CPAN::HTTP::Client; + my $chc = CPAN::HTTP::Client->new( + proxy => $CPAN::Config->{http_proxy} || $ENV{http_proxy}, + no_proxy => $CPAN::Config->{no_proxy} || $ENV{no_proxy}, + ); + for my $try ( $url, ( $url !~ /\.gz(?!\n)\Z/ ? "$url.gz" : () ) ) { + $CPAN::Frontend->myprint("Fetching with HTTP::Tiny:\n$try\n"); + my $res = eval { $chc->mirror($try, $aslocal) }; + if ( $res && $res->{success} ) { + $ThesiteURL = $ro_url; + my $now = time; + utime $now, $now, $aslocal; # download time is more + # important than upload + # time + return $aslocal; + } + elsif ( $res && $res->{status} ne '599') { + $CPAN::Frontend->myprint(sprintf( + "HTTP::Tiny failed with code[%s] message[%s]\n", + $res->{status}, + $res->{reason}, + ) + ); + } + elsif ( $res && $res->{status} eq '599') { + $CPAN::Frontend->myprint(sprintf( + "HTTP::Tiny failed with an internal error: %s\n", + $res->{content}, + ) + ); + } + else { + my $err = $@ || 'Unknown error'; + $CPAN::Frontend->myprint(sprintf( + "Error downloading with HTTP::Tiny: %s\n", $err + ) + ); + } + } + } + return if $CPAN::Signal; + if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { + # that's the nice and easy way thanks to Graham + $self->debug("recognized ftp") if $CPAN::DEBUG; + my($host,$dir,$getfile) = ($1,$2,$3); + if ($CPAN::META->has_usable('Net::FTP')) { + $dir =~ s|/+|/|g; + $CPAN::Frontend->myprint("Fetching with Net::FTP:\n$url\n"); + $self->debug("getfile[$getfile]dir[$dir]host[$host]" . + "aslocal[$aslocal]") if $CPAN::DEBUG; + if (CPAN::FTP->ftp_get($host,$dir,$getfile,$aslocal)) { + $ThesiteURL = $ro_url; + return $aslocal; + } + if ($aslocal !~ /\.gz(?!\n)\Z/) { + my $gz = "$aslocal.gz"; + $CPAN::Frontend->myprint("Fetching with Net::FTP\n$url.gz\n"); + if (CPAN::FTP->ftp_get($host, + $dir, + "$getfile.gz", + $gz) && + eval{CPAN::Tarzip->new($gz)->gunzip($aslocal)} + ) { + $ThesiteURL = $ro_url; + return $aslocal; + } + } + # next HOSTEASY; + } else { + CPAN->debug("Net::FTP does not count as usable atm") if $CPAN::DEBUG; + } + } + if ( + UNIVERSAL::can($ro_url,"text") + and + $ro_url->{FROM} eq "USER" + ) { + ##address #17973: default URLs should not try to override + ##user-defined URLs just because LWP is not available + my $ret = $self->hostdlhard([$ro_url],$file,$aslocal,$stats); + return $ret if $ret; + } + return if $CPAN::Signal; + } +} + +# package CPAN::FTP; +sub hostdlhard { + my($self,$host_seq,$file,$aslocal,$stats) = @_; + + # Came back if Net::FTP couldn't establish connection (or + # failed otherwise) Maybe they are behind a firewall, but they + # gave us a socksified (or other) ftp program... + + my($ro_url); + my($devnull) = $CPAN::Config->{devnull} || ""; + # < /dev/null "; + my($aslocal_dir) = dirname($aslocal); + mkpath($aslocal_dir); + my $some_dl_success = 0; + my $any_attempt = 0; + HOSTHARD: for $ro_url (@$host_seq) { + $self->_set_attempt($stats,"dlhard",$ro_url); + my $url = "$ro_url$file"; + my($proto,$host,$dir,$getfile); + + # Courtesy Mark Conty mark_conty@cargill.com change from + # if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { + # to + if ($url =~ m|^([^:]+)://(.*?)/(.*)/(.*)|) { + # proto not yet used + ($proto,$host,$dir,$getfile) = ($1,$2,$3,$4); + } else { + next HOSTHARD; # who said, we could ftp anything except ftp? + } + next HOSTHARD if $proto eq "file"; # file URLs would have had + # success above. Likely a bogus URL + + # making at least one attempt against a host + $any_attempt++; + + $self->debug("localizing funkyftpwise[$url]") if $CPAN::DEBUG; + + # Try the most capable first and leave ncftp* for last as it only + # does FTP. + my $proxy_vars = $self->_proxy_vars($ro_url); + DLPRG: for my $f (qw(curl wget lynx ncftpget ncftp)) { + my $funkyftp = CPAN::HandleConfig->safe_quote($CPAN::Config->{$f}); + next DLPRG unless defined $funkyftp; + next DLPRG if $funkyftp =~ /^\s*$/; + + my($src_switch) = ""; + my($chdir) = ""; + my($stdout_redir) = " > \"$aslocal\""; + if ($f eq "lynx") { + $src_switch = " -source"; + } elsif ($f eq "ncftp") { + next DLPRG unless $url =~ m{\Aftp://}; + $src_switch = " -c"; + } elsif ($f eq "wget") { + $src_switch = " -O \"$aslocal\""; + $stdout_redir = ""; + } elsif ($f eq 'curl') { + $src_switch = ' -L -f -s -S --netrc-optional'; + if ($proxy_vars->{http_proxy}) { + $src_switch .= qq{ -U "$proxy_vars->{proxy_user}:$proxy_vars->{proxy_pass}" -x "$proxy_vars->{http_proxy}"}; + } + } elsif ($f eq "ncftpget") { + next DLPRG unless $url =~ m{\Aftp://}; + $chdir = "cd $aslocal_dir && "; + $stdout_redir = ""; + } + $CPAN::Frontend->myprint( + qq[ +Trying with + $funkyftp$src_switch +to get + $url +]); + my($system) = + "$chdir$funkyftp$src_switch \"$url\" $devnull$stdout_redir"; + $self->debug("system[$system]") if $CPAN::DEBUG; + my($wstatus) = system($system); + if ($f eq "lynx") { + # lynx returns 0 when it fails somewhere + if (-s $aslocal) { + my $content = do { local *FH; + open FH, $aslocal or die; + local $/; + <FH> }; + if ($content =~ /^<.*(<title>[45]|Error [45])/si) { + $CPAN::Frontend->mywarn(qq{ +No success, the file that lynx has downloaded looks like an error message: +$content +}); + $CPAN::Frontend->mysleep(1); + next DLPRG; + } + $some_dl_success++; + } else { + $CPAN::Frontend->myprint(qq{ +No success, the file that lynx has downloaded is an empty file. +}); + next DLPRG; + } + } + if ($wstatus == 0) { + if (-s $aslocal) { + # Looks good + $some_dl_success++; + } + $ThesiteURL = $ro_url; + return $aslocal; + } else { + my $estatus = $wstatus >> 8; + my $size = -f $aslocal ? + ", left\n$aslocal with size ".-s _ : + "\nWarning: expected file [$aslocal] doesn't exist"; + $CPAN::Frontend->myprint(qq{ + Function system("$system") + returned status $estatus (wstat $wstatus)$size + }); + } + return if $CPAN::Signal; + } # download/transfer programs (DLPRG) + } # host + return unless $any_attempt; + if ($some_dl_success) { + $CPAN::Frontend->mywarn("Warning: doesn't seem we had substantial success downloading '$aslocal'. Don't know how to proceed.\n"); + } else { + $CPAN::Frontend->mywarn("Warning: no success downloading '$aslocal'. Giving up on it.\n"); + } + return; +} + +#-> CPAN::FTP::_proxy_vars +sub _proxy_vars { + my($self,$url) = @_; + my $ret = +{}; + my $http_proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'}; + if ($http_proxy) { + my($host) = $url =~ m|://([^/:]+)|; + my $want_proxy = 1; + my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'} || ""; + my @noproxy = split /\s*,\s*/, $noproxy; + if ($host) { + DOMAIN: for my $domain (@noproxy) { + if ($host =~ /\Q$domain\E$/) { # cf. LWP::UserAgent + $want_proxy = 0; + last DOMAIN; + } + } + } else { + $CPAN::Frontend->mywarn(" Could not determine host from http_proxy '$http_proxy'\n"); + } + if ($want_proxy) { + my($user, $pass) = + CPAN::HTTP::Credentials->get_proxy_credentials(); + $ret = { + proxy_user => $user, + proxy_pass => $pass, + http_proxy => $http_proxy + }; + } + } + return $ret; +} + +# package CPAN::FTP; +sub hostdlhardest { + my($self,$host_seq,$file,$aslocal,$stats) = @_; + + return unless @$host_seq; + my($ro_url); + my($aslocal_dir) = dirname($aslocal); + mkpath($aslocal_dir); + my $ftpbin = $CPAN::Config->{ftp}; + unless ($ftpbin && length $ftpbin && MM->maybe_command($ftpbin)) { + $CPAN::Frontend->myprint("No external ftp command available\n\n"); + return; + } + $CPAN::Frontend->mywarn(qq{ +As a last resort we now switch to the external ftp command '$ftpbin' +to get '$aslocal'. + +Doing so often leads to problems that are hard to diagnose. + +If you're the victim of such problems, please consider unsetting the +ftp config variable with + + o conf ftp "" + o conf commit + +}); + $CPAN::Frontend->mysleep(2); + HOSTHARDEST: for $ro_url (@$host_seq) { + $self->_set_attempt($stats,"dlhardest",$ro_url); + my $url = "$ro_url$file"; + $self->debug("localizing ftpwise[$url]") if $CPAN::DEBUG; + unless ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { + next; + } + my($host,$dir,$getfile) = ($1,$2,$3); + my $timestamp = 0; + my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, + $ctime,$blksize,$blocks) = stat($aslocal); + $timestamp = $mtime ||= 0; + my($netrc) = CPAN::FTP::netrc->new; + my($netrcfile) = $netrc->netrc; + my($verbose) = $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG ? " -v" : ""; + my $targetfile = File::Basename::basename($aslocal); + my(@dialog); + push( + @dialog, + "lcd $aslocal_dir", + "cd /", + map("cd $_", split /\//, $dir), # RFC 1738 + "bin", + "passive", + "get $getfile $targetfile", + "quit" + ); + if (! $netrcfile) { + CPAN->debug("No ~/.netrc file found") if $CPAN::DEBUG; + } elsif ($netrc->hasdefault || $netrc->contains($host)) { + CPAN->debug(sprintf("hasdef[%d]cont($host)[%d]", + $netrc->hasdefault, + $netrc->contains($host))) if $CPAN::DEBUG; + if ($netrc->protected) { + my $dialog = join "", map { " $_\n" } @dialog; + my $netrc_explain; + if ($netrc->contains($host)) { + $netrc_explain = "Relying that your .netrc entry for '$host' ". + "manages the login"; + } else { + $netrc_explain = "Relying that your default .netrc entry ". + "manages the login"; + } + $CPAN::Frontend->myprint(qq{ + Trying with external ftp to get + '$url' + $netrc_explain + Sending the dialog +$dialog +} + ); + $self->talk_ftp("$ftpbin$verbose $host", + @dialog); + ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks) = stat($aslocal); + $mtime ||= 0; + if ($mtime > $timestamp) { + $CPAN::Frontend->myprint("GOT $aslocal\n"); + $ThesiteURL = $ro_url; + return $aslocal; + } else { + $CPAN::Frontend->myprint("Hmm... Still failed!\n"); + } + return if $CPAN::Signal; + } else { + $CPAN::Frontend->mywarn(qq{Your $netrcfile is not }. + qq{correctly protected.\n}); + } + } else { + $CPAN::Frontend->mywarn("Your ~/.netrc neither contains $host + nor does it have a default entry\n"); + } + + # OK, they don't have a valid ~/.netrc. Use 'ftp -n' + # then and login manually to host, using e-mail as + # password. + $CPAN::Frontend->myprint(qq{Issuing "$ftpbin$verbose -n"\n}); + unshift( + @dialog, + "open $host", + "user anonymous $Config::Config{'cf_email'}" + ); + my $dialog = join "", map { " $_\n" } @dialog; + $CPAN::Frontend->myprint(qq{ + Trying with external ftp to get + $url + Sending the dialog +$dialog +} + ); + $self->talk_ftp("$ftpbin$verbose -n", @dialog); + ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks) = stat($aslocal); + $mtime ||= 0; + if ($mtime > $timestamp) { + $CPAN::Frontend->myprint("GOT $aslocal\n"); + $ThesiteURL = $ro_url; + return $aslocal; + } else { + $CPAN::Frontend->myprint("Bad luck... Still failed!\n"); + } + return if $CPAN::Signal; + $CPAN::Frontend->mywarn("Can't access URL $url.\n\n"); + $CPAN::Frontend->mysleep(2); + } # host +} + +# package CPAN::FTP; +sub talk_ftp { + my($self,$command,@dialog) = @_; + my $fh = FileHandle->new; + $fh->open("|$command") or die "Couldn't open ftp: $!"; + foreach (@dialog) { $fh->print("$_\n") } + $fh->close; # Wait for process to complete + my $wstatus = $?; + my $estatus = $wstatus >> 8; + $CPAN::Frontend->myprint(qq{ +Subprocess "|$command" + returned status $estatus (wstat $wstatus) +}) if $wstatus; +} + +# find2perl needs modularization, too, all the following is stolen +# from there +# CPAN::FTP::ls +sub ls { + my($self,$name) = @_; + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$sizemm, + $atime,$mtime,$ctime,$blksize,$blocks) = lstat($name); + + my($perms,%user,%group); + my $pname = $name; + + if ($blocks) { + $blocks = int(($blocks + 1) / 2); + } + else { + $blocks = int(($sizemm + 1023) / 1024); + } + + if (-f _) { $perms = '-'; } + elsif (-d _) { $perms = 'd'; } + elsif (-c _) { $perms = 'c'; $sizemm = &sizemm; } + elsif (-b _) { $perms = 'b'; $sizemm = &sizemm; } + elsif (-p _) { $perms = 'p'; } + elsif (-S _) { $perms = 's'; } + else { $perms = 'l'; $pname .= ' -> ' . readlink($_); } + + my(@rwx) = ('---','--x','-w-','-wx','r--','r-x','rw-','rwx'); + my(@moname) = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); + my $tmpmode = $mode; + my $tmp = $rwx[$tmpmode & 7]; + $tmpmode >>= 3; + $tmp = $rwx[$tmpmode & 7] . $tmp; + $tmpmode >>= 3; + $tmp = $rwx[$tmpmode & 7] . $tmp; + substr($tmp,2,1) =~ tr/-x/Ss/ if -u _; + substr($tmp,5,1) =~ tr/-x/Ss/ if -g _; + substr($tmp,8,1) =~ tr/-x/Tt/ if -k _; + $perms .= $tmp; + + my $user = $user{$uid} || $uid; # too lazy to implement lookup + my $group = $group{$gid} || $gid; + + my($sec,$min,$hour,$mday,$mon,$year) = localtime($mtime); + my($timeyear); + my($moname) = $moname[$mon]; + if (-M _ > 365.25 / 2) { + $timeyear = $year + 1900; + } + else { + $timeyear = sprintf("%02d:%02d", $hour, $min); + } + + sprintf "%5lu %4ld %-10s %2d %-8s %-8s %8s %s %2d %5s %s\n", + $ino, + $blocks, + $perms, + $nlink, + $user, + $group, + $sizemm, + $moname, + $mday, + $timeyear, + $pname; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/FTP/netrc.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/FTP/netrc.pm new file mode 100644 index 0000000000..0778e8adbc --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/FTP/netrc.pm @@ -0,0 +1,62 @@ +package CPAN::FTP::netrc; +use strict; + +$CPAN::FTP::netrc::VERSION = $CPAN::FTP::netrc::VERSION = "1.01"; + +# package CPAN::FTP::netrc; +sub new { + my($class) = @_; + my $file = File::Spec->catfile($ENV{HOME},".netrc"); + + my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks) + = stat($file); + $mode ||= 0; + my $protected = 0; + + my($fh,@machines,$hasdefault); + $hasdefault = 0; + $fh = FileHandle->new or die "Could not create a filehandle"; + + if($fh->open($file)) { + $protected = ($mode & 077) == 0; + local($/) = ""; + NETRC: while (<$fh>) { + my(@tokens) = split " ", $_; + TOKEN: while (@tokens) { + my($t) = shift @tokens; + if ($t eq "default") { + $hasdefault++; + last NETRC; + } + last TOKEN if $t eq "macdef"; + if ($t eq "machine") { + push @machines, shift @tokens; + } + } + } + } else { + $file = $hasdefault = $protected = ""; + } + + bless { + 'mach' => [@machines], + 'netrc' => $file, + 'hasdefault' => $hasdefault, + 'protected' => $protected, + }, $class; +} + +# CPAN::FTP::netrc::hasdefault; +sub hasdefault { shift->{'hasdefault'} } +sub netrc { shift->{'netrc'} } +sub protected { shift->{'protected'} } +sub contains { + my($self,$mach) = @_; + for ( @{$self->{'mach'}} ) { + return 1 if $_ eq $mach; + } + return 0; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/FirstTime.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/FirstTime.pm new file mode 100644 index 0000000000..310e73abef --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/FirstTime.pm @@ -0,0 +1,2186 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::FirstTime; +use strict; + +use ExtUtils::MakeMaker (); +use FileHandle (); +use File::Basename (); +use File::Path (); +use File::Spec (); +use CPAN::Mirrors (); +use CPAN::Version (); +use vars qw($VERSION $auto_config); +$VERSION = "5.5315"; + +=head1 NAME + +CPAN::FirstTime - Utility for CPAN::Config file Initialization + +=head1 SYNOPSIS + +CPAN::FirstTime::init() + +=head1 DESCRIPTION + +The init routine asks a few questions and writes a CPAN/Config.pm or +CPAN/MyConfig.pm file (depending on what it is currently using). + +In the following all questions and explanations regarding config +variables are collected. + +=cut + +# down until the next =back the manpage must be parsed by the program +# because the text is used in the init dialogues. + +my @podpara = split /\n\n/, <<'=back'; + +=over 2 + +=item allow_installing_module_downgrades + +The CPAN shell can watch the C<blib/> directories that are built up +before running C<make test> to determine whether the current +distribution will end up with modules being overwritten with decreasing module version numbers. It +can then let the build of this distro fail when it discovers a +downgrade. + +Do you want to allow installing distros with decreasing module +versions compared to what you have installed (yes, no, ask/yes, +ask/no)? + +=item allow_installing_outdated_dists + +The CPAN shell can watch the C<blib/> directories that are built up +before running C<make test> to determine whether the current +distribution contains modules that are indexed with a distro with a +higher distro-version number than the current one. It can +then let the build of this distro fail when it would not represent the +most up-to-date version of the distro. + +Note: choosing anything but 'yes' for this option will need +CPAN::DistnameInfo being installed for taking effect. + +Do you want to allow installing distros that are not indexed as the +highest distro-version for all contained modules (yes, no, ask/yes, +ask/no)? + +=item auto_commit + +Normally CPAN.pm keeps config variables in memory and changes need to +be saved in a separate 'o conf commit' command to make them permanent +between sessions. If you set the 'auto_commit' option to true, changes +to a config variable are always automatically committed to disk. + +Always commit changes to config variables to disk? + +=item build_cache + +CPAN.pm can limit the size of the disk area for keeping the build +directories with all the intermediate files. + +Cache size for build directory (in MB)? + +=item build_dir + +Directory where the build process takes place? + +=item build_dir_reuse + +Until version 1.88 CPAN.pm never trusted the contents of the build_dir +directory between sessions. Since 1.88_58 CPAN.pm has a YAML-based +mechanism that makes it possible to share the contents of the +build_dir/ directory between different sessions with the same version +of perl. People who prefer to test things several days before +installing will like this feature because it saves a lot of time. + +If you say yes to the following question, CPAN will try to store +enough information about the build process so that it can pick up in +future sessions at the same state of affairs as it left a previous +session. + +Store and re-use state information about distributions between +CPAN.pm sessions? + +=item build_requires_install_policy + +When a module declares another one as a 'build_requires' prerequisite +this means that the other module is only needed for building or +testing the module but need not be installed permanently. In this case +you may wish to install that other module nonetheless or just keep it +in the 'build_dir' directory to have it available only temporarily. +Installing saves time on future installations but makes the perl +installation bigger. + +You can choose if you want to always install (yes), never install (no) +or be always asked. In the latter case you can set the default answer +for the question to yes (ask/yes) or no (ask/no). + +Policy on installing 'build_requires' modules (yes, no, ask/yes, +ask/no)? + +=item cache_metadata + +To considerably speed up the initial CPAN shell startup, it is +possible to use Storable to create a cache of metadata. If Storable is +not available, the normal index mechanism will be used. + +Note: this mechanism is not used when use_sqlite is on and SQLLite is +running. + +Cache metadata (yes/no)? + +=item check_sigs + +CPAN packages can be digitally signed by authors and thus verified +with the security provided by strong cryptography. The exact mechanism +is defined in the Module::Signature module. While this is generally +considered a good thing, it is not always convenient to the end user +to install modules that are signed incorrectly or where the key of the +author is not available or where some prerequisite for +Module::Signature has a bug and so on. + +With the check_sigs parameter you can turn signature checking on and +off. The default is off for now because the whole tool chain for the +functionality is not yet considered mature by some. The author of +CPAN.pm would recommend setting it to true most of the time and +turning it off only if it turns out to be annoying. + +Note that if you do not have Module::Signature installed, no signature +checks will be performed at all. + +Always try to check and verify signatures if a SIGNATURE file is in +the package and Module::Signature is installed (yes/no)? + +=item cleanup_after_install + +Users who install modules and do not intend to look back, can free +occupied disk space quickly by letting CPAN.pm cleanup each build +directory immediately after a successful install. + +Remove build directory after a successful install? (yes/no)? + +=item colorize_output + +When you have Term::ANSIColor installed, you can turn on colorized +output to have some visual differences between normal CPAN.pm output, +warnings, debugging output, and the output of the modules being +installed. Set your favorite colors after some experimenting with the +Term::ANSIColor module. + +Please note that on Windows platforms colorized output also requires +the Win32::Console::ANSI module. + +Do you want to turn on colored output? + +=item colorize_print + +Color for normal output? + +=item colorize_warn + +Color for warnings? + +=item colorize_debug + +Color for debugging messages? + +=item commandnumber_in_prompt + +The prompt of the cpan shell can contain the current command number +for easier tracking of the session or be a plain string. + +Do you want the command number in the prompt (yes/no)? + +=item connect_to_internet_ok + +If you have never defined your own C<urllist> in your configuration +then C<CPAN.pm> will be hesitant to use the built in default sites for +downloading. It will ask you once per session if a connection to the +internet is OK and only if you say yes, it will try to connect. But to +avoid this question, you can choose your favorite download sites once +and get away with it. Or, if you have no favorite download sites +answer yes to the following question. + +If no urllist has been chosen yet, would you prefer CPAN.pm to connect +to the built-in default sites without asking? (yes/no)? + +=item ftp_passive + +Shall we always set the FTP_PASSIVE environment variable when dealing +with ftp download (yes/no)? + +=item ftpstats_period + +Statistics about downloads are truncated by size and period +simultaneously. + +How many days shall we keep statistics about downloads? + +=item ftpstats_size + +Statistics about downloads are truncated by size and period +simultaneously. Setting this to zero or negative disables download +statistics. + +How many items shall we keep in the statistics about downloads? + +=item getcwd + +CPAN.pm changes the current working directory often and needs to +determine its own current working directory. Per default it uses +Cwd::cwd but if this doesn't work on your system for some reason, +alternatives can be configured according to the following table: + + cwd Cwd::cwd + getcwd Cwd::getcwd + fastcwd Cwd::fastcwd + getdcwd Cwd::getdcwd + backtickcwd external command cwd + +Preferred method for determining the current working directory? + +=item halt_on_failure + +Normally, CPAN.pm continues processing the full list of targets and +dependencies, even if one of them fails. However, you can specify +that CPAN should halt after the first failure. (Note that optional +recommended or suggested modules that fail will not cause a halt.) + +Do you want to halt on failure (yes/no)? + +=item histfile + +If you have one of the readline packages (Term::ReadLine::Perl, +Term::ReadLine::Gnu, possibly others) installed, the interactive CPAN +shell will have history support. The next two questions deal with the +filename of the history file and with its size. If you do not want to +set this variable, please hit SPACE ENTER to the following question. + +File to save your history? + +=item histsize + +Number of lines to save? + +=item inactivity_timeout + +Sometimes you may wish to leave the processes run by CPAN alone +without caring about them. Because the Makefile.PL or the Build.PL +sometimes contains question you're expected to answer, you can set a +timer that will kill a 'perl Makefile.PL' process after the specified +time in seconds. + +If you set this value to 0, these processes will wait forever. This is +the default and recommended setting. + +Timeout for inactivity during {Makefile,Build}.PL? + +=item index_expire + +The CPAN indexes are usually rebuilt once or twice per hour, but the +typical CPAN mirror mirrors only once or twice per day. Depending on +the quality of your mirror and your desire to be on the bleeding edge, +you may want to set the following value to more or less than one day +(which is the default). It determines after how many days CPAN.pm +downloads new indexes. + +Let the index expire after how many days? + +=item inhibit_startup_message + +When the CPAN shell is started it normally displays a greeting message +that contains the running version and the status of readline support. + +Do you want to turn this message off? + +=item keep_source_where + +Unless you are accessing the CPAN on your filesystem via a file: URL, +CPAN.pm needs to keep the source files it downloads somewhere. Please +supply a directory where the downloaded files are to be kept. + +Download target directory? + +=item load_module_verbosity + +When CPAN.pm loads a module it needs for some optional feature, it +usually reports about module name and version. Choose 'v' to get this +message, 'none' to suppress it. + +Verbosity level for loading modules (none or v)? + +=item makepl_arg + +Every Makefile.PL is run by perl in a separate process. Likewise we +run 'make' and 'make install' in separate processes. If you have +any parameters (e.g. PREFIX, UNINST or the like) you want to +pass to the calls, please specify them here. + +If you don't understand this question, just press ENTER. + +Typical frequently used settings: + + PREFIX=~/perl # non-root users (please see manual for more hints) + +Parameters for the 'perl Makefile.PL' command? + +=item make_arg + +Parameters for the 'make' command? Typical frequently used setting: + + -j3 # dual processor system (on GNU make) + +Your choice: + +=item make_install_arg + +Parameters for the 'make install' command? +Typical frequently used setting: + + UNINST=1 # to always uninstall potentially conflicting files + # (but do NOT use with local::lib or INSTALL_BASE) + +Your choice: + +=item make_install_make_command + +Do you want to use a different make command for 'make install'? +Cautious people will probably prefer: + + su root -c make + or + sudo make + or + /path1/to/sudo -u admin_account /path2/to/make + +or some such. Your choice: + +=item mbuildpl_arg + +A Build.PL is run by perl in a separate process. Likewise we run +'./Build' and './Build install' in separate processes. If you have any +parameters you want to pass to the calls, please specify them here. + +Typical frequently used settings: + + --install_base /home/xxx # different installation directory + +Parameters for the 'perl Build.PL' command? + +=item mbuild_arg + +Parameters for the './Build' command? Setting might be: + + --extra_linker_flags -L/usr/foo/lib # non-standard library location + +Your choice: + +=item mbuild_install_arg + +Parameters for the './Build install' command? Typical frequently used +setting: + + --uninst 1 # uninstall conflicting files + # (but do NOT use with local::lib or INSTALL_BASE) + +Your choice: + +=item mbuild_install_build_command + +Do you want to use a different command for './Build install'? Sudo +users will probably prefer: + + su root -c ./Build + or + sudo ./Build + or + /path1/to/sudo -u admin_account ./Build + +or some such. Your choice: + +=item pager + +What is your favorite pager program? + +=item prefer_installer + +When you have Module::Build installed and a module comes with both a +Makefile.PL and a Build.PL, which shall have precedence? + +The main two standard installer modules are the old and well +established ExtUtils::MakeMaker (for short: EUMM) which uses the +Makefile.PL. And the next generation installer Module::Build (MB) +which works with the Build.PL (and often comes with a Makefile.PL +too). If a module comes only with one of the two we will use that one +but if both are supplied then a decision must be made between EUMM and +MB. See also http://rt.cpan.org/Ticket/Display.html?id=29235 for a +discussion about the right default. + +Or, as a third option you can choose RAND which will make a random +decision (something regular CPAN testers will enjoy). + +In case you can choose between running a Makefile.PL or a Build.PL, +which installer would you prefer (EUMM or MB or RAND)? + +=item prefs_dir + +CPAN.pm can store customized build environments based on regular +expressions for distribution names. These are YAML files where the +default options for CPAN.pm and the environment can be overridden and +dialog sequences can be stored that can later be executed by an +Expect.pm object. The CPAN.pm distribution comes with some prefab YAML +files that cover sample distributions that can be used as blueprints +to store your own prefs. Please check out the distroprefs/ directory of +the CPAN.pm distribution to get a quick start into the prefs system. + +Directory where to store default options/environment/dialogs for +building modules that need some customization? + +=item prerequisites_policy + +The CPAN module can detect when a module which you are trying to build +depends on prerequisites. If this happens, it can build the +prerequisites for you automatically ('follow'), ask you for +confirmation ('ask'), or just ignore them ('ignore'). Choosing +'follow' also sets PERL_AUTOINSTALL and PERL_EXTUTILS_AUTOINSTALL for +"--defaultdeps" if not already set. + +Please set your policy to one of the three values. + +Policy on building prerequisites (follow, ask or ignore)? + +=item randomize_urllist + +CPAN.pm can introduce some randomness when using hosts for download +that are configured in the urllist parameter. Enter a numeric value +between 0 and 1 to indicate how often you want to let CPAN.pm try a +random host from the urllist. A value of one specifies to always use a +random host as the first try. A value of zero means no randomness at +all. Anything in between specifies how often, on average, a random +host should be tried first. + +Randomize parameter + +=item recommends_policy + +(Experimental feature!) Some CPAN modules recommend additional, optional dependencies. These should +generally be installed except in resource constrained environments. When this +policy is true, recommended modules will be included with required modules. + +Include recommended modules? + +=item scan_cache + +By default, each time the CPAN module is started, cache scanning is +performed to keep the cache size in sync ('atstart'). Alternatively, +scanning and cleanup can happen when CPAN exits ('atexit'). To prevent +any cache cleanup, answer 'never'. + +Perform cache scanning ('atstart', 'atexit' or 'never')? + +=item shell + +What is your favorite shell? + +=item show_unparsable_versions + +During the 'r' command CPAN.pm finds modules without version number. +When the command finishes, it prints a report about this. If you +want this report to be very verbose, say yes to the following +variable. + +Show all individual modules that have no $VERSION? + +=item show_upload_date + +The 'd' and the 'm' command normally only show you information they +have in their in-memory database and thus will never connect to the +internet. If you set the 'show_upload_date' variable to true, 'm' and +'d' will additionally show you the upload date of the module or +distribution. Per default this feature is off because it may require a +net connection to get at the upload date. + +Always try to show upload date with 'd' and 'm' command (yes/no)? + +=item show_zero_versions + +During the 'r' command CPAN.pm finds modules with a version number of +zero. When the command finishes, it prints a report about this. If you +want this report to be very verbose, say yes to the following +variable. + +Show all individual modules that have a $VERSION of zero? + +=item suggests_policy + +(Experimental feature!) Some CPAN modules suggest additional, optional dependencies. These 'suggest' +dependencies provide enhanced operation. When this policy is true, suggested +modules will be included with required modules. + +Include suggested modules? + +=item tar_verbosity + +When CPAN.pm uses the tar command, which switch for the verbosity +shall be used? Choose 'none' for quiet operation, 'v' for file +name listing, 'vv' for full listing. + +Tar command verbosity level (none or v or vv)? + +=item term_is_latin + +The next option deals with the charset (a.k.a. character set) your +terminal supports. In general, CPAN is English speaking territory, so +the charset does not matter much but some CPAN have names that are +outside the ASCII range. If your terminal supports UTF-8, you should +say no to the next question. If it expects ISO-8859-1 (also known as +LATIN1) then you should say yes. If it supports neither, your answer +does not matter because you will not be able to read the names of some +authors anyway. If you answer no, names will be output in UTF-8. + +Your terminal expects ISO-8859-1 (yes/no)? + +=item term_ornaments + +When using Term::ReadLine, you can turn ornaments on so that your +input stands out against the output from CPAN.pm. + +Do you want to turn ornaments on? + +=item test_report + +The goal of the CPAN Testers project (http://testers.cpan.org/) is to +test as many CPAN packages as possible on as many platforms as +possible. This provides valuable feedback to module authors and +potential users to identify bugs or platform compatibility issues and +improves the overall quality and value of CPAN. + +One way you can contribute is to send test results for each module +that you install. If you install the CPAN::Reporter module, you have +the option to automatically generate and deliver test reports to CPAN +Testers whenever you run tests on a CPAN package. + +See the CPAN::Reporter documentation for additional details and +configuration settings. If your firewall blocks outgoing traffic, +you may need to configure CPAN::Reporter before sending reports. + +Generate test reports if CPAN::Reporter is installed (yes/no)? + +=item perl5lib_verbosity + +When CPAN.pm extends @INC via PERL5LIB, it prints a list of +directories added (or a summary of how many directories are +added). Choose 'v' to get this message, 'none' to suppress it. + +Verbosity level for PERL5LIB changes (none or v)? + +=item prefer_external_tar + +Per default all untar operations are done with the perl module +Archive::Tar; by setting this variable to true the external tar +command is used if available; on Unix this is usually preferred +because they have a reliable and fast gnutar implementation. + +Use the external tar program instead of Archive::Tar? + +=item trust_test_report_history + +When a distribution has already been tested by CPAN::Reporter on +this machine, CPAN can skip the test phase and just rely on the +test report history instead. + +Note that this will not apply to distributions that failed tests +because of missing dependencies. Also, tests can be run +regardless of the history using "force". + +Do you want to rely on the test report history (yes/no)? + +=item urllist_ping_external + +When automatic selection of the nearest cpan mirrors is performed, +turn on the use of the external ping via Net::Ping::External. This is +recommended in the case the local network has a transparent proxy. + +Do you want to use the external ping command when autoselecting +mirrors? + +=item urllist_ping_verbose + +When automatic selection of the nearest cpan mirrors is performed, +this option can be used to turn on verbosity during the selection +process. + +Do you want to see verbosity turned on when autoselecting mirrors? + +=item use_prompt_default + +When this is true, CPAN will set PERL_MM_USE_DEFAULT to a true +value. This causes ExtUtils::MakeMaker (and compatible) prompts +to use default values instead of stopping to prompt you to answer +questions. It also sets NONINTERACTIVE_TESTING to a true value to +signal more generally that distributions should not try to +interact with you. + +Do you want to use prompt defaults (yes/no)? + +=item use_sqlite + +CPAN::SQLite is a layer between the index files that are downloaded +from the CPAN and CPAN.pm that speeds up metadata queries and reduces +memory consumption of CPAN.pm considerably. + +Use CPAN::SQLite if available? (yes/no)? + +=item version_timeout + +This timeout prevents CPAN from hanging when trying to parse a +pathologically coded $VERSION from a module. + +The default is 15 seconds. If you set this value to 0, no timeout +will occur, but this is not recommended. + +Timeout for parsing module versions? + +=item yaml_load_code + +Both YAML.pm and YAML::Syck are capable of deserialising code. As this +requires a string eval, which might be a security risk, you can use +this option to enable or disable the deserialisation of code via +CPAN::DeferredCode. (Note: This does not work under perl 5.6) + +Do you want to enable code deserialisation (yes/no)? + +=item yaml_module + +At the time of this writing (2009-03) there are three YAML +implementations working: YAML, YAML::Syck, and YAML::XS. The latter +two are faster but need a C compiler installed on your system. There +may be more alternative YAML conforming modules. When I tried two +other players, YAML::Tiny and YAML::Perl, they seemed not powerful +enough to work with CPAN.pm. This may have changed in the meantime. + +Which YAML implementation would you prefer? + +=back + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut + +use vars qw( %prompts ); + +{ + + my @prompts = ( + +auto_config => qq{ +CPAN.pm requires configuration, but most of it can be done automatically. +If you answer 'no' below, you will enter an interactive dialog for each +configuration option instead. + +Would you like to configure as much as possible automatically?}, + +auto_pick => qq{ +Would you like me to automatically choose some CPAN mirror +sites for you? (This means connecting to the Internet)}, + +config_intro => qq{ + +The following questions are intended to help you with the +configuration. The CPAN module needs a directory of its own to cache +important index files and maybe keep a temporary mirror of CPAN files. +This may be a site-wide or a personal directory. + +}, + +# cpan_home => qq{ }, + +cpan_home_where => qq{ + +First of all, I'd like to create this directory. Where? + +}, + +external_progs => qq{ + +The CPAN module will need a few external programs to work properly. +Please correct me, if I guess the wrong path for a program. Don't +panic if you do not have some of them, just press ENTER for those. To +disable the use of a program, you can type a space followed by ENTER. + +}, + +proxy_intro => qq{ + +If you're accessing the net via proxies, you can specify them in the +CPAN configuration or via environment variables. The variable in +the \$CPAN::Config takes precedence. + +}, + +proxy_user => qq{ + +If your proxy is an authenticating proxy, you can store your username +permanently. If you do not want that, just press ENTER. You will then +be asked for your username in every future session. + +}, + +proxy_pass => qq{ + +Your password for the authenticating proxy can also be stored +permanently on disk. If this violates your security policy, just press +ENTER. You will then be asked for the password in every future +session. + +}, + +urls_intro => qq{ +Now you need to choose your CPAN mirror sites. You can let me +pick mirrors for you, you can select them from a list or you +can enter them by hand. +}, + +urls_picker_intro => qq{First, pick a nearby continent and country by typing in the number(s) +in front of the item(s) you want to select. You can pick several of +each, separated by spaces. Then, you will be presented with a list of +URLs of CPAN mirrors in the countries you selected, along with +previously selected URLs. Select some of those URLs, or just keep the +old list. Finally, you will be prompted for any extra URLs -- file:, +ftp:, or http: -- that host a CPAN mirror. + +You should select more than one (just in case the first isn't available). + +}, + +password_warn => qq{ + +Warning: Term::ReadKey seems not to be available, your password will +be echoed to the terminal! + +}, + +install_help => qq{ +Warning: You do not have write permission for Perl library directories. + +To install modules, you need to configure a local Perl library directory or +escalate your privileges. CPAN can help you by bootstrapping the local::lib +module or by configuring itself to use 'sudo' (if available). You may also +resolve this problem manually if you need to customize your setup. + +What approach do you want? (Choose 'local::lib', 'sudo' or 'manual') +}, + +local_lib_installed => qq{ +local::lib is installed. You must now add the following environment variables +to your shell configuration files (or registry, if you are on Windows) and +then restart your command line shell and CPAN before installing modules: + +}, + + ); + + die "Coding error in \@prompts declaration. Odd number of elements, above" + if (@prompts % 2); + + %prompts = @prompts; + + if (scalar(keys %prompts) != scalar(@prompts)/2) { + my %already; + for my $item (0..$#prompts) { + next if $item % 2; + die "$prompts[$item] is duplicated\n" if $already{$prompts[$item]}++; + } + } + + shift @podpara; + while (@podpara) { + warn "Alert: cannot parse my own manpage for init dialog" unless $podpara[0] =~ s/^=item\s+//; + my $name = shift @podpara; + my @para; + while (@podpara && $podpara[0] !~ /^=item/) { + push @para, shift @podpara; + } + $prompts{$name} = pop @para; + if (@para) { + $prompts{$name . "_intro"} = join "", map { "$_\n\n" } @para; + } + } + +} + +sub init { + my($configpm, %args) = @_; + use Config; + # extra args after 'o conf init' + my $matcher = $args{args} && @{$args{args}} ? $args{args}[0] : ''; + if ($matcher =~ /^\/(.*)\/$/) { + # case /regex/ => take the first, ignore the rest + $matcher = $1; + shift @{$args{args}}; + if (@{$args{args}}) { + local $" = " "; + $CPAN::Frontend->mywarn("Ignoring excessive arguments '@{$args{args}}'"); + $CPAN::Frontend->mysleep(2); + } + } elsif (0 == length $matcher) { + } elsif (0 && $matcher eq "~") { # extremely buggy, but a nice idea + my @unconfigured = sort grep { not exists $CPAN::Config->{$_} + or not defined $CPAN::Config->{$_} + or not length $CPAN::Config->{$_} + } keys %$CPAN::Config; + $matcher = "\\b(".join("|", @unconfigured).")\\b"; + $CPAN::Frontend->mywarn("matcher[$matcher]"); + } else { + # case WORD... => all arguments must be valid + for my $arg (@{$args{args}}) { + unless (exists $CPAN::HandleConfig::keys{$arg}) { + $CPAN::Frontend->mywarn("'$arg' is not a valid configuration variable\n"); + return; + } + } + $matcher = "\\b(".join("|",@{$args{args}}).")\\b"; + } + CPAN->debug("matcher[$matcher]") if $CPAN::DEBUG; + + unless ($CPAN::VERSION) { + require CPAN::Nox; + } + require CPAN::HandleConfig; + CPAN::HandleConfig::require_myconfig_or_config(); + $CPAN::Config ||= {}; + local($/) = "\n"; + local($\) = ""; + local($|) = 1; + + my($ans,$default); # why so half global? + + # + #= Files, directories + # + + local *_real_prompt; + if ( $args{autoconfig} ) { + $auto_config = 1; + } elsif ($matcher) { + $auto_config = 0; + } else { + my $_conf = prompt($prompts{auto_config}, "yes"); + $auto_config = ($_conf and $_conf =~ /^y/i) ? 1 : 0; + } + CPAN->debug("auto_config[$auto_config]") if $CPAN::DEBUG; + if ( $auto_config ) { + local $^W = 0; + # prototype should match that of &MakeMaker::prompt + my $current_second = time; + my $current_second_count = 0; + my $i_am_mad = 0; + # silent prompting -- just quietly use default + *_real_prompt = sub { return $_[1] }; + } + + # + # bootstrap local::lib or sudo + # + unless ( $matcher + || _can_write_to_libdirs() || _using_installbase() || _using_sudo() + ) { + local $auto_config = 0; # We *must* ask, even under autoconfig + local *_real_prompt; # We *must* show prompt + my_prompt_loop(install_help => 'local::lib', $matcher, + 'local::lib|sudo|manual'); + } + $CPAN::Config->{install_help} ||= ''; # Temporary to suppress warnings + + if (!$matcher or q{ + build_dir + build_dir_reuse + cpan_home + keep_source_where + prefs_dir + } =~ /$matcher/) { + $CPAN::Frontend->myprint($prompts{config_intro}) unless $auto_config; + + init_cpan_home($matcher); + + my_dflt_prompt("keep_source_where", + File::Spec->catdir($CPAN::Config->{cpan_home},"sources"), + $matcher, + ); + my_dflt_prompt("build_dir", + File::Spec->catdir($CPAN::Config->{cpan_home},"build"), + $matcher + ); + my_yn_prompt(build_dir_reuse => 0, $matcher); + my_dflt_prompt("prefs_dir", + File::Spec->catdir($CPAN::Config->{cpan_home},"prefs"), + $matcher + ); + } + + # + #= Config: auto_commit + # + + my_yn_prompt(auto_commit => 0, $matcher); + + # + #= Cache size, Index expire + # + my_dflt_prompt(build_cache => 100, $matcher); + + my_dflt_prompt(index_expire => 1, $matcher); + my_prompt_loop(scan_cache => 'atstart', $matcher, 'atstart|atexit|never'); + my_yn_prompt(cleanup_after_install => 0, $matcher); + + # + #= cache_metadata + # + + my_yn_prompt(cache_metadata => 1, $matcher); + my_yn_prompt(use_sqlite => 0, $matcher); + + # + #= Do we follow PREREQ_PM? + # + + my_prompt_loop(prerequisites_policy => 'follow', $matcher, + 'follow|ask|ignore'); + my_prompt_loop(build_requires_install_policy => 'yes', $matcher, + 'yes|no|ask/yes|ask/no'); + my_yn_prompt(recommends_policy => 1, $matcher); + my_yn_prompt(suggests_policy => 0, $matcher); + + # + #= Module::Signature + # + my_yn_prompt(check_sigs => 0, $matcher); + + # + #= CPAN::Reporter + # + if (!$matcher or 'test_report' =~ /$matcher/) { + my_yn_prompt(test_report => 0, $matcher); + if ( + $matcher && + $CPAN::Config->{test_report} && + $CPAN::META->has_inst("CPAN::Reporter") && + CPAN::Reporter->can('configure') + ) { + my $_conf = prompt("Would you like me configure CPAN::Reporter now?", "yes"); + if ($_conf =~ /^y/i) { + $CPAN::Frontend->myprint("\nProceeding to configure CPAN::Reporter.\n"); + CPAN::Reporter::configure(); + $CPAN::Frontend->myprint("\nReturning to CPAN configuration.\n"); + } + } + } + + my_yn_prompt(trust_test_report_history => 0, $matcher); + + # + #= YAML vs. YAML::Syck + # + if (!$matcher or "yaml_module" =~ /$matcher/) { + my_dflt_prompt(yaml_module => "YAML", $matcher); + my $old_v = $CPAN::Config->{load_module_verbosity}; + $CPAN::Config->{load_module_verbosity} = q[none]; + if (!$auto_config && !$CPAN::META->has_inst($CPAN::Config->{yaml_module})) { + $CPAN::Frontend->mywarn + ("Warning (maybe harmless): '$CPAN::Config->{yaml_module}' not installed.\n"); + $CPAN::Frontend->mysleep(3); + } + $CPAN::Config->{load_module_verbosity} = $old_v; + } + + # + #= YAML code deserialisation + # + my_yn_prompt(yaml_load_code => 0, $matcher); + + # + #= External programs + # + my(@path) = split /$Config{'path_sep'}/, $ENV{'PATH'}; + $CPAN::Frontend->myprint($prompts{external_progs}) + if !$matcher && !$auto_config; + _init_external_progs($matcher, { + path => \@path, + progs => [ qw/make bzip2 gzip tar unzip gpg patch applypatch/ ], + shortcut => 0 + }); + _init_external_progs($matcher, { + path => \@path, + progs => [ qw/wget curl lynx ncftpget ncftp ftp/ ], + shortcut => 1 + }); + + { + my $path = $CPAN::Config->{'pager'} || + $ENV{PAGER} || find_exe("less",\@path) || + find_exe("more",\@path) || ($^O eq 'MacOS' ? $ENV{EDITOR} : 0 ) + || "more"; + my_dflt_prompt(pager => $path, $matcher); + } + + { + my $path = $CPAN::Config->{'shell'}; + if ($path && File::Spec->file_name_is_absolute($path)) { + $CPAN::Frontend->mywarn("Warning: configured $path does not exist\n") + unless -e $path; + $path = ""; + } + $path ||= $ENV{SHELL}; + $path ||= $ENV{COMSPEC} if $^O eq "MSWin32"; + if ($^O eq 'MacOS') { + $CPAN::Config->{'shell'} = 'not_here'; + } else { + $path ||= 'sh', $path =~ s,\\,/,g if $^O eq 'os2'; # Cosmetic only + my_dflt_prompt(shell => $path, $matcher); + } + } + + { + my $tar = $CPAN::Config->{tar}; + my $prefer_external_tar = $CPAN::Config->{prefer_external_tar}; # XXX not yet supported + unless (defined $prefer_external_tar) { + if ($^O =~ /(MSWin32|solaris)/) { + # both have a record of broken tars + $prefer_external_tar = 0; + } elsif ($tar) { + $prefer_external_tar = 1; + } else { + $prefer_external_tar = 0; + } + } + my_yn_prompt(prefer_external_tar => $prefer_external_tar, $matcher); + } + + # + # verbosity + # + + my_prompt_loop(tar_verbosity => 'none', $matcher, + 'none|v|vv'); + my_prompt_loop(load_module_verbosity => 'none', $matcher, + 'none|v'); + my_prompt_loop(perl5lib_verbosity => 'none', $matcher, + 'none|v'); + my_yn_prompt(inhibit_startup_message => 0, $matcher); + + # + #= Installer, arguments to make etc. + # + + my_prompt_loop(prefer_installer => 'MB', $matcher, 'MB|EUMM|RAND'); + + if (!$matcher or 'makepl_arg make_arg' =~ /$matcher/) { + my_dflt_prompt(makepl_arg => "", $matcher); + my_dflt_prompt(make_arg => "", $matcher); + if ( $CPAN::Config->{makepl_arg} =~ /LIBS=|INC=/ ) { + $CPAN::Frontend->mywarn( + "Warning: Using LIBS or INC in makepl_arg will likely break distributions\n" . + "that specify their own LIBS or INC options in Makefile.PL.\n" + ); + } + + } + + require CPAN::HandleConfig; + if (exists $CPAN::HandleConfig::keys{make_install_make_command}) { + # as long as Windows needs $self->_build_command, we cannot + # support sudo on windows :-) + my $default = $CPAN::Config->{make} || ""; + if ( $default && $CPAN::Config->{install_help} eq 'sudo' ) { + if ( find_exe('sudo') ) { + $default = "sudo $default"; + delete $CPAN::Config->{make_install_make_command} + unless $CPAN::Config->{make_install_make_command} =~ /sudo/; + } + else { + $CPAN::Frontend->mywarnonce("Could not find 'sudo' in PATH\n"); + } + } + my_dflt_prompt(make_install_make_command => $default, $matcher); + } + + my_dflt_prompt(make_install_arg => $CPAN::Config->{make_arg} || "", + $matcher); + + my_dflt_prompt(mbuildpl_arg => "", $matcher); + my_dflt_prompt(mbuild_arg => "", $matcher); + + if (exists $CPAN::HandleConfig::keys{mbuild_install_build_command} + and $^O ne "MSWin32") { + # as long as Windows needs $self->_build_command, we cannot + # support sudo on windows :-) + my $default = $^O eq 'VMS' ? '@Build.com' : "./Build"; + if ( $CPAN::Config->{install_help} eq 'sudo' ) { + if ( find_exe('sudo') ) { + $default = "sudo $default"; + delete $CPAN::Config->{mbuild_install_build_command} + unless $CPAN::Config->{mbuild_install_build_command} =~ /sudo/; + } + else { + $CPAN::Frontend->mywarnonce("Could not find 'sudo' in PATH\n"); + } + } + my_dflt_prompt(mbuild_install_build_command => $default, $matcher); + } + + my_dflt_prompt(mbuild_install_arg => "", $matcher); + + for my $o (qw( + allow_installing_outdated_dists + allow_installing_module_downgrades + )) { + my_prompt_loop($o => 'ask/no', $matcher, + 'yes|no|ask/yes|ask/no'); + } + + # + #== use_prompt_default + # + my_yn_prompt(use_prompt_default => 0, $matcher); + + # + #= Alarm period + # + + my_dflt_prompt(inactivity_timeout => 0, $matcher); + my_dflt_prompt(version_timeout => 15, $matcher); + + # + #== halt_on_failure + # + my_yn_prompt(halt_on_failure => 0, $matcher); + + # + #= Proxies + # + + my @proxy_vars = qw/ftp_proxy http_proxy no_proxy/; + my @proxy_user_vars = qw/proxy_user proxy_pass/; + if (!$matcher or "@proxy_vars @proxy_user_vars" =~ /$matcher/) { + $CPAN::Frontend->myprint($prompts{proxy_intro}) unless $auto_config; + + for (@proxy_vars) { + $prompts{$_} = "Your $_?"; + my_dflt_prompt($_ => $ENV{$_}||"", $matcher); + } + + if ($CPAN::Config->{ftp_proxy} || + $CPAN::Config->{http_proxy}) { + + $default = $CPAN::Config->{proxy_user} || $CPAN::LWP::UserAgent::USER || ""; + + $CPAN::Frontend->myprint($prompts{proxy_user}) unless $auto_config; + + if ($CPAN::Config->{proxy_user} = prompt("Your proxy user id?",$default)) { + $CPAN::Frontend->myprint($prompts{proxy_pass}) unless $auto_config; + + if ($CPAN::META->has_inst("Term::ReadKey")) { + Term::ReadKey::ReadMode("noecho"); + } else { + $CPAN::Frontend->myprint($prompts{password_warn}) unless $auto_config; + } + $CPAN::Config->{proxy_pass} = prompt_no_strip("Your proxy password?"); + if ($CPAN::META->has_inst("Term::ReadKey")) { + Term::ReadKey::ReadMode("restore"); + } + $CPAN::Frontend->myprint("\n\n") unless $auto_config; + } + } + } + + # + #= how plugins work + # + + # XXX MISSING: my_array_prompt to be used with plugins. We did something like this near + # git log -p fd68f8f5e33f4cecea4fdb7abc5ee19c12f138f0..test-notest-test-dependency + # Need to do similar steps for plugin_list. As long as we do not support it here, people + # must use the cpan shell prompt to write something like + # o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/foo-20141013,... + # o conf commit + + # + #= how FTP works + # + + my_yn_prompt(ftp_passive => 1, $matcher); + + # + #= how cwd works + # + + my_prompt_loop(getcwd => 'cwd', $matcher, + 'cwd|getcwd|fastcwd|getdcwd|backtickcwd'); + + # + #= the CPAN shell itself (prompt, color) + # + + my_yn_prompt(commandnumber_in_prompt => 1, $matcher); + my_yn_prompt(term_ornaments => 1, $matcher); + if ("colorize_output colorize_print colorize_warn colorize_debug" =~ $matcher) { + my_yn_prompt(colorize_output => 0, $matcher); + if ($CPAN::Config->{colorize_output}) { + if ($CPAN::META->has_inst("Term::ANSIColor")) { + my $T="gYw"; + $CPAN::Frontend->myprint( " on_ on_y ". + " on_ma on_\n") unless $auto_config; + $CPAN::Frontend->myprint( " on_black on_red green ellow ". + "on_blue genta on_cyan white\n") unless $auto_config; + + for my $FG ("", "bold", + map {$_,"bold $_"} "black","red","green", + "yellow","blue", + "magenta", + "cyan","white") { + $CPAN::Frontend->myprint(sprintf( "%12s ", $FG)) unless $auto_config; + for my $BG ("",map {"on_$_"} qw(black red green yellow + blue magenta cyan white)) { + $CPAN::Frontend->myprint( $FG||$BG ? + Term::ANSIColor::colored(" $T ","$FG $BG") : " $T ") unless $auto_config; + } + $CPAN::Frontend->myprint( "\n" ) unless $auto_config; + } + $CPAN::Frontend->myprint( "\n" ) unless $auto_config; + } + for my $tuple ( + ["colorize_print", "bold blue on_white"], + ["colorize_warn", "bold red on_white"], + ["colorize_debug", "black on_cyan"], + ) { + my_dflt_prompt($tuple->[0] => $tuple->[1], $matcher); + if ($CPAN::META->has_inst("Term::ANSIColor")) { + eval { Term::ANSIColor::color($CPAN::Config->{$tuple->[0]})}; + if ($@) { + $CPAN::Config->{$tuple->[0]} = $tuple->[1]; + $CPAN::Frontend->mywarn($@."setting to default '$tuple->[1]'\n"); + } + } + } + } + } + + # + #== term_is_latin + # + + my_yn_prompt(term_is_latin => 1, $matcher); + + # + #== save history in file 'histfile' + # + + if (!$matcher or 'histfile histsize' =~ /$matcher/) { + $CPAN::Frontend->myprint($prompts{histfile_intro}) unless $auto_config; + defined($default = $CPAN::Config->{histfile}) or + $default = File::Spec->catfile($CPAN::Config->{cpan_home},"histfile"); + my_dflt_prompt(histfile => $default, $matcher); + + if ($CPAN::Config->{histfile}) { + defined($default = $CPAN::Config->{histsize}) or $default = 100; + my_dflt_prompt(histsize => $default, $matcher); + } + } + + # + #== do an ls on the m or the d command + # + my_yn_prompt(show_upload_date => 0, $matcher); + + # + #== verbosity at the end of the r command + # + if (!$matcher + or 'show_unparsable_versions' =~ /$matcher/ + or 'show_zero_versions' =~ /$matcher/ + ) { + my_yn_prompt(show_unparsable_versions => 0, $matcher); + my_yn_prompt(show_zero_versions => 0, $matcher); + } + + # + #= MIRRORED.BY and conf_sites() + # + + # Let's assume they want to use the internet and make them turn it + # off if they really don't. + my_yn_prompt("connect_to_internet_ok" => 1, $matcher); + + # Allow matching but don't show during manual config + if ($matcher) { + if ("urllist_ping_external" =~ $matcher) { + my_yn_prompt(urllist_ping_external => 0, $matcher); + } + if ("urllist_ping_verbose" =~ $matcher) { + my_yn_prompt(urllist_ping_verbose => 0, $matcher); + } + if ("randomize_urllist" =~ $matcher) { + my_dflt_prompt(randomize_urllist => 0, $matcher); + } + if ("ftpstats_size" =~ $matcher) { + my_dflt_prompt(ftpstats_size => 99, $matcher); + } + if ("ftpstats_period" =~ $matcher) { + my_dflt_prompt(ftpstats_period => 14, $matcher); + } + } + + $CPAN::Config->{urllist} ||= []; + + if ($auto_config) { + if(@{ $CPAN::Config->{urllist} }) { + $CPAN::Frontend->myprint( + "Your 'urllist' is already configured. Type 'o conf init urllist' to change it.\n" + ); + } + else { + $CPAN::Config->{urllist} = [ 'http://www.cpan.org/' ]; + } + } + elsif (!$matcher || "urllist" =~ $matcher) { + _do_pick_mirrors(); + } + + if ($auto_config) { + $CPAN::Frontend->myprint( + "\nAutoconfiguration complete.\n" + ); + $auto_config = 0; # reset + } + + # bootstrap local::lib now if requested + if ( $CPAN::Config->{install_help} eq 'local::lib' ) { + if ( ! @{ $CPAN::Config->{urllist} } ) { + $CPAN::Frontend->myprint( + "Skipping local::lib bootstrap because 'urllist' is not configured.\n" + ); + } + else { + $CPAN::Frontend->myprint("\nAttempting to bootstrap local::lib...\n"); + $CPAN::Frontend->myprint("\nWriting $configpm for bootstrap...\n"); + delete $CPAN::Config->{install_help}; # temporary only + CPAN::HandleConfig->commit; + my($dist, $locallib); + $locallib = CPAN::Shell->expand('Module', 'local::lib'); + if ( $locallib and $dist = $locallib->distribution ) { + # this is a hack to force bootstrapping + $dist->{prefs}{pl}{commandline} = "$^X Makefile.PL --bootstrap"; + # Set @INC for this process so we find things as they bootstrap + require lib; + lib->import(_local_lib_inc_path()); + eval { $dist->install }; + } + if ( ! $dist || (my $err = $@) ) { + $err ||= 'Could not locate local::lib in the CPAN index'; + $CPAN::Frontend->mywarn("Error bootstrapping local::lib: $@\n"); + $CPAN::Frontend->myprint("From the CPAN Shell, you might try 'look local::lib' and \n" + . "run 'perl Makefile --bootstrap' and see if that is successful. Then\n" + . "restart your CPAN client\n" + ); + } + else { + _local_lib_config(); + } + } + } + + # install_help is temporary for configuration and not saved + delete $CPAN::Config->{install_help}; + + $CPAN::Frontend->myprint("\n"); + if ($matcher && !$CPAN::Config->{auto_commit}) { + $CPAN::Frontend->myprint("Please remember to call 'o conf commit' to ". + "make the config permanent!\n"); + } else { + CPAN::HandleConfig->commit; + } + + if (! $matcher) { + $CPAN::Frontend->myprint( + "\nYou can re-run configuration any time with 'o conf init' in the CPAN shell\n" + ); + } + +} + +sub _local_lib_config { + # Set environment stuff for this process + require local::lib; + + # Tell user about environment vars to set + $CPAN::Frontend->myprint($prompts{local_lib_installed}); + local $ENV{SHELL} = $CPAN::Config->{shell} || $ENV{SHELL}; + my $shellvars = local::lib->environment_vars_string_for(_local_lib_path()); + $CPAN::Frontend->myprint($shellvars); + + # Set %ENV after getting string above + my %env = local::lib->build_environment_vars_for(_local_lib_path(), 1); + while ( my ($k, $v) = each %env ) { + $ENV{$k} = $v; + } + + # Offer to mangle the shell config + my $munged_rc; + if ( my $rc = _find_shell_config() ) { + local $auto_config = 0; # We *must* ask, even under autoconfig + local *_real_prompt; # We *must* show prompt + my $_conf = prompt( + "\nWould you like me to append that to $rc now?", "yes" + ); + if ($_conf =~ /^y/i) { + open my $fh, ">>", $rc; + print {$fh} "\n$shellvars"; + close $fh; + $munged_rc++; + } + } + + # Warn at exit time + if ($munged_rc) { + push @{$CPAN::META->_exit_messages}, << "HERE"; + +*** Remember to restart your shell before running cpan again *** +HERE + } + else { + push @{$CPAN::META->_exit_messages}, << "HERE"; + +*** Remember to add these environment variables to your shell config + and restart your shell before running cpan again *** + +$shellvars +HERE + } +} + +{ + my %shell_rc_map = ( + map { $_ => ".${_}rc" } qw/ bash tcsh csh /, + map { $_ => ".profile" } qw/dash ash sh/, + zsh => ".zshenv", + ); + + sub _find_shell_config { + my $shell = File::Basename::basename($CPAN::Config->{shell}); + if ( my $rc = $shell_rc_map{$shell} ) { + my $path = File::Spec->catfile($ENV{HOME}, $rc); + return $path if -w $path; + } + } +} + + +sub _local_lib_inc_path { + return File::Spec->catdir(_local_lib_path(), qw/lib perl5/); +} + +sub _local_lib_path { + return File::Spec->catdir(_local_lib_home(), 'perl5'); +} + +# Adapted from resolve_home_path() in local::lib -- this is where +# local::lib thinks the user's home is +{ + my $local_lib_home; + sub _local_lib_home { + $local_lib_home ||= File::Spec->rel2abs( do { + if ($CPAN::META->has_usable("File::HomeDir") && File::HomeDir->VERSION >= 0.65) { + File::HomeDir->my_home; + } elsif (defined $ENV{HOME}) { + $ENV{HOME}; + } else { + (getpwuid $<)[7] || "~"; + } + }); + } +} + +sub _do_pick_mirrors { + local *_real_prompt; + *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; + $CPAN::Frontend->myprint($prompts{urls_intro}); + # Only prompt for auto-pick if Net::Ping is new enough to do timings + my $_conf = 'n'; + if ( $CPAN::META->has_usable("Net::Ping") && CPAN::Version->vgt(Net::Ping->VERSION, '2.13')) { + $_conf = prompt($prompts{auto_pick}, "yes"); + } else { + prompt("Autoselection disabled due to Net::Ping missing or insufficient. Please press ENTER"); + } + my @old_list = @{ $CPAN::Config->{urllist} }; + if ( $_conf =~ /^y/i ) { + conf_sites( auto_pick => 1 ) or bring_your_own(); + } + else { + _print_urllist('Current') if @old_list; + my $msg = scalar @old_list + ? "\nWould you like to edit the urllist or pick new mirrors from a list?" + : "\nWould you like to pick from the CPAN mirror list?" ; + my $_conf = prompt($msg, "yes"); + if ( $_conf =~ /^y/i ) { + conf_sites(); + } + bring_your_own(); + } + _print_urllist('New'); +} + +sub _init_external_progs { + my($matcher,$args) = @_; + my $PATH = $args->{path}; + my @external_progs = @{ $args->{progs} }; + my $shortcut = $args->{shortcut}; + my $showed_make_warning; + + if (!$matcher or "@external_progs" =~ /$matcher/) { + my $old_warn = $^W; + local $^W if $^O eq 'MacOS'; + local $^W = $old_warn; + my $progname; + for $progname (@external_progs) { + next if $matcher && $progname !~ /$matcher/; + if ($^O eq 'MacOS') { + $CPAN::Config->{$progname} = 'not_here'; + next; + } + + my $progcall = $progname; + unless ($matcher) { + # we really don't need ncftp if we have ncftpget, but + # if they chose this dialog via matcher, they shall have it + next if $progname eq "ncftp" && $CPAN::Config->{ncftpget} gt " "; + } + my $path = $CPAN::Config->{$progname} + || $Config::Config{$progname} + || ""; + if (File::Spec->file_name_is_absolute($path)) { + # testing existence is not good enough, some have these exe + # extensions + + # warn "Warning: configured $path does not exist\n" unless -e $path; + # $path = ""; + } elsif ($path =~ /^\s+$/) { + # preserve disabled programs + } else { + $path = ''; + } + unless ($path) { + # e.g. make -> nmake + $progcall = $Config::Config{$progname} if $Config::Config{$progname}; + } + + $path ||= find_exe($progcall,$PATH); + unless ($path) { # not -e $path, because find_exe already checked that + local $"=";"; + $CPAN::Frontend->mywarn("Warning: $progcall not found in PATH[@$PATH]\n") unless $auto_config; + _beg_for_make(), $showed_make_warning++ if $progname eq "make"; + } + $prompts{$progname} = "Where is your $progname program?"; + $path = my_dflt_prompt($progname,$path,$matcher,1); # 1 => no strip spaces + my $disabling = $path =~ m/^\s*$/; + + # don't let them disable or misconfigure make without warning + if ( $progname eq "make" && ( $disabling || ! _check_found($path) ) ) { + if ( $disabling && $showed_make_warning ) { + next; + } + else { + _beg_for_make() unless $showed_make_warning++; + undef $CPAN::Config->{$progname}; + $CPAN::Frontend->mywarn("Press SPACE and ENTER to disable make (NOT RECOMMENDED)\n"); + redo; + } + } + elsif ( $disabling ) { + next; + } + elsif ( _check_found( $CPAN::Config->{$progname} ) ) { + last if $shortcut && !$matcher; + } + else { + undef $CPAN::Config->{$progname}; + $CPAN::Frontend->mywarn("Press SPACE and ENTER to disable $progname\n"); + redo; + } + } + } +} + +sub _check_found { + my ($prog) = @_; + if ( ! -f $prog ) { + $CPAN::Frontend->mywarn("Warning: '$prog' does not exist\n") + unless $auto_config; + return; + } + elsif ( ! -x $prog ) { + $CPAN::Frontend->mywarn("Warning: '$prog' is not executable\n") + unless $auto_config; + return; + } + return 1; +} + +sub _beg_for_make { + $CPAN::Frontend->mywarn(<<"HERE"); + +ALERT: 'make' is an essential tool for building perl Modules. +Please make sure you have 'make' (or some equivalent) working. + +HERE + if ($^O eq "MSWin32") { + $CPAN::Frontend->mywarn(<<"HERE"); +Windows users may want to follow this procedure when back in the CPAN shell: + + look YVES/scripts/alien_nmake.pl + perl alien_nmake.pl + +This will install nmake on your system which can be used as a 'make' +substitute. You can then revisit this dialog with + + o conf init make + +HERE + } +} + +sub init_cpan_home { + my($matcher) = @_; + if (!$matcher or 'cpan_home' =~ /$matcher/) { + my $cpan_home = + $CPAN::Config->{cpan_home} || CPAN::HandleConfig::cpan_home(); + if (-d $cpan_home) { + $CPAN::Frontend->myprint( + "\nI see you already have a directory\n" . + "\n$cpan_home\n" . + "Shall we use it as the general CPAN build and cache directory?\n\n" + ) unless $auto_config; + } else { + # no cpan-home, must prompt and get one + $CPAN::Frontend->myprint($prompts{cpan_home_where}) unless $auto_config; + } + + my $default = $cpan_home; + my $loop = 0; + my($last_ans,$ans); + $CPAN::Frontend->myprint(" <cpan_home>\n") unless $auto_config; + PROMPT: while ($ans = prompt("CPAN build and cache directory?",$default)) { + if (File::Spec->file_name_is_absolute($ans)) { + my @cpan_home = split /[\/\\]/, $ans; + DIR: for my $dir (@cpan_home) { + if ($dir =~ /^~/ and (!$last_ans or $ans ne $last_ans)) { + $CPAN::Frontend + ->mywarn("Warning: a tilde in the path will be ". + "taken as a literal tilde. Please ". + "confirm again if you want to keep it\n"); + $last_ans = $default = $ans; + next PROMPT; + } + } + } else { + require Cwd; + my $cwd = Cwd::cwd(); + my $absans = File::Spec->catdir($cwd,$ans); + $CPAN::Frontend->mywarn("The path '$ans' is not an ". + "absolute path. Please specify ". + "an absolute path\n"); + $default = $absans; + next PROMPT; + } + eval { File::Path::mkpath($ans); }; # dies if it can't + if ($@) { + $CPAN::Frontend->mywarn("Couldn't create directory $ans.\n". + "Please retry.\n"); + next PROMPT; + } + if (-d $ans && -w _) { + last PROMPT; + } else { + $CPAN::Frontend->mywarn("Couldn't find directory $ans\n". + "or directory is not writable. Please retry.\n"); + if (++$loop > 5) { + $CPAN::Frontend->mydie("Giving up"); + } + } + } + $CPAN::Config->{cpan_home} = $ans; + } +} + +sub my_dflt_prompt { + my ($item, $dflt, $m, $no_strip) = @_; + my $default = $CPAN::Config->{$item} || $dflt; + + if (!$auto_config && (!$m || $item =~ /$m/)) { + if (my $intro = $prompts{$item . "_intro"}) { + $CPAN::Frontend->myprint($intro); + } + $CPAN::Frontend->myprint(" <$item>\n"); + $CPAN::Config->{$item} = + $no_strip ? prompt_no_strip($prompts{$item}, $default) + : prompt( $prompts{$item}, $default); + } else { + $CPAN::Config->{$item} = $default; + } + return $CPAN::Config->{$item}; +} + +sub my_yn_prompt { + my ($item, $dflt, $m) = @_; + my $default; + defined($default = $CPAN::Config->{$item}) or $default = $dflt; + + if (!$auto_config && (!$m || $item =~ /$m/)) { + if (my $intro = $prompts{$item . "_intro"}) { + $CPAN::Frontend->myprint($intro); + } + $CPAN::Frontend->myprint(" <$item>\n"); + my $ans = prompt($prompts{$item}, $default ? 'yes' : 'no'); + $CPAN::Config->{$item} = ($ans =~ /^[y1]/i ? 1 : 0); + } else { + $CPAN::Config->{$item} = $default; + } +} + +sub my_prompt_loop { + my ($item, $dflt, $m, $ok) = @_; + my $default = $CPAN::Config->{$item} || $dflt; + my $ans; + + if (!$auto_config && (!$m || $item =~ /$m/)) { + my $intro = $prompts{$item . "_intro"}; + $CPAN::Frontend->myprint($intro) if defined $intro; + $CPAN::Frontend->myprint(" <$item>\n"); + do { $ans = prompt($prompts{$item}, $default); + } until $ans =~ /$ok/; + $CPAN::Config->{$item} = $ans; + } else { + $CPAN::Config->{$item} = $default; + } +} + + +# Here's the logic about the MIRRORED.BY file. There are a number of scenarios: +# (1) We have a cached MIRRORED.BY file +# (1a) We're auto-picking +# - Refresh it automatically if it's old +# (1b) Otherwise, ask if using cached is ok. If old, default to no. +# - If cached is not ok, get it from the Internet. If it succeeds we use +# the new file. Otherwise, we use the old file. +# (2) We don't have a copy at all +# (2a) If we are allowed to connect, we try to get a new copy. If it succeeds, +# we use it, otherwise, we warn about failure +# (2b) If we aren't allowed to connect, + +sub conf_sites { + my %args = @_; + # auto pick implies using the internet + $CPAN::Config->{connect_to_internet_ok} = 1 if $args{auto_pick}; + + my $m = 'MIRRORED.BY'; + my $mby = File::Spec->catfile($CPAN::Config->{keep_source_where},$m); + File::Path::mkpath(File::Basename::dirname($mby)); + # Why are we using MIRRORED.BY from the current directory? + # Is this for testing? -- dagolden, 2009-11-05 + if (-f $mby && -f $m && -M $m < -M $mby) { + require File::Copy; + File::Copy::copy($m,$mby) or die "Could not update $mby: $!"; + } + local $^T = time; + # if we have a cached copy is not older than 60 days, we either + # use it or refresh it or fall back to it if the refresh failed. + if ($mby && -f $mby && -s _ > 0 ) { + my $very_old = (-M $mby > 60); + my $mtime = localtime((stat _)[9]); + # if auto_pick, refresh anything old automatically + if ( $args{auto_pick} ) { + if ( $very_old ) { + $CPAN::Frontend->myprint(qq{Trying to refresh your mirror list\n}); + eval { CPAN::FTP->localize($m,$mby,3,1) } + or $CPAN::Frontend->myprint(qq{Refresh failed. Using the old cached copy instead.\n}); + $CPAN::Frontend->myprint("\n"); + } + } + else { + my $prompt = qq{Found a cached mirror list as of $mtime + +If you'd like to just use the cached copy, answer 'yes', below. +If you'd like an updated copy of the mirror list, answer 'no' and +I'll get a fresh one from the Internet. + +Shall I use the cached mirror list?}; + my $ans = prompt($prompt, $very_old ? "no" : "yes"); + if ($ans =~ /^n/i) { + $CPAN::Frontend->myprint(qq{Trying to refresh your mirror list\n}); + # you asked for it from the Internet + $CPAN::Config->{connect_to_internet_ok} = 1; + eval { CPAN::FTP->localize($m,$mby,3,1) } + or $CPAN::Frontend->myprint(qq{Refresh failed. Using the old cached copy instead.\n}); + $CPAN::Frontend->myprint("\n"); + } + } + } + # else there is no cached copy and we must fetch or fail + else { + # If they haven't agree to connect to the internet, ask again + if ( ! $CPAN::Config->{connect_to_internet_ok} ) { + my $prompt = q{You are missing a copy of the CPAN mirror list. + +May I connect to the Internet to get it?}; + my $ans = prompt($prompt, "yes"); + if ($ans =~ /^y/i) { + $CPAN::Config->{connect_to_internet_ok} = 1; + } + } + + # Now get it from the Internet or complain + if ( $CPAN::Config->{connect_to_internet_ok} ) { + $CPAN::Frontend->myprint(qq{Trying to fetch a mirror list from the Internet\n}); + eval { CPAN::FTP->localize($m,$mby,3,1) } + or $CPAN::Frontend->mywarn(<<'HERE'); +We failed to get a copy of the mirror list from the Internet. +You will need to provide CPAN mirror URLs yourself. +HERE + $CPAN::Frontend->myprint("\n"); + } + else { + $CPAN::Frontend->mywarn(<<'HERE'); +You will need to provide CPAN mirror URLs yourself or set +'o conf connect_to_internet_ok 1' and try again. +HERE + } + } + + # if we finally have a good local MIRRORED.BY, get on with picking + if (-f $mby && -s _ > 0){ + $CPAN::Config->{urllist} = + $args{auto_pick} ? auto_mirrored_by($mby) : choose_mirrored_by($mby); + return 1; + } + + return; +} + +sub find_exe { + my($exe,$path) = @_; + $path ||= [split /$Config{'path_sep'}/, $ENV{'PATH'}]; + my($dir); + #warn "in find_exe exe[$exe] path[@$path]"; + for $dir (@$path) { + my $abs = File::Spec->catfile($dir,$exe); + if (($abs = MM->maybe_command($abs))) { + return $abs; + } + } +} + +sub picklist { + my($items,$prompt,$default,$require_nonempty,$empty_warning)=@_; + CPAN->debug("picklist('$items','$prompt','$default','$require_nonempty',". + "'$empty_warning')") if $CPAN::DEBUG; + $default ||= ''; + + my $pos = 0; + + my @nums; + SELECTION: while (1) { + + # display, at most, 15 items at a time + my $limit = $#{ $items } - $pos; + $limit = 15 if $limit > 15; + + # show the next $limit items, get the new position + $pos = display_some($items, $limit, $pos, $default); + $pos = 0 if $pos >= @$items; + + my $num = prompt($prompt,$default); + + @nums = split (' ', $num); + { + my %seen; + @nums = grep { !$seen{$_}++ } @nums; + } + my $i = scalar @$items; + unrangify(\@nums); + if (0 == @nums) { + # cannot allow nothing because nothing means paging! + # return; + } elsif (grep (/\D/ || $_ < 1 || $_ > $i, @nums)) { + $CPAN::Frontend->mywarn("invalid items entered, try again\n"); + if ("@nums" =~ /\D/) { + $CPAN::Frontend->mywarn("(we are expecting only numbers between 1 and $i)\n"); + } + next SELECTION; + } + if ($require_nonempty && !@nums) { + $CPAN::Frontend->mywarn("$empty_warning\n"); + } + + # a blank line continues... + unless (@nums){ + $CPAN::Frontend->mysleep(0.1); # prevent hot spinning process on the next bug + next SELECTION; + } + last; + } + for (@nums) { $_-- } + @{$items}[@nums]; +} + +sub unrangify ($) { + my($nums) = $_[0]; + my @nums2 = (); + while (@{$nums||[]}) { + my $n = shift @$nums; + if ($n =~ /^(\d+)-(\d+)$/) { + my @range = $1 .. $2; + # warn "range[@range]"; + push @nums2, @range; + } else { + push @nums2, $n; + } + } + push @$nums, @nums2; +} + +sub display_some { + my ($items, $limit, $pos, $default) = @_; + $pos ||= 0; + + my @displayable = @$items[$pos .. ($pos + $limit)]; + for my $item (@displayable) { + $CPAN::Frontend->myprint(sprintf "(%d) %s\n", ++$pos, $item); + } + my $hit_what = $default ? "SPACE ENTER" : "ENTER"; + $CPAN::Frontend->myprint(sprintf("%d more items, hit %s to show them\n", + (@$items - $pos), + $hit_what, + )) + if $pos < @$items; + return $pos; +} + +sub auto_mirrored_by { + my $local = shift or return; + local $|=1; + $CPAN::Frontend->myprint("Looking for CPAN mirrors near you (please be patient)\n"); + my $mirrors = CPAN::Mirrors->new($local); + + my $cnt = 0; + my $callback_was_active = 0; + my @best = $mirrors->best_mirrors( + how_many => 3, + callback => sub { + $callback_was_active++; + $CPAN::Frontend->myprint("."); + if ($cnt++>60) { $cnt=0; $CPAN::Frontend->myprint("\n"); } + }, + $CPAN::Config->{urllist_ping_external} ? (external_ping => 1) : (), + $CPAN::Config->{urllist_ping_verbose} ? (verbose => 1) : (), + ); + + my $urllist = [ + map { $_->http } + grep { $_ && ref $_ && $_->can('http') } + @best + ]; + push @$urllist, grep { /^file:/ } @{$CPAN::Config->{urllist}}; + $CPAN::Frontend->myprint(" done!\n\n") if $callback_was_active; + + return $urllist +} + +sub choose_mirrored_by { + my $local = shift or return; + my ($default); + my $mirrors = CPAN::Mirrors->new($local); + my @previous_urls = @{$CPAN::Config->{urllist}}; + + $CPAN::Frontend->myprint($prompts{urls_picker_intro}); + + my (@cont, $cont, %cont, @countries, @urls, %seen); + my $no_previous_warn = + "Sorry! since you don't have any existing picks, you must make a\n" . + "geographic selection."; + my $offer_cont = [sort $mirrors->continents]; + if (@previous_urls) { + push @$offer_cont, "(edit previous picks)"; + $default = @$offer_cont; + } else { + # cannot allow nothing because nothing means paging! + # push @$offer_cont, "(none of the above)"; + } + @cont = picklist($offer_cont, + "Select your continent (or several nearby continents)", + $default, + ! @previous_urls, + $no_previous_warn); + # cannot allow nothing because nothing means paging! + # return unless @cont; + + foreach $cont (@cont) { + my @c = sort $mirrors->countries($cont); + @cont{@c} = map ($cont, 0..$#c); + @c = map ("$_ ($cont)", @c) if @cont > 1; + push (@countries, @c); + } + if (@previous_urls && @countries) { + push @countries, "(edit previous picks)"; + $default = @countries; + } + + if (@countries) { + @countries = picklist (\@countries, + "Select your country (or several nearby countries)", + $default, + ! @previous_urls, + $no_previous_warn); + %seen = map (($_ => 1), @previous_urls); + # hmmm, should take list of defaults from CPAN::Config->{'urllist'}... + foreach my $country (@countries) { + next if $country =~ /edit previous picks/; + (my $bare_country = $country) =~ s/ \(.*\)//; + my @u; + for my $m ( $mirrors->mirrors($bare_country) ) { + push @u, $m->ftp if $m->ftp; + push @u, $m->http if $m->http; + } + @u = grep (! $seen{$_}, @u); + @u = map ("$_ ($bare_country)", @u) + if @countries > 1; + push (@urls, sort @u); + } + } + push (@urls, map ("$_ (previous pick)", @previous_urls)); + my $prompt = "Select as many URLs as you like (by number), +put them on one line, separated by blanks, hyphenated ranges allowed + e.g. '1 4 5' or '7 1-4 8'"; + if (@previous_urls) { + $default = join (' ', ((scalar @urls) - (scalar @previous_urls) + 1) .. + (scalar @urls)); + $prompt .= "\n(or just hit ENTER to keep your previous picks)"; + } + + @urls = picklist (\@urls, $prompt, $default); + foreach (@urls) { s/ \(.*\)//; } + return [ @urls ]; +} + +sub bring_your_own { + my $urllist = [ @{$CPAN::Config->{urllist}} ]; + my %seen = map (($_ => 1), @$urllist); + my($ans,@urls); + my $eacnt = 0; # empty answers + $CPAN::Frontend->myprint(<<'HERE'); +Now you can enter your own CPAN URLs by hand. A local CPAN mirror can be +listed using a 'file:' URL like 'file:///path/to/cpan/' + +HERE + do { + my $prompt = "Enter another URL or ENTER to quit:"; + unless (%seen) { + $prompt = qq{CPAN.pm needs at least one URL where it can fetch CPAN files from. + +Please enter your CPAN site:}; + } + $ans = prompt ($prompt, ""); + + if ($ans) { + $ans =~ s|/?\z|/|; # has to end with one slash + # XXX This manipulation is odd. Shouldn't we check that $ans is + # a directory before converting to file:///? And we need /// below, + # too, don't we? -- dagolden, 2009-11-05 + $ans = "file:$ans" unless $ans =~ /:/; # without a scheme is a file: + if ($ans =~ /^\w+:\/./) { + push @urls, $ans unless $seen{$ans}++; + } else { + $CPAN::Frontend-> + myprint(sprintf(qq{"%s" doesn\'t look like an URL at first sight. +I\'ll ignore it for now. +You can add it to your %s +later if you\'re sure it\'s right.\n}, + $ans, + $INC{'CPAN/MyConfig.pm'} + || $INC{'CPAN/Config.pm'} + || "configuration file", + )); + } + } else { + if (++$eacnt >= 5) { + $CPAN::Frontend-> + mywarn("Giving up.\n"); + $CPAN::Frontend->mysleep(5); + return; + } + } + } while $ans || !%seen; + + @$urllist = CPAN::_uniq(@$urllist, @urls); + $CPAN::Config->{urllist} = $urllist; +} + +sub _print_urllist { + my ($which) = @_; + $CPAN::Frontend->myprint("$which urllist\n"); + for ( @{$CPAN::Config->{urllist} || []} ) { + $CPAN::Frontend->myprint(" $_\n") + }; +} + +sub _can_write_to_libdirs { + return -w $Config{installprivlib} + && -w $Config{installarchlib} + && -w $Config{installsitelib} + && -w $Config{installsitearch} +} + +sub _using_installbase { + return 1 if $ENV{PERL_MM_OPT} && $ENV{PERL_MM_OPT} =~ /install_base/i; + return 1 if grep { ($CPAN::Config->{$_}||q{}) =~ /install_base/i } + qw(makepl_arg make_install_arg mbuildpl_arg mbuild_install_arg); + return; +} + +sub _using_sudo { + return 1 if grep { ($CPAN::Config->{$_}||q{}) =~ /sudo/ } + qw(make_install_make_command mbuild_install_build_command); + return; +} + +sub _strip_spaces { + $_[0] =~ s/^\s+//; # no leading spaces + $_[0] =~ s/\s+\z//; # no trailing spaces +} + +sub prompt ($;$) { + unless (defined &_real_prompt) { + *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; + } + my $ans = _real_prompt(@_); + + _strip_spaces($ans); + $CPAN::Frontend->myprint("\n") unless $auto_config; + + return $ans; +} + + +sub prompt_no_strip ($;$) { + unless (defined &_real_prompt) { + *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; + } + return _real_prompt(@_); +} + + + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/HTTP/Client.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/HTTP/Client.pm new file mode 100644 index 0000000000..4fc792c26a --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/HTTP/Client.pm @@ -0,0 +1,254 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::HTTP::Client; +use strict; +use vars qw(@ISA); +use CPAN::HTTP::Credentials; +use HTTP::Tiny 0.005; + +$CPAN::HTTP::Client::VERSION = $CPAN::HTTP::Client::VERSION = "1.9601"; + +# CPAN::HTTP::Client is adapted from parts of cpanm by Tatsuhiko Miyagawa +# and parts of LWP by Gisle Aas + +sub new { + my $class = shift; + my %args = @_; + for my $k ( keys %args ) { + $args{$k} = '' unless defined $args{$k}; + } + $args{no_proxy} = [split(",", $args{no_proxy}) ] if $args{no_proxy}; + return bless \%args, $class; +} + +# This executes a request with redirection (up to 5) and returns the +# response structure generated by HTTP::Tiny +# +# If authentication fails, it will attempt to get new authentication +# information and repeat up to 5 times + +sub mirror { + my($self, $uri, $path) = @_; + + my $want_proxy = $self->_want_proxy($uri); + my $http = HTTP::Tiny->new( + $want_proxy ? (proxy => $self->{proxy}) : () + ); + + my ($response, %headers); + my $retries = 0; + while ( $retries++ < 5 ) { + $response = $http->mirror( $uri, $path, {headers => \%headers} ); + if ( $response->{status} eq '401' ) { + last unless $self->_get_auth_params( $response, 'non_proxy' ); + } + elsif ( $response->{status} eq '407' ) { + last unless $self->_get_auth_params( $response, 'proxy' ); + } + else { + last; # either success or failure + } + my %headers = ( + $self->_auth_headers( $uri, 'non_proxy' ), + ( $want_proxy ? $self->_auth_headers($uri, 'proxy') : () ), + ); + } + + return $response; +} + +sub _want_proxy { + my ($self, $uri) = @_; + return unless $self->{proxy}; + my($host) = $uri =~ m|://([^/:]+)|; + return ! grep { $host =~ /\Q$_\E$/ } @{ $self->{no_proxy} || [] }; +} + +# Generates the authentication headers for a given mode +# C<mode> is 'proxy' or 'non_proxy' +# C<_${mode}_type> is 'basic' or 'digest' +# C<_${mode}_params> will be the challenge parameters from the 401/407 headers +sub _auth_headers { + my ($self, $uri, $mode) = @_; + # Get names for our mode-specific attributes + my ($type_key, $param_key) = map {"_" . $mode . $_} qw/_type _params/; + + # If _prepare_auth has not been called, we can't prepare headers + return unless $self->{$type_key}; + + # Get user credentials for mode + my $cred_method = "get_" . ($mode ? "proxy" : "non_proxy") ."_credentials"; + my ($user, $pass) = CPAN::HTTP::Credentials->$cred_method; + + # Generate the header for the mode & type + my $header = $mode eq 'proxy' ? 'Proxy-Authorization' : 'Authorization'; + my $value_method = "_" . $self->{$type_key} . "_auth"; + my $value = $self->$value_method($user, $pass, $self->{$param_key}, $uri); + + # If we didn't get a value, we didn't have the right modules available + return $value ? ( $header, $value ) : (); +} + +# Extract authentication parameters from headers, but clear any prior +# credentials if we failed (so we might prompt user for password again) +sub _get_auth_params { + my ($self, $response, $mode) = @_; + my $prefix = $mode eq 'proxy' ? 'Proxy' : 'WWW'; + my ($type_key, $param_key) = map {"_" . $mode . $_} qw/_type _params/; + if ( ! $response->{success} ) { # auth failed + my $method = "clear_${mode}_credentials"; + CPAN::HTTP::Credentials->$method; + delete $self->{$_} for $type_key, $param_key; + } + ($self->{$type_key}, $self->{$param_key}) = + $self->_get_challenge( $response, "${prefix}-Authenticate"); + return $self->{$type_key}; +} + +# Extract challenge type and parameters for a challenge list +sub _get_challenge { + my ($self, $response, $auth_header) = @_; + + my $auth_list = $response->{headers}(lc $auth_header); + return unless defined $auth_list; + $auth_list = [$auth_list] unless ref $auth_list; + + for my $challenge (@$auth_list) { + $challenge =~ tr/,/;/; # "," is used to separate auth-params!! + ($challenge) = $self->split_header_words($challenge); + my $scheme = shift(@$challenge); + shift(@$challenge); # no value + $challenge = { @$challenge }; # make rest into a hash + + unless ($scheme =~ /^(basic|digest)$/) { + next; # bad scheme + } + $scheme = $1; # untainted now + + return ($scheme, $challenge); + } + return; +} + +# Generate a basic authentication header value +sub _basic_auth { + my ($self, $user, $pass) = @_; + unless ( $CPAN::META->has_usable('MIME::Base64') ) { + $CPAN::Frontend->mywarn( + "MIME::Base64 is required for 'Basic' style authentication" + ); + return; + } + return "Basic " . MIME::Base64::encode_base64("$user\:$pass", q{}); +} + +# Generate a digest authentication header value +sub _digest_auth { + my ($self, $user, $pass, $auth_param, $uri) = @_; + unless ( $CPAN::META->has_usable('Digest::MD5') ) { + $CPAN::Frontend->mywarn( + "Digest::MD5 is required for 'Digest' style authentication" + ); + return; + } + + my $nc = sprintf "%08X", ++$self->{_nonce_count}{$auth_param->{nonce}}; + my $cnonce = sprintf "%8x", time; + + my ($path) = $uri =~ m{^\w+?://[^/]+(/.*)$}; + $path = "/" unless defined $path; + + my $md5 = Digest::MD5->new; + + my(@digest); + $md5->add(join(":", $user, $auth_param->{realm}, $pass)); + push(@digest, $md5->hexdigest); + $md5->reset; + + push(@digest, $auth_param->{nonce}); + + if ($auth_param->{qop}) { + push(@digest, $nc, $cnonce, ($auth_param->{qop} =~ m|^auth[,;]auth-int$|) ? 'auth' : $auth_param->{qop}); + } + + $md5->add(join(":", 'GET', $path)); + push(@digest, $md5->hexdigest); + $md5->reset; + + $md5->add(join(":", @digest)); + my($digest) = $md5->hexdigest; + $md5->reset; + + my %resp = map { $_ => $auth_param->{$_} } qw(realm nonce opaque); + @resp{qw(username uri response algorithm)} = ($user, $path, $digest, "MD5"); + + if (($auth_param->{qop} || "") =~ m|^auth([,;]auth-int)?$|) { + @resp{qw(qop cnonce nc)} = ("auth", $cnonce, $nc); + } + + my(@order) = + qw(username realm qop algorithm uri nonce nc cnonce response opaque); + my @pairs; + for (@order) { + next unless defined $resp{$_}; + push(@pairs, "$_=" . qq("$resp{$_}")); + } + + my $auth_value = "Digest " . join(", ", @pairs); + return $auth_value; +} + +# split_header_words adapted from HTTP::Headers::Util +sub split_header_words { + my ($self, @words) = @_; + my @res = $self->_split_header_words(@words); + for my $arr (@res) { + for (my $i = @$arr - 2; $i >= 0; $i -= 2) { + $arr->[$i] = lc($arr->[$i]); + } + } + return @res; +} + +sub _split_header_words { + my($self, @val) = @_; + my @res; + for (@val) { + my @cur; + while (length) { + if (s/^\s*(=*[^\s=;,]+)//) { # 'token' or parameter 'attribute' + push(@cur, $1); + # a quoted value + if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) { + my $val = $1; + $val =~ s/\\(.)/$1/g; + push(@cur, $val); + # some unquoted value + } + elsif (s/^\s*=\s*([^;,\s]*)//) { + my $val = $1; + $val =~ s/\s+$//; + push(@cur, $val); + # no value, a lone token + } + else { + push(@cur, undef); + } + } + elsif (s/^\s*,//) { + push(@res, [@cur]) if @cur; + @cur = (); + } + elsif (s/^\s*;// || s/^\s+//) { + # continue + } + else { + die "This should not happen: '$_'"; + } + } + push(@res, \@cur) if @cur; + } + @res; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/HTTP/Credentials.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/HTTP/Credentials.pm new file mode 100644 index 0000000000..96a9880092 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/HTTP/Credentials.pm @@ -0,0 +1,91 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::HTTP::Credentials; +use strict; +use vars qw($USER $PASSWORD $PROXY_USER $PROXY_PASSWORD); + +$CPAN::HTTP::Credentials::VERSION = $CPAN::HTTP::Credentials::VERSION = "1.9601"; + +sub clear_credentials { + clear_non_proxy_credentials(); + clear_proxy_credentials(); +} + +sub clear_non_proxy_credentials { + undef $USER; + undef $PASSWORD; +} + +sub clear_proxy_credentials { + undef $PROXY_USER; + undef $PROXY_PASSWORD; +} + +sub get_proxy_credentials { + my $self = shift; + if ($PROXY_USER && $PROXY_PASSWORD) { + return ($PROXY_USER, $PROXY_PASSWORD); + } + if ( defined $CPAN::Config->{proxy_user} + && $CPAN::Config->{proxy_user} + ) { + $PROXY_USER = $CPAN::Config->{proxy_user}; + $PROXY_PASSWORD = $CPAN::Config->{proxy_pass} || ""; + return ($PROXY_USER, $PROXY_PASSWORD); + } + my $username_prompt = "\nProxy authentication needed! + (Note: to permanently configure username and password run + o conf proxy_user your_username + o conf proxy_pass your_password + )\nUsername:"; + ($PROXY_USER, $PROXY_PASSWORD) = + _get_username_and_password_from_user($username_prompt); + return ($PROXY_USER,$PROXY_PASSWORD); +} + +sub get_non_proxy_credentials { + my $self = shift; + if ($USER && $PASSWORD) { + return ($USER, $PASSWORD); + } + if ( defined $CPAN::Config->{username} ) { + $USER = $CPAN::Config->{username}; + $PASSWORD = $CPAN::Config->{password} || ""; + return ($USER, $PASSWORD); + } + my $username_prompt = "\nAuthentication needed! + (Note: to permanently configure username and password run + o conf username your_username + o conf password your_password + )\nUsername:"; + + ($USER, $PASSWORD) = + _get_username_and_password_from_user($username_prompt); + return ($USER,$PASSWORD); +} + +sub _get_username_and_password_from_user { + my $username_message = shift; + my ($username,$password); + + ExtUtils::MakeMaker->import(qw(prompt)); + $username = prompt($username_message); + if ($CPAN::META->has_inst("Term::ReadKey")) { + Term::ReadKey::ReadMode("noecho"); + } + else { + $CPAN::Frontend->mywarn( + "Warning: Term::ReadKey seems not to be available, your password will be echoed to the terminal!\n" + ); + } + $password = prompt("Password:"); + + if ($CPAN::META->has_inst("Term::ReadKey")) { + Term::ReadKey::ReadMode("restore"); + } + $CPAN::Frontend->myprint("\n\n"); + return ($username,$password); +} + +1; + diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/HandleConfig.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/HandleConfig.pm new file mode 100644 index 0000000000..e24a969c11 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/HandleConfig.pm @@ -0,0 +1,806 @@ +package CPAN::HandleConfig; +use strict; +use vars qw(%can %keys $loading $VERSION); +use File::Path (); +use File::Spec (); +use File::Basename (); +use Carp (); + +=head1 NAME + +CPAN::HandleConfig - internal configuration handling for CPAN.pm + +=cut + +$VERSION = "5.5011"; # see also CPAN::Config::VERSION at end of file + +%can = ( + commit => "Commit changes to disk", + defaults => "Reload defaults from disk", + help => "Short help about 'o conf' usage", + init => "Interactive setting of all options", +); + +# Q: where is the "How do I add a new config option" HOWTO? +# A1: svn diff -r 757:758 # where dagolden added test_report [git e997b71de88f1019a1472fc13cb97b1b7f96610f] +# A2: svn diff -r 985:986 # where andk added yaml_module [git 312b6d9b12b1bdec0b6e282d853482145475021f] +# A3: 1. add new config option to %keys below +# 2. add a Pod description in CPAN::FirstTime; it should include a +# prompt line; see others for examples +# 3. add a "matcher" section in CPAN::FirstTime::init that includes +# a prompt function; see others for examples +# 4. add config option to documentation section in CPAN.pm + +%keys = map { $_ => undef } + ( + "allow_installing_module_downgrades", + "allow_installing_outdated_dists", + "applypatch", + "auto_commit", + "build_cache", + "build_dir", + "build_dir_reuse", + "build_requires_install_policy", + "bzip2", + "cache_metadata", + "check_sigs", + "cleanup_after_install", + "colorize_debug", + "colorize_output", + "colorize_print", + "colorize_warn", + "commandnumber_in_prompt", + "commands_quote", + "connect_to_internet_ok", + "cpan_home", + "curl", + "dontload_hash", # deprecated after 1.83_68 (rev. 581) + "dontload_list", + "ftp", + "ftp_passive", + "ftp_proxy", + "ftpstats_size", + "ftpstats_period", + "getcwd", + "gpg", + "gzip", + "halt_on_failure", + "histfile", + "histsize", + "http_proxy", + "inactivity_timeout", + "index_expire", + "inhibit_startup_message", + "keep_source_where", + "load_module_verbosity", + "lynx", + "make", + "make_arg", + "make_install_arg", + "make_install_make_command", + "makepl_arg", + "mbuild_arg", + "mbuild_install_arg", + "mbuild_install_build_command", + "mbuildpl_arg", + "ncftp", + "ncftpget", + "no_proxy", + "pager", + "password", + "patch", + "patches_dir", + "perl5lib_verbosity", + "plugin_list", + "prefer_external_tar", + "prefer_installer", + "prefs_dir", + "prerequisites_policy", + "proxy_pass", + "proxy_user", + "randomize_urllist", + "recommends_policy", + "scan_cache", + "shell", + "show_unparsable_versions", + "show_upload_date", + "show_zero_versions", + "suggests_policy", + "tar", + "tar_verbosity", + "term_is_latin", + "term_ornaments", + "test_report", + "trust_test_report_history", + "unzip", + "urllist", + "urllist_ping_verbose", + "urllist_ping_external", + "use_prompt_default", + "use_sqlite", + "username", + "version_timeout", + "wait_list", + "wget", + "yaml_load_code", + "yaml_module", + ); + +my %prefssupport = map { $_ => 1 } + ( + "allow_installing_module_downgrades", + "allow_installing_outdated_dists", + "build_requires_install_policy", + "check_sigs", + "make", + "make_install_make_command", + "prefer_installer", + "test_report", + ); + +# returns true on successful action +sub edit { + my($self,@args) = @_; + return unless @args; + CPAN->debug("self[$self]args[".join(" | ",@args)."]"); + my($o,$str,$func,$args,$key_exists); + $o = shift @args; + if($can{$o}) { + my $success = $self->$o(args => \@args); # o conf init => sub init => sub load + unless ($success) { + die "Panic: could not configure CPAN.pm for args [@args]. Giving up."; + } + } else { + CPAN->debug("o[$o]") if $CPAN::DEBUG; + unless (exists $keys{$o}) { + $CPAN::Frontend->mywarn("Warning: unknown configuration variable '$o'\n"); + } + my $changed; + + + # one day I used randomize_urllist for a boolean, so we must + # list them explicitly --ak + if (0) { + } elsif ($o =~ /^(wait_list|urllist|dontload_list|plugin_list)$/) { + + # + # ARRAYS + # + + $func = shift @args; + $func ||= ""; + CPAN->debug("func[$func]args[@args]") if $CPAN::DEBUG; + # Let's avoid eval, it's easier to comprehend without. + if ($func eq "push") { + push @{$CPAN::Config->{$o}}, @args; + $changed = 1; + } elsif ($func eq "pop") { + pop @{$CPAN::Config->{$o}}; + $changed = 1; + } elsif ($func eq "shift") { + shift @{$CPAN::Config->{$o}}; + $changed = 1; + } elsif ($func eq "unshift") { + unshift @{$CPAN::Config->{$o}}, @args; + $changed = 1; + } elsif ($func eq "splice") { + my $offset = shift @args || 0; + my $length = shift @args || 0; + splice @{$CPAN::Config->{$o}}, $offset, $length, @args; # may warn + $changed = 1; + } elsif ($func) { + $CPAN::Config->{$o} = [$func, @args]; + $changed = 1; + } else { + $self->prettyprint($o); + } + if ($changed) { + if ($o eq "urllist") { + # reset the cached values + undef $CPAN::FTP::Thesite; + undef $CPAN::FTP::Themethod; + $CPAN::Index::LAST_TIME = 0; + } elsif ($o eq "dontload_list") { + # empty it, it will be built up again + $CPAN::META->{dontload_hash} = {}; + } + } + } elsif ($o =~ /_hash$/) { + + # + # HASHES + # + + if (@args==1 && $args[0] eq "") { + @args = (); + } elsif (@args % 2) { + push @args, ""; + } + $CPAN::Config->{$o} = { @args }; + $changed = 1; + } else { + + # + # SCALARS + # + + if (defined $args[0]) { + $CPAN::CONFIG_DIRTY = 1; + $CPAN::Config->{$o} = $args[0]; + $changed = 1; + } + $self->prettyprint($o) + if exists $keys{$o} or defined $CPAN::Config->{$o}; + } + if ($changed) { + if ($CPAN::Config->{auto_commit}) { + $self->commit; + } else { + $CPAN::CONFIG_DIRTY = 1; + $CPAN::Frontend->myprint("Please use 'o conf commit' to ". + "make the config permanent!\n\n"); + } + } + } +} + +sub prettyprint { + my($self,$k) = @_; + my $v = $CPAN::Config->{$k}; + if (ref $v) { + my(@report); + if (ref $v eq "ARRAY") { + @report = map {"\t$_ \[$v->[$_]]\n"} 0..$#$v; + } else { + @report = map + { + sprintf "\t%-18s => %s\n", + "[$_]", + defined $v->{$_} ? "[$v->{$_}]" : "undef" + } sort keys %$v; + } + $CPAN::Frontend->myprint( + join( + "", + sprintf( + " %-18s\n", + $k + ), + @report + ) + ); + } elsif (defined $v) { + $CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v); + } else { + $CPAN::Frontend->myprint(sprintf " %-18s undef\n", $k); + } +} + +# generally, this should be called without arguments so that the currently +# loaded config file is where changes are committed. +sub commit { + my($self,@args) = @_; + CPAN->debug("args[@args]") if $CPAN::DEBUG; + if ($CPAN::RUN_DEGRADED) { + $CPAN::Frontend->mydie( + "'o conf commit' disabled in ". + "degraded mode. Maybe try\n". + " !undef \$CPAN::RUN_DEGRADED\n" + ); + } + my ($configpm, $must_reload); + + # XXX does anything do this? can it be simplified? -- dagolden, 2011-01-19 + if (@args) { + if ($args[0] eq "args") { + # we have not signed that contract + } else { + $configpm = $args[0]; + } + } + + # use provided name or the current config or create a new MyConfig + $configpm ||= require_myconfig_or_config() || make_new_config(); + + # commit to MyConfig if we can't write to Config + if ( ! -w $configpm && $configpm =~ m{CPAN/Config\.pm} ) { + my $myconfig = _new_config_name(); + $CPAN::Frontend->mywarn( + "Your $configpm file\n". + "is not writable. I will attempt to write your configuration to\n" . + "$myconfig instead.\n\n" + ); + $configpm = make_new_config(); + $must_reload++; # so it gets loaded as $INC{'CPAN/MyConfig.pm'} + } + + # XXX why not just "-w $configpm"? -- dagolden, 2011-01-19 + my($mode); + if (-f $configpm) { + $mode = (stat $configpm)[2]; + if ($mode && ! -w _) { + _die_cant_write_config($configpm); + } + } + + $self->_write_config_file($configpm); + require_myconfig_or_config() if $must_reload; + + #$mode = 0444 | ( $mode & 0111 ? 0111 : 0 ); + #chmod $mode, $configpm; +###why was that so? $self->defaults; + $CPAN::Frontend->myprint("commit: wrote '$configpm'\n"); + $CPAN::CONFIG_DIRTY = 0; + 1; +} + +sub _write_config_file { + my ($self, $configpm) = @_; + my $msg; + $msg = <<EOF if $configpm =~ m{CPAN/Config\.pm}; + +# This is CPAN.pm's systemwide configuration file. This file provides +# defaults for users, and the values can be changed in a per-user +# configuration file. + +EOF + $msg ||= "\n"; + my($fh) = FileHandle->new; + rename $configpm, "$configpm~" if -f $configpm; + open $fh, ">$configpm" or + $CPAN::Frontend->mydie("Couldn't open >$configpm: $!"); + $fh->print(qq[$msg\$CPAN::Config = \{\n]); + foreach (sort keys %$CPAN::Config) { + unless (exists $keys{$_}) { + # do not drop them: forward compatibility! + $CPAN::Frontend->mywarn("Unknown config variable '$_'\n"); + next; + } + $fh->print( + " '$_' => ", + $self->neatvalue($CPAN::Config->{$_}), + ",\n" + ); + } + $fh->print("};\n1;\n__END__\n"); + close $fh; + + return; +} + + +# stolen from MakeMaker; not taking the original because it is buggy; +# bugreport will have to say: keys of hashes remain unquoted and can +# produce syntax errors +sub neatvalue { + my($self, $v) = @_; + return "undef" unless defined $v; + my($t) = ref $v; + unless ($t) { + $v =~ s/\\/\\\\/g; + return "q[$v]"; + } + if ($t eq 'ARRAY') { + my(@m, @neat); + push @m, "["; + foreach my $elem (@$v) { + push @neat, "q[$elem]"; + } + push @m, join ", ", @neat; + push @m, "]"; + return join "", @m; + } + return "$v" unless $t eq 'HASH'; + my @m; + foreach my $key (sort keys %$v) { + my $val = $v->{$key}; + push(@m,"q[$key]=>".$self->neatvalue($val)) ; + } + return "{ ".join(', ',@m)." }"; +} + +sub defaults { + my($self) = @_; + if ($CPAN::RUN_DEGRADED) { + $CPAN::Frontend->mydie( + "'o conf defaults' disabled in ". + "degraded mode. Maybe try\n". + " !undef \$CPAN::RUN_DEGRADED\n" + ); + } + my $done; + for my $config (qw(CPAN/MyConfig.pm CPAN/Config.pm)) { + if ($INC{$config}) { + CPAN->debug("INC{'$config'}[$INC{$config}]") if $CPAN::DEBUG; + CPAN::Shell->_reload_this($config,{reloforce => 1}); + $CPAN::Frontend->myprint("'$INC{$config}' reread\n"); + last; + } + } + $CPAN::CONFIG_DIRTY = 0; + 1; +} + +=head2 C<< CLASS->safe_quote ITEM >> + +Quotes an item to become safe against spaces +in shell interpolation. An item is enclosed +in double quotes if: + + - the item contains spaces in the middle + - the item does not start with a quote + +This happens to avoid shell interpolation +problems when whitespace is present in +directory names. + +This method uses C<commands_quote> to determine +the correct quote. If C<commands_quote> is +a space, no quoting will take place. + + +if it starts and ends with the same quote character: leave it as it is + +if it contains no whitespace: leave it as it is + +if it contains whitespace, then + +if it contains quotes: better leave it as it is + +else: quote it with the correct quote type for the box we're on + +=cut + +{ + # Instead of patching the guess, set commands_quote + # to the right value + my ($quotes,$use_quote) + = $^O eq 'MSWin32' + ? ('"', '"') + : (q{"'}, "'") + ; + + sub safe_quote { + my ($self, $command) = @_; + # Set up quote/default quote + my $quote = $CPAN::Config->{commands_quote} || $quotes; + + if ($quote ne ' ' + and defined($command ) + and $command =~ /\s/ + and $command !~ /[$quote]/) { + return qq<$use_quote$command$use_quote> + } + return $command; + } +} + +sub init { + my($self,@args) = @_; + CPAN->debug("self[$self]args[".join(",",@args)."]"); + $self->load(do_init => 1, @args); + 1; +} + +# Loads CPAN::MyConfig or fall-back to CPAN::Config. Will not reload a file +# if already loaded. Returns the path to the file %INC or else the empty string +# +# Note -- if CPAN::Config were loaded and CPAN::MyConfig subsequently +# created, calling this again will leave *both* in %INC + +sub require_myconfig_or_config () { + if ( $INC{"CPAN/MyConfig.pm"} || _try_loading("CPAN::MyConfig", cpan_home())) { + return $INC{"CPAN/MyConfig.pm"}; + } + elsif ( $INC{"CPAN/Config.pm"} || _try_loading("CPAN::Config") ) { + return $INC{"CPAN/Config.pm"}; + } + else { + return q{}; + } +} + +# Load a module, but ignore "can't locate..." errors +# Optionally take a list of directories to add to @INC for the load +sub _try_loading { + my ($module, @dirs) = @_; + (my $file = $module) =~ s{::}{/}g; + $file .= ".pm"; + + local @INC = @INC; + for my $dir ( @dirs ) { + if ( -f File::Spec->catfile($dir, $file) ) { + unshift @INC, $dir; + last; + } + } + + eval { require $file }; + my $err_myconfig = $@; + if ($err_myconfig and $err_myconfig !~ m#locate \Q$file\E#) { + die "Error while requiring ${module}:\n$err_myconfig"; + } + return $INC{$file}; +} + +# prioritized list of possible places for finding "CPAN/MyConfig.pm" +sub cpan_home_dir_candidates { + my @dirs; + my $old_v = $CPAN::Config->{load_module_verbosity}; + $CPAN::Config->{load_module_verbosity} = q[none]; + if ($CPAN::META->has_usable('File::HomeDir')) { + if ($^O ne 'darwin') { + push @dirs, File::HomeDir->my_data; + # my_data is ~/Library/Application Support on darwin, + # which causes issues in the toolchain. + } + push @dirs, File::HomeDir->my_home; + } + # Windows might not have HOME, so check it first + push @dirs, $ENV{HOME} if $ENV{HOME}; + # Windows might have these instead + push( @dirs, File::Spec->catpath($ENV{HOMEDRIVE}, $ENV{HOMEPATH}, '') ) + if $ENV{HOMEDRIVE} && $ENV{HOMEPATH}; + push @dirs, $ENV{USERPROFILE} if $ENV{USERPROFILE}; + + $CPAN::Config->{load_module_verbosity} = $old_v; + my $dotcpan = $^O eq 'VMS' ? '_cpan' : '.cpan'; + @dirs = map { File::Spec->catdir($_, $dotcpan) } grep { defined } @dirs; + return wantarray ? @dirs : $dirs[0]; +} + +sub load { + my($self, %args) = @_; + $CPAN::Be_Silent+=0; # protect against 'used only once' + $CPAN::Be_Silent++ if $args{be_silent}; # do not use; planned to be removed in 2011 + my $do_init = delete $args{do_init} || 0; + my $make_myconfig = delete $args{make_myconfig}; + $loading = 0 unless defined $loading; + + my $configpm = require_myconfig_or_config; + my @miss = $self->missing_config_data; + CPAN->debug("do_init[$do_init]loading[$loading]miss[@miss]") if $CPAN::DEBUG; + return unless $do_init || @miss; + + # I'm not how we'd ever wind up in a recursive loop, but I'm leaving + # this here for safety's sake -- dagolden, 2011-01-19 + return if $loading; + local $loading = ($loading||0) + 1; + + # Warn if we have a config file, but things were found missing + if ($configpm && @miss && !$do_init) { + if ($make_myconfig || ( ! -w $configpm && $configpm =~ m{CPAN/Config\.pm})) { + $configpm = make_new_config(); + $CPAN::Frontend->myprint(<<END); +The system CPAN configuration file has provided some default values, +but you need to complete the configuration dialog for CPAN.pm. +Configuration will be written to + <<$configpm>> +END + } + else { + $CPAN::Frontend->myprint(<<END); +Sorry, we have to rerun the configuration dialog for CPAN.pm due to +some missing parameters. Configuration will be written to + <<$configpm>> + +END + } + } + + require CPAN::FirstTime; + return CPAN::FirstTime::init($configpm || make_new_config(), %args); +} + +# Creates a new, empty config file at the preferred location +# Any existing will be renamed with a ".bak" suffix if possible +# If the file cannot be created, an exception is thrown +sub make_new_config { + my $configpm = _new_config_name(); + my $configpmdir = File::Basename::dirname( $configpm ); + File::Path::mkpath($configpmdir) unless -d $configpmdir; + + if ( -w $configpmdir ) { + #_#_# following code dumped core on me with 5.003_11, a.k. + if( -f $configpm ) { + my $configpm_bak = "$configpm.bak"; + unlink $configpm_bak if -f $configpm_bak; + if( rename $configpm, $configpm_bak ) { + $CPAN::Frontend->mywarn(<<END); +Old configuration file $configpm + moved to $configpm_bak +END + } + } + my $fh = FileHandle->new; + if ($fh->open(">$configpm")) { + $fh->print("1;\n"); + return $configpm; + } + } + _die_cant_write_config($configpm); +} + +sub _die_cant_write_config { + my ($configpm) = @_; + $CPAN::Frontend->mydie(<<"END"); +WARNING: CPAN.pm is unable to write a configuration file. You +must be able to create and write to '$configpm'. + +Aborting configuration. +END + +} + +# From candidate directories, we would like (in descending preference order): +# * the one that contains a MyConfig file +# * one that exists (even without MyConfig) +# * the first one on the list +sub cpan_home { + my @dirs = cpan_home_dir_candidates(); + for my $d (@dirs) { + return $d if -f "$d/CPAN/MyConfig.pm"; + } + for my $d (@dirs) { + return $d if -d $d; + } + return $dirs[0]; +} + +sub _new_config_name { + return File::Spec->catfile(cpan_home(), 'CPAN', 'MyConfig.pm'); +} + +# returns mandatory but missing entries in the Config +sub missing_config_data { + my(@miss); + for ( + "auto_commit", + "build_cache", + "build_dir", + "cache_metadata", + "cpan_home", + "ftp_proxy", + #"gzip", + "http_proxy", + "index_expire", + #"inhibit_startup_message", + "keep_source_where", + #"make", + "make_arg", + "make_install_arg", + "makepl_arg", + "mbuild_arg", + "mbuild_install_arg", + ($^O eq "MSWin32" ? "" : "mbuild_install_build_command"), + "mbuildpl_arg", + "no_proxy", + #"pager", + "prerequisites_policy", + "scan_cache", + #"tar", + #"unzip", + "urllist", + ) { + next unless exists $keys{$_}; + push @miss, $_ unless defined $CPAN::Config->{$_}; + } + return @miss; +} + +sub help { + $CPAN::Frontend->myprint(q[ +Known options: + commit commit session changes to disk + defaults reload default config values from disk + help this help + init enter a dialog to set all or a set of parameters + +Edit key values as in the following (the "o" is a literal letter o): + o conf build_cache 15 + o conf build_dir "/foo/bar" + o conf urllist shift + o conf urllist unshift ftp://ftp.foo.bar/ + o conf inhibit_startup_message 1 + +]); + 1; #don't reprint CPAN::Config +} + +sub cpl { + my($word,$line,$pos) = @_; + $word ||= ""; + CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; + my(@words) = split " ", substr($line,0,$pos+1); + if ( + defined($words[2]) + and + $words[2] =~ /list$/ + and + ( + @words == 3 + || + @words == 4 && length($word) + ) + ) { + return grep /^\Q$word\E/, qw(splice shift unshift pop push); + } elsif (defined($words[2]) + and + $words[2] eq "init" + and + ( + @words == 3 + || + @words >= 4 && length($word) + )) { + return sort grep /^\Q$word\E/, keys %keys; + } elsif (@words >= 4) { + return (); + } + my %seen; + my(@o_conf) = sort grep { !$seen{$_}++ } + keys %can, + keys %$CPAN::Config, + keys %keys; + return grep /^\Q$word\E/, @o_conf; +} + +sub prefs_lookup { + my($self,$distro,$what) = @_; + + if ($prefssupport{$what}) { + return $CPAN::Config->{$what} unless + $distro + and $distro->prefs + and $distro->prefs->{cpanconfig} + and defined $distro->prefs->{cpanconfig}{$what}; + return $distro->prefs->{cpanconfig}{$what}; + } else { + $CPAN::Frontend->mywarn("Warning: $what not yet officially ". + "supported for distroprefs, doing a normal lookup\n"); + return $CPAN::Config->{$what}; + } +} + + +{ + package + CPAN::Config; ####::###### #hide from indexer + # note: J. Nick Koston wrote me that they are using + # CPAN::Config->commit although undocumented. I suggested + # CPAN::Shell->o("conf","commit") even when ugly it is at least + # documented + + # that's why I added the CPAN::Config class with autoload and + # deprecated warning + + use strict; + use vars qw($AUTOLOAD $VERSION); + $VERSION = "5.5011"; + + # formerly CPAN::HandleConfig was known as CPAN::Config + sub AUTOLOAD { ## no critic + my $class = shift; # e.g. in dh-make-perl: CPAN::Config + my($l) = $AUTOLOAD; + $CPAN::Frontend->mywarn("Dispatching deprecated method '$l' to CPAN::HandleConfig\n"); + $l =~ s/.*:://; + CPAN::HandleConfig->$l(@_); + } +} + +1; + +__END__ + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut + +# Local Variables: +# mode: cperl +# cperl-indent-level: 4 +# End: +# vim: ts=4 sts=4 sw=4: diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Index.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Index.pm new file mode 100644 index 0000000000..59e75dcaee --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Index.pm @@ -0,0 +1,626 @@ +package CPAN::Index; +use strict; +use vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03 $HAVE_REANIMATED $VERSION); +$VERSION = "2.12"; +@CPAN::Index::ISA = qw(CPAN::Debug); +$LAST_TIME ||= 0; +$DATE_OF_03 ||= 0; +# use constant PROTOCOL => "2.0"; # commented out to avoid warning on upgrade from 1.57 +sub PROTOCOL { 2.0 } + +#-> sub CPAN::Index::force_reload ; +sub force_reload { + my($class) = @_; + $CPAN::Index::LAST_TIME = 0; + $class->reload(1); +} + +my @indexbundle = + ( + { + reader => "rd_authindex", + dir => "authors", + remotefile => '01mailrc.txt.gz', + shortlocalfile => '01mailrc.gz', + }, + { + reader => "rd_modpacks", + dir => "modules", + remotefile => '02packages.details.txt.gz', + shortlocalfile => '02packag.gz', + }, + { + reader => "rd_modlist", + dir => "modules", + remotefile => '03modlist.data.gz', + shortlocalfile => '03mlist.gz', + }, + ); + +#-> sub CPAN::Index::reload ; +sub reload { + my($self,$force) = @_; + my $time = time; + + # XXX check if a newer one is available. (We currently read it + # from time to time) + for ($CPAN::Config->{index_expire}) { + $_ = 0.001 unless $_ && $_ > 0.001; + } + unless (1 || $CPAN::Have_warned->{readmetadatacache}++) { + # debug here when CPAN doesn't seem to read the Metadata + require Carp; + Carp::cluck("META-PROTOCOL[$CPAN::META->{PROTOCOL}]"); + } + unless ($CPAN::META->{PROTOCOL}) { + $self->read_metadata_cache; + $CPAN::META->{PROTOCOL} ||= "1.0"; + } + if ( $CPAN::META->{PROTOCOL} < PROTOCOL ) { + # warn "Setting last_time to 0"; + $LAST_TIME = 0; # No warning necessary + } + if ($LAST_TIME + $CPAN::Config->{index_expire}*86400 > $time + and ! $force) { + # called too often + # CPAN->debug("LAST_TIME[$LAST_TIME]index_expire[$CPAN::Config->{index_expire}]time[$time]"); + } elsif (0) { + # IFF we are developing, it helps to wipe out the memory + # between reloads, otherwise it is not what a user expects. + undef $CPAN::META; # Neue Gruendlichkeit since v1.52(r1.274) + $CPAN::META = CPAN->new; + } else { + my($debug,$t2); + local $LAST_TIME = $time; + local $CPAN::META->{PROTOCOL} = PROTOCOL; + + my $needshort = $^O eq "dos"; + + INX: for my $indexbundle (@indexbundle) { + my $reader = $indexbundle->{reader}; + my $localfile = $needshort ? $indexbundle->{shortlocalfile} : $indexbundle->{remotefile}; + my $localpath = File::Spec->catfile($indexbundle->{dir}, $localfile); + my $remote = join "/", $indexbundle->{dir}, $indexbundle->{remotefile}; + my $localized = $self->reload_x($remote, $localpath, $force); + $self->$reader($localized); # may die but we let the shell catch it + if ($CPAN::DEBUG){ + $t2 = time; + $debug = "timing reading 01[".($t2 - $time)."]"; + $time = $t2; + } + return if $CPAN::Signal; # this is sometimes lengthy + } + $self->write_metadata_cache; + if ($CPAN::DEBUG){ + $t2 = time; + $debug .= "03[".($t2 - $time)."]"; + $time = $t2; + } + CPAN->debug($debug) if $CPAN::DEBUG; + } + if ($CPAN::Config->{build_dir_reuse}) { + $self->reanimate_build_dir; + } + if (CPAN::_sqlite_running()) { + $CPAN::SQLite->reload(time => $time, force => $force) + if not $LAST_TIME; + } + $LAST_TIME = $time; + $CPAN::META->{PROTOCOL} = PROTOCOL; +} + +#-> sub CPAN::Index::reanimate_build_dir ; +sub reanimate_build_dir { + my($self) = @_; + unless ($CPAN::META->has_inst($CPAN::Config->{yaml_module}||"YAML")) { + return; + } + return if $HAVE_REANIMATED++; + my $d = $CPAN::Config->{build_dir}; + my $dh = DirHandle->new; + opendir $dh, $d or return; # does not exist + my $dirent; + my $i = 0; + my $painted = 0; + my $restored = 0; + my $start = CPAN::FTP::_mytime(); + my @candidates = map { $_->[0] } + sort { $b->[1] <=> $a->[1] } + map { [ $_, -M File::Spec->catfile($d,$_) ] } + grep {/(.+)\.yml$/ && -d File::Spec->catfile($d,$1)} readdir $dh; + if ( @candidates ) { + $CPAN::Frontend->myprint + (sprintf("Reading %d yaml file%s from %s/\n", + scalar @candidates, + @candidates==1 ? "" : "s", + $CPAN::Config->{build_dir} + )); + DISTRO: for $i (0..$#candidates) { + my $dirent = $candidates[$i]; + my $y = eval {CPAN->_yaml_loadfile(File::Spec->catfile($d,$dirent))}; + if ($@) { + warn "Error while parsing file '$dirent'; error: '$@'"; + next DISTRO; + } + my $c = $y->[0]; + if ($c && $c->{perl} && $c->{distribution} && CPAN->_perl_fingerprint($c->{perl})) { + my $key = $c->{distribution}{ID}; + for my $k (keys %{$c->{distribution}}) { + if ($c->{distribution}{$k} + && ref $c->{distribution}{$k} + && UNIVERSAL::isa($c->{distribution}{$k},"CPAN::Distrostatus")) { + $c->{distribution}{$k}{COMMANDID} = $i - @candidates; + } + } + + #we tried to restore only if element already + #exists; but then we do not work with metadata + #turned off. + my $do + = $CPAN::META->{readwrite}{'CPAN::Distribution'}{$key} + = $c->{distribution}; + for my $skipper (qw( + badtestcnt + configure_requires_later + configure_requires_later_for + force_update + later + later_for + notest + should_report + sponsored_mods + prefs + negative_prefs_cache + )) { + delete $do->{$skipper}; + } + if ($do->can("tested_ok_but_not_installed")) { + if ($do->tested_ok_but_not_installed) { + $CPAN::META->is_tested($do->{build_dir},$do->{make_test}{TIME}); + } else { + next DISTRO; + } + } + $restored++; + } + $i++; + while (($painted/76) < ($i/@candidates)) { + $CPAN::Frontend->myprint("."); + $painted++; + } + } + } + else { + $CPAN::Frontend->myprint("Build_dir empty, nothing to restore\n"); + } + my $took = CPAN::FTP::_mytime() - $start; + $CPAN::Frontend->myprint(sprintf( + "DONE\nRestored the state of %s (in %.4f secs)\n", + $restored || "none", + $took, + )); +} + + +#-> sub CPAN::Index::reload_x ; +sub reload_x { + my($cl,$wanted,$localname,$force) = @_; + $force |= 2; # means we're dealing with an index here + CPAN::HandleConfig->load; # we should guarantee loading wherever + # we rely on Config XXX + $localname ||= $wanted; + my $abs_wanted = File::Spec->catfile($CPAN::Config->{'keep_source_where'}, + $localname); + if ( + -f $abs_wanted && + -M $abs_wanted < $CPAN::Config->{'index_expire'} && + !($force & 1) + ) { + my $s = $CPAN::Config->{'index_expire'} == 1 ? "" : "s"; + $cl->debug(qq{$abs_wanted younger than $CPAN::Config->{'index_expire'} }. + qq{day$s. I\'ll use that.}); + return $abs_wanted; + } else { + $force |= 1; # means we're quite serious about it. + } + return CPAN::FTP->localize($wanted,$abs_wanted,$force); +} + +#-> sub CPAN::Index::rd_authindex ; +sub rd_authindex { + my($cl, $index_target) = @_; + return unless defined $index_target; + return if CPAN::_sqlite_running(); + my @lines; + $CPAN::Frontend->myprint("Reading '$index_target'\n"); + local(*FH); + tie *FH, 'CPAN::Tarzip', $index_target; + local($/) = "\n"; + local($_); + push @lines, split /\012/ while <FH>; + my $i = 0; + my $painted = 0; + foreach (@lines) { + my($userid,$fullname,$email) = + m/alias\s+(\S+)\s+\"([^\"\<]*)\s+\<(.*)\>\"/; + $fullname ||= $email; + if ($userid && $fullname && $email) { + my $userobj = $CPAN::META->instance('CPAN::Author',$userid); + $userobj->set('FULLNAME' => $fullname, 'EMAIL' => $email); + } else { + CPAN->debug(sprintf "line[%s]", $_) if $CPAN::DEBUG; + } + $i++; + while (($painted/76) < ($i/@lines)) { + $CPAN::Frontend->myprint("."); + $painted++; + } + return if $CPAN::Signal; + } + $CPAN::Frontend->myprint("DONE\n"); +} + +sub userid { + my($self,$dist) = @_; + $dist = $self->{'id'} unless defined $dist; + my($ret) = $dist =~ m|(?:\w/\w\w/)?([^/]+)/|; + $ret; +} + +#-> sub CPAN::Index::rd_modpacks ; +sub rd_modpacks { + my($self, $index_target) = @_; + return unless defined $index_target; + return if CPAN::_sqlite_running(); + $CPAN::Frontend->myprint("Reading '$index_target'\n"); + my $fh = CPAN::Tarzip->TIEHANDLE($index_target); + local $_; + CPAN->debug(sprintf "start[%d]", time) if $CPAN::DEBUG; + my $slurp = ""; + my $chunk; + while (my $bytes = $fh->READ(\$chunk,8192)) { + $slurp.=$chunk; + } + my @lines = split /\012/, $slurp; + CPAN->debug(sprintf "end[%d]", time) if $CPAN::DEBUG; + undef $fh; + # read header + my($line_count,$last_updated); + while (@lines) { + my $shift = shift(@lines); + last if $shift =~ /^\s*$/; + $shift =~ /^Line-Count:\s+(\d+)/ and $line_count = $1; + $shift =~ /^Last-Updated:\s+(.+)/ and $last_updated = $1; + } + CPAN->debug("line_count[$line_count]last_updated[$last_updated]") if $CPAN::DEBUG; + my $errors = 0; + if (not defined $line_count) { + + $CPAN::Frontend->mywarn(qq{Warning: Your $index_target does not contain a Line-Count header. +Please check the validity of the index file by comparing it to more +than one CPAN mirror. I'll continue but problems seem likely to +happen.\a +}); + $errors++; + $CPAN::Frontend->mysleep(5); + } elsif ($line_count != scalar @lines) { + + $CPAN::Frontend->mywarn(sprintf qq{Warning: Your %s +contains a Line-Count header of %d but I see %d lines there. Please +check the validity of the index file by comparing it to more than one +CPAN mirror. I'll continue but problems seem likely to happen.\a\n}, +$index_target, $line_count, scalar(@lines)); + + } + if (not defined $last_updated) { + + $CPAN::Frontend->mywarn(qq{Warning: Your $index_target does not contain a Last-Updated header. +Please check the validity of the index file by comparing it to more +than one CPAN mirror. I'll continue but problems seem likely to +happen.\a +}); + $errors++; + $CPAN::Frontend->mysleep(5); + } else { + + $CPAN::Frontend + ->myprint(sprintf qq{ Database was generated on %s\n}, + $last_updated); + $DATE_OF_02 = $last_updated; + + my $age = time; + if ($CPAN::META->has_inst('HTTP::Date')) { + require HTTP::Date; + $age -= HTTP::Date::str2time($last_updated); + } else { + $CPAN::Frontend->mywarn(" HTTP::Date not available\n"); + require Time::Local; + my(@d) = $last_updated =~ / (\d+) (\w+) (\d+) (\d+):(\d+):(\d+) /; + $d[1] = index("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", $d[1])/4; + $age -= $d[1]>=0 ? Time::Local::timegm(@d[5,4,3,0,1,2]) : 0; + } + $age /= 3600*24; + if ($age > 30) { + + $CPAN::Frontend + ->mywarn(sprintf + qq{Warning: This index file is %d days old. + Please check the host you chose as your CPAN mirror for staleness. + I'll continue but problems seem likely to happen.\a\n}, + $age); + + } elsif ($age < -1) { + + $CPAN::Frontend + ->mywarn(sprintf + qq{Warning: Your system date is %d days behind this index file! + System time: %s + Timestamp index file: %s + Please fix your system time, problems with the make command expected.\n}, + -$age, + scalar gmtime, + $DATE_OF_02, + ); + + } + } + + + # A necessity since we have metadata_cache: delete what isn't + # there anymore + my $secondtime = $CPAN::META->exists("CPAN::Module","CPAN"); + CPAN->debug("secondtime[$secondtime]") if $CPAN::DEBUG; + my(%exists); + my $i = 0; + my $painted = 0; + LINE: foreach (@lines) { + # before 1.56 we split into 3 and discarded the rest. From + # 1.57 we assign remaining text to $comment thus allowing to + # influence isa_perl + my($mod,$version,$dist,$comment) = split " ", $_, 4; + unless ($mod && defined $version && $dist) { + require Dumpvalue; + my $dv = Dumpvalue->new(tick => '"'); + $CPAN::Frontend->mywarn(sprintf "Could not split line[%s]\n", $dv->stringify($_)); + if ($errors++ >= 5){ + $CPAN::Frontend->mydie("Giving up parsing your $index_target, too many errors"); + } + next LINE; + } + my($bundle,$id,$userid); + + if ($mod eq 'CPAN' && + ! ( + CPAN::Queue->exists('Bundle::CPAN') || + CPAN::Queue->exists('CPAN') + ) + ) { + local($^W)= 0; + if ($version > $CPAN::VERSION) { + $CPAN::Frontend->mywarn(qq{ + New CPAN.pm version (v$version) available. + [Currently running version is v$CPAN::VERSION] + You might want to try + install CPAN + reload cpan + to both upgrade CPAN.pm and run the new version without leaving + the current session. + +}); #}); + $CPAN::Frontend->mysleep(2); + $CPAN::Frontend->myprint(qq{\n}); + } + last if $CPAN::Signal; + } elsif ($mod =~ /^Bundle::(.*)/) { + $bundle = $1; + } + + if ($bundle) { + $id = $CPAN::META->instance('CPAN::Bundle',$mod); + # Let's make it a module too, because bundles have so much + # in common with modules. + + # Changed in 1.57_63: seems like memory bloat now without + # any value, so commented out + + # $CPAN::META->instance('CPAN::Module',$mod); + + } else { + + # instantiate a module object + $id = $CPAN::META->instance('CPAN::Module',$mod); + + } + + # Although CPAN prohibits same name with different version the + # indexer may have changed the version for the same distro + # since the last time ("Force Reindexing" feature) + if ($id->cpan_file ne $dist + || + $id->cpan_version ne $version + ) { + $userid = $id->userid || $self->userid($dist); + $id->set( + 'CPAN_USERID' => $userid, + 'CPAN_VERSION' => $version, + 'CPAN_FILE' => $dist, + ); + } + + # instantiate a distribution object + if ($CPAN::META->exists('CPAN::Distribution',$dist)) { + # we do not need CONTAINSMODS unless we do something with + # this dist, so we better produce it on demand. + + ## my $obj = $CPAN::META->instance( + ## 'CPAN::Distribution' => $dist + ## ); + ## $obj->{CONTAINSMODS}{$mod} = undef; # experimental + } else { + $CPAN::META->instance( + 'CPAN::Distribution' => $dist + )->set( + 'CPAN_USERID' => $userid, + 'CPAN_COMMENT' => $comment, + ); + } + if ($secondtime) { + for my $name ($mod,$dist) { + # $self->debug("exists name[$name]") if $CPAN::DEBUG; + $exists{$name} = undef; + } + } + $i++; + while (($painted/76) < ($i/@lines)) { + $CPAN::Frontend->myprint("."); + $painted++; + } + return if $CPAN::Signal; + } + $CPAN::Frontend->myprint("DONE\n"); + if ($secondtime) { + for my $class (qw(CPAN::Module CPAN::Bundle CPAN::Distribution)) { + for my $o ($CPAN::META->all_objects($class)) { + next if exists $exists{$o->{ID}}; + $CPAN::META->delete($class,$o->{ID}); + # CPAN->debug("deleting ID[$o->{ID}] in class[$class]") + # if $CPAN::DEBUG; + } + } + } +} + +#-> sub CPAN::Index::rd_modlist ; +sub rd_modlist { + my($cl,$index_target) = @_; + return unless defined $index_target; + return if CPAN::_sqlite_running(); + $CPAN::Frontend->myprint("Reading '$index_target'\n"); + my $fh = CPAN::Tarzip->TIEHANDLE($index_target); + local $_; + my $slurp = ""; + my $chunk; + while (my $bytes = $fh->READ(\$chunk,8192)) { + $slurp.=$chunk; + } + my @eval2 = split /\012/, $slurp; + + while (@eval2) { + my $shift = shift(@eval2); + if ($shift =~ /^Date:\s+(.*)/) { + if ($DATE_OF_03 eq $1) { + $CPAN::Frontend->myprint("Unchanged.\n"); + return; + } + ($DATE_OF_03) = $1; + } + last if $shift =~ /^\s*$/; + } + push @eval2, q{CPAN::Modulelist->data;}; + local($^W) = 0; + my($compmt) = Safe->new("CPAN::Safe1"); + my($eval2) = join("\n", @eval2); + CPAN->debug(sprintf "length of eval2[%d]", length $eval2) if $CPAN::DEBUG; + my $ret = $compmt->reval($eval2); + Carp::confess($@) if $@; + return if $CPAN::Signal; + my $i = 0; + my $until = keys(%$ret); + my $painted = 0; + CPAN->debug(sprintf "until[%d]", $until) if $CPAN::DEBUG; + for (sort keys %$ret) { + my $obj = $CPAN::META->instance("CPAN::Module",$_); + delete $ret->{$_}{modid}; # not needed here, maybe elsewhere + $obj->set(%{$ret->{$_}}); + $i++; + while (($painted/76) < ($i/$until)) { + $CPAN::Frontend->myprint("."); + $painted++; + } + return if $CPAN::Signal; + } + $CPAN::Frontend->myprint("DONE\n"); +} + +#-> sub CPAN::Index::write_metadata_cache ; +sub write_metadata_cache { + my($self) = @_; + return unless $CPAN::Config->{'cache_metadata'}; + return if CPAN::_sqlite_running(); + return unless $CPAN::META->has_usable("Storable"); + my $cache; + foreach my $k (qw(CPAN::Bundle CPAN::Author CPAN::Module + CPAN::Distribution)) { + $cache->{$k} = $CPAN::META->{readonly}{$k}; # unsafe meta access, ok + } + my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata"); + $cache->{last_time} = $LAST_TIME; + $cache->{DATE_OF_02} = $DATE_OF_02; + $cache->{PROTOCOL} = PROTOCOL; + $CPAN::Frontend->myprint("Writing $metadata_file\n"); + eval { Storable::nstore($cache, $metadata_file) }; + $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ?? +} + +#-> sub CPAN::Index::read_metadata_cache ; +sub read_metadata_cache { + my($self) = @_; + return unless $CPAN::Config->{'cache_metadata'}; + return if CPAN::_sqlite_running(); + return unless $CPAN::META->has_usable("Storable"); + my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata"); + return unless -r $metadata_file and -f $metadata_file; + $CPAN::Frontend->myprint("Reading '$metadata_file'\n"); + my $cache; + eval { $cache = Storable::retrieve($metadata_file) }; + $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ?? + if (!$cache || !UNIVERSAL::isa($cache, 'HASH')) { + $LAST_TIME = 0; + return; + } + if (exists $cache->{PROTOCOL}) { + if (PROTOCOL > $cache->{PROTOCOL}) { + $CPAN::Frontend->mywarn(sprintf("Ignoring Metadata cache written ". + "with protocol v%s, requiring v%s\n", + $cache->{PROTOCOL}, + PROTOCOL) + ); + return; + } + } else { + $CPAN::Frontend->mywarn("Ignoring Metadata cache written ". + "with protocol v1.0\n"); + return; + } + my $clcnt = 0; + my $idcnt = 0; + while(my($class,$v) = each %$cache) { + next unless $class =~ /^CPAN::/; + $CPAN::META->{readonly}{$class} = $v; # unsafe meta access, ok + while (my($id,$ro) = each %$v) { + $CPAN::META->{readwrite}{$class}{$id} ||= + $class->new(ID=>$id, RO=>$ro); + $idcnt++; + } + $clcnt++; + } + unless ($clcnt) { # sanity check + $CPAN::Frontend->myprint("Warning: Found no data in $metadata_file\n"); + return; + } + if ($idcnt < 1000) { + $CPAN::Frontend->myprint("Warning: Found only $idcnt objects ". + "in $metadata_file\n"); + return; + } + $CPAN::META->{PROTOCOL} ||= + $cache->{PROTOCOL}; # reading does not up or downgrade, but it + # does initialize to some protocol + $LAST_TIME = $cache->{last_time}; + $DATE_OF_02 = $cache->{DATE_OF_02}; + $CPAN::Frontend->myprint(" Database was generated on $DATE_OF_02\n") + if defined $DATE_OF_02; # An old cache may not contain DATE_OF_02 + return; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/InfoObj.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/InfoObj.pm new file mode 100644 index 0000000000..9198316c69 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/InfoObj.pm @@ -0,0 +1,224 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::InfoObj; +use strict; + +use CPAN::Debug; +@CPAN::InfoObj::ISA = qw(CPAN::Debug); + +use Cwd qw(chdir); + +use vars qw( + $VERSION +); +$VERSION = "5.5"; + +sub ro { + my $self = shift; + exists $self->{RO} and return $self->{RO}; +} + +#-> sub CPAN::InfoObj::cpan_userid +sub cpan_userid { + my $self = shift; + my $ro = $self->ro; + if ($ro) { + return $ro->{CPAN_USERID} || "N/A"; + } else { + $self->debug("ID[$self->{ID}]"); + # N/A for bundles found locally + return "N/A"; + } +} + +sub id { shift->{ID}; } + +#-> sub CPAN::InfoObj::new ; +sub new { + my $this = bless {}, shift; + %$this = @_; + $this +} + +# The set method may only be used by code that reads index data or +# otherwise "objective" data from the outside world. All session +# related material may do anything else with instance variables but +# must not touch the hash under the RO attribute. The reason is that +# the RO hash gets written to Metadata file and is thus persistent. + +#-> sub CPAN::InfoObj::safe_chdir ; +sub safe_chdir { + my($self,$todir) = @_; + # we die if we cannot chdir and we are debuggable + Carp::confess("safe_chdir called without todir argument") + unless defined $todir and length $todir; + if (chdir $todir) { + $self->debug(sprintf "changed directory to %s", CPAN::anycwd()) + if $CPAN::DEBUG; + } else { + if (-e $todir) { + unless (-x $todir) { + unless (chmod 0755, $todir) { + my $cwd = CPAN::anycwd(); + $CPAN::Frontend->mywarn("I have neither the -x permission nor the ". + "permission to change the permission; cannot ". + "chdir to '$todir'\n"); + $CPAN::Frontend->mysleep(5); + $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] }. + qq{to todir[$todir]: $!}); + } + } + } else { + $CPAN::Frontend->mydie("Directory '$todir' has gone. Cannot continue.\n"); + } + if (chdir $todir) { + $self->debug(sprintf "changed directory to %s", CPAN::anycwd()) + if $CPAN::DEBUG; + } else { + my $cwd = CPAN::anycwd(); + $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] }. + qq{to todir[$todir] (a chmod has been issued): $!}); + } + } +} + +#-> sub CPAN::InfoObj::set ; +sub set { + my($self,%att) = @_; + my $class = ref $self; + + # This must be ||=, not ||, because only if we write an empty + # reference, only then the set method will write into the readonly + # area. But for Distributions that spring into existence, maybe + # because of a typo, we do not like it that they are written into + # the readonly area and made permanent (at least for a while) and + # that is why we do not "allow" other places to call ->set. + unless ($self->id) { + CPAN->debug("Bug? Empty ID, rejecting"); + return; + } + my $ro = $self->{RO} = + $CPAN::META->{readonly}{$class}{$self->id} ||= {}; + + while (my($k,$v) = each %att) { + $ro->{$k} = $v; + } +} + +#-> sub CPAN::InfoObj::as_glimpse ; +sub as_glimpse { + my($self) = @_; + my(@m); + my $class = ref($self); + $class =~ s/^CPAN:://; + my $id = $self->can("pretty_id") ? $self->pretty_id : $self->{ID}; + push @m, sprintf "%-15s %s\n", $class, $id; + join "", @m; +} + +#-> sub CPAN::InfoObj::as_string ; +sub as_string { + my($self) = @_; + my(@m); + my $class = ref($self); + $class =~ s/^CPAN:://; + push @m, $class, " id = $self->{ID}\n"; + my $ro; + unless ($ro = $self->ro) { + if (substr($self->{ID},-1,1) eq ".") { # directory + $ro = +{}; + } else { + $CPAN::Frontend->mywarn("Unknown object $self->{ID}\n"); + $CPAN::Frontend->mysleep(5); + return; + } + } + for (sort keys %$ro) { + # next if m/^(ID|RO)$/; + my $extra = ""; + if ($_ eq "CPAN_USERID") { + $extra .= " ("; + $extra .= $self->fullname; + my $email; # old perls! + if ($email = $CPAN::META->instance("CPAN::Author", + $self->cpan_userid + )->email) { + $extra .= " <$email>"; + } else { + $extra .= " <no email>"; + } + $extra .= ")"; + } elsif ($_ eq "FULLNAME") { # potential UTF-8 conversion + push @m, sprintf " %-12s %s\n", $_, $self->fullname; + next; + } + next unless defined $ro->{$_}; + push @m, sprintf " %-12s %s%s\n", $_, $ro->{$_}, $extra; + } + KEY: for (sort keys %$self) { + next if m/^(ID|RO)$/; + unless (defined $self->{$_}) { + delete $self->{$_}; + next KEY; + } + if (ref($self->{$_}) eq "ARRAY") { + push @m, sprintf " %-12s %s\n", $_, "@{$self->{$_}}"; + } elsif (ref($self->{$_}) eq "HASH") { + my $value; + if (/^CONTAINSMODS$/) { + $value = join(" ",sort keys %{$self->{$_}}); + } elsif (/^prereq_pm$/) { + my @value; + my $v = $self->{$_}; + for my $x (sort keys %$v) { + my @svalue; + for my $y (sort keys %{$v->{$x}}) { + push @svalue, "$y=>$v->{$x}{$y}"; + } + push @value, "$x\:" . join ",", @svalue if @svalue; + } + $value = join ";", @value; + } else { + $value = $self->{$_}; + } + push @m, sprintf( + " %-12s %s\n", + $_, + $value, + ); + } else { + push @m, sprintf " %-12s %s\n", $_, $self->{$_}; + } + } + join "", @m, "\n"; +} + +#-> sub CPAN::InfoObj::fullname ; +sub fullname { + my($self) = @_; + $CPAN::META->instance("CPAN::Author",$self->cpan_userid)->fullname; +} + +#-> sub CPAN::InfoObj::dump ; +sub dump { + my($self, $what) = @_; + unless ($CPAN::META->has_inst("Data::Dumper")) { + $CPAN::Frontend->mydie("dump command requires Data::Dumper installed"); + } + local $Data::Dumper::Sortkeys; + $Data::Dumper::Sortkeys = 1; + my $out = Data::Dumper::Dumper($what ? eval $what : $self); + if (length $out > 100000) { + my $fh_pager = FileHandle->new; + local($SIG{PIPE}) = "IGNORE"; + my $pager = $CPAN::Config->{'pager'} || "cat"; + $fh_pager->open("|$pager") + or die "Could not open pager $pager\: $!"; + $fh_pager->print($out); + close $fh_pager; + } else { + $CPAN::Frontend->myprint($out); + } +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify.pm new file mode 100644 index 0000000000..3cade90b91 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify.pm @@ -0,0 +1,136 @@ +=head1 NAME + +CPAN::Kwalify - Interface between CPAN.pm and Kwalify.pm + +=head1 SYNOPSIS + + use CPAN::Kwalify; + validate($schema_name, $data, $file, $doc); + +=head1 DESCRIPTION + +=over + +=item _validate($schema_name, $data, $file, $doc) + +$schema_name is the name of a supported schema. Currently only +C<distroprefs> is supported. $data is the data to be validated. $file +is the absolute path to the file the data are coming from. $doc is the +index of the document within $doc that is to be validated. The last +two arguments are only there for better error reporting. + +Relies on being called from within CPAN.pm. + +Dies if something fails. Does not return anything useful. + +=item yaml($schema_name) + +Returns the YAML text of that schema. Dies if something fails. + +=back + +=head1 AUTHOR + +Andreas Koenig C<< <andk@cpan.org> >> + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +See L<http://www.perl.com/perl/misc/Artistic.html> + + + +=cut + + +use strict; + +package CPAN::Kwalify; +use vars qw($VERSION $VAR1); +$VERSION = "5.50"; + +use File::Spec (); + +my %vcache = (); + +my $schema_loaded = {}; + +sub _validate { + my($schema_name,$data,$abs,$y) = @_; + my $yaml_module = CPAN->_yaml_module; + if ( + $CPAN::META->has_inst($yaml_module) + && + $CPAN::META->has_inst("Kwalify") + ) { + my $load = UNIVERSAL::can($yaml_module,"Load"); + unless ($schema_loaded->{$schema_name}) { + eval { + my $schema_yaml = yaml($schema_name); + $schema_loaded->{$schema_name} = $load->($schema_yaml); + }; + if ($@) { + # we know that YAML.pm 0.62 cannot parse the schema, + # so we try a fallback + my $content = do { + my $path = __FILE__; + $path =~ s/\.pm$//; + $path = File::Spec->catfile($path, "$schema_name.dd"); + local *FH; + open FH, $path or die "Could not open '$path': $!"; + local $/; + <FH>; + }; + $VAR1 = undef; + eval $content; + if (my $err = $@) { + die "parsing of '$schema_name.dd' failed: $err"; + } + $schema_loaded->{$schema_name} = $VAR1; + } + } + } + if (my $schema = $schema_loaded->{$schema_name}) { + my $mtime = (stat $abs)[9]; + for my $k (keys %{$vcache{$abs}}) { + delete $vcache{$abs}{$k} unless $k eq $mtime; + } + return if $vcache{$abs}{$mtime}{$y}++; + eval { Kwalify::validate($schema, $data) }; + if (my $err = $@) { + my $info = {}; yaml($schema_name, info => $info); + die "validation of distropref '$abs'[$y] against schema '$info->{path}' failed: $err"; + } + } +} + +sub _clear_cache { + %vcache = (); +} + +sub yaml { + my($schema_name, %opt) = @_; + my $content = do { + my $path = __FILE__; + $path =~ s/\.pm$//; + $path = File::Spec->catfile($path, "$schema_name.yml"); + if ($opt{info}) { + $opt{info}{path} = $path; + } + local *FH; + open FH, $path or die "Could not open '$path': $!"; + local $/; + <FH>; + }; + return $content; +} + +1; + +# Local Variables: +# mode: cperl +# cperl-indent-level: 4 +# End: + diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.dd b/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.dd new file mode 100644 index 0000000000..fd046271b8 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.dd @@ -0,0 +1,150 @@ +$VAR1 = { + "mapping" => { + "comment" => { + "type" => "text" + }, + "cpanconfig" => { + "mapping" => { + "=" => { + "type" => "text" + } + }, + "type" => "map" + }, + "depends" => { + "mapping" => { + "build_requires" => { + "mapping" => { + "=" => { + "type" => "text" + } + }, + "type" => "map" + }, + "configure_requires" => {}, + "requires" => {} + }, + "type" => "map" + }, + "disabled" => { + "enum" => [ + 0, + 1 + ], + "type" => "int" + }, + "features" => { + "sequence" => [ + { + "type" => "text" + } + ], + "type" => "seq" + }, + "goto" => { + "type" => "text" + }, + "install" => { + "mapping" => { + "args" => { + "sequence" => [ + { + "type" => "text" + } + ], + "type" => "seq" + }, + "commandline" => { + "type" => "text" + }, + "eexpect" => { + "mapping" => { + "mode" => { + "enum" => [ + "deterministic", + "anyorder" + ], + "type" => "text" + }, + "reuse" => { + "type" => "int" + }, + "talk" => { + "sequence" => [ + { + "type" => "text" + } + ], + "type" => "seq" + }, + "timeout" => { + "type" => "number" + } + }, + "type" => "map" + }, + "env" => { + "mapping" => { + "=" => { + "type" => "text" + } + }, + "type" => "map" + }, + "expect" => { + "sequence" => [ + { + "type" => "text" + } + ], + "type" => "seq" + } + }, + "type" => "map" + }, + "make" => {}, + "match" => { + "mapping" => { + "distribution" => { + "type" => "text" + }, + "env" => { + "mapping" => { + "=" => { + "type" => "text" + } + }, + "type" => "map" + }, + "module" => { + "type" => "text" + }, + "perl" => { + "type" => "text" + }, + "perlconfig" => {} + }, + "type" => "map" + }, + "patches" => { + "sequence" => [ + { + "type" => "text" + } + ], + "type" => "seq" + }, + "pl" => {}, + "reminder" => { + "type" => "text" + }, + "test" => {} + }, + "type" => "map" +}; +$VAR1->{"mapping"}{"depends"}{"mapping"}{"configure_requires"} = $VAR1->{"mapping"}{"depends"}{"mapping"}{"build_requires"}; +$VAR1->{"mapping"}{"depends"}{"mapping"}{"requires"} = $VAR1->{"mapping"}{"depends"}{"mapping"}{"build_requires"}; +$VAR1->{"mapping"}{"make"} = $VAR1->{"mapping"}{"install"}; +$VAR1->{"mapping"}{"match"}{"mapping"}{"perlconfig"} = $VAR1->{"mapping"}{"match"}{"mapping"}{"env"}; +$VAR1->{"mapping"}{"pl"} = $VAR1->{"mapping"}{"install"}; +$VAR1->{"mapping"}{"test"} = $VAR1->{"mapping"}{"install"}; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.yml b/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.yml new file mode 100644 index 0000000000..431f174276 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Kwalify/distroprefs.yml @@ -0,0 +1,92 @@ +--- +type: map +mapping: + comment: + type: text + depends: + type: map + mapping: + configure_requires: + &requires_common + type: map + mapping: + =: + type: text + build_requires: *requires_common + requires: *requires_common + match: + type: map + mapping: + distribution: + type: text + module: + type: text + perl: + type: text + perlconfig: + &matchhash_common + type: map + mapping: + =: + type: text + env: *matchhash_common + install: + &args_env_expect + type: map + mapping: + args: + type: seq + sequence: + - type: text + commandline: + type: text + env: + type: map + mapping: + =: + type: text + expect: + type: seq + sequence: + - type: text + eexpect: + type: map + mapping: + mode: + type: text + enum: + - deterministic + - anyorder + timeout: + type: number + reuse: + type: int + talk: + type: seq + sequence: + - type: text + make: *args_env_expect + pl: *args_env_expect + test: *args_env_expect + patches: + type: seq + sequence: + - type: text + disabled: + type: int + enum: + - 0 + - 1 + goto: + type: text + cpanconfig: + type: map + mapping: + =: + type: text + features: + type: seq + sequence: + - type: text + reminder: + type: text diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/LWP/UserAgent.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/LWP/UserAgent.pm new file mode 100644 index 0000000000..fe8bf27a4a --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/LWP/UserAgent.pm @@ -0,0 +1,62 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::LWP::UserAgent; +use strict; +use vars qw(@ISA $USER $PASSWD $SETUPDONE); +use CPAN::HTTP::Credentials; +# we delay requiring LWP::UserAgent and setting up inheritance until we need it + +$CPAN::LWP::UserAgent::VERSION = $CPAN::LWP::UserAgent::VERSION = "1.9601"; + + +sub config { + return if $SETUPDONE; + if ($CPAN::META->has_usable('LWP::UserAgent')) { + require LWP::UserAgent; + @ISA = qw(Exporter LWP::UserAgent); ## no critic + $SETUPDONE++; + } else { + $CPAN::Frontend->mywarn(" LWP::UserAgent not available\n"); + } +} + +sub get_basic_credentials { + my($self, $realm, $uri, $proxy) = @_; + if ( $proxy ) { + return CPAN::HTTP::Credentials->get_proxy_credentials(); + } else { + return CPAN::HTTP::Credentials->get_non_proxy_credentials(); + } +} + +sub no_proxy { + my ( $self, $no_proxy ) = @_; + return $self->SUPER::no_proxy( split(',',$no_proxy) ); +} + +# mirror(): Its purpose is to deal with proxy authentication. When we +# call SUPER::mirror, we really call the mirror method in +# LWP::UserAgent. LWP::UserAgent will then call +# $self->get_basic_credentials or some equivalent and this will be +# $self->dispatched to our own get_basic_credentials method. + +# Our own get_basic_credentials sets $USER and $PASSWD, two globals. + +# 407 stands for HTTP_PROXY_AUTHENTICATION_REQUIRED. Which means +# although we have gone through our get_basic_credentials, the proxy +# server refuses to connect. This could be a case where the username or +# password has changed in the meantime, so I'm trying once again without +# $USER and $PASSWD to give the get_basic_credentials routine another +# chance to set $USER and $PASSWD. + +sub mirror { + my($self,$url,$aslocal) = @_; + my $result = $self->SUPER::mirror($url,$aslocal); + if ($result->code == 407) { + CPAN::HTTP::Credentials->clear_credentials; + $result = $self->SUPER::mirror($url,$aslocal); + } + $result; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Mirrors.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Mirrors.pm new file mode 100644 index 0000000000..721ead2a85 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Mirrors.pm @@ -0,0 +1,638 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +=head1 NAME + +CPAN::Mirrors - Get CPAN mirror information and select a fast one + +=head1 SYNOPSIS + + use CPAN::Mirrors; + + my $mirrors = CPAN::Mirrors->new( $mirrored_by_file ); + + my $seen = {}; + + my $best_continent = $mirrors->find_best_continents( { seen => $seen } ); + my @mirrors = $mirrors->get_mirrors_by_continents( $best_continent ); + + my $callback = sub { + my( $m ) = @_; + printf "%s = %s\n", $m->hostname, $m->rtt + }; + $mirrors->get_mirrors_timings( \@mirrors, $seen, $callback, %args ); + + @mirrors = sort { $a->rtt <=> $b->rtt } @mirrors; + + print "Best mirrors are ", map( { $_->rtt } @mirrors[0..3] ), "\n"; + +=head1 DESCRIPTION + +=over + +=cut + +package CPAN::Mirrors; +use strict; +use vars qw($VERSION $urllist $silent); +$VERSION = "2.27"; + +use Carp; +use FileHandle; +use Fcntl ":flock"; +use Net::Ping (); +use CPAN::Version; + +=item new( LOCAL_FILE_NAME ) + +Create a new CPAN::Mirrors object from LOCAL_FILE_NAME. This file +should look like that in http://www.cpan.org/MIRRORED.BY . + +=cut + +sub new { + my ($class, $file) = @_; + croak "CPAN::Mirrors->new requires a filename" unless defined $file; + croak "The file [$file] was not found" unless -e $file; + + my $self = bless { + mirrors => [], + geography => {}, + }, $class; + + $self->parse_mirrored_by( $file ); + + return $self; +} + +sub parse_mirrored_by { + my ($self, $file) = @_; + my $handle = FileHandle->new; + $handle->open($file) + or croak "Couldn't open $file: $!"; + flock $handle, LOCK_SH; + $self->_parse($file,$handle); + flock $handle, LOCK_UN; + $handle->close; +} + +=item continents() + +Return a list of continents based on those defined in F<MIRRORED.BY>. + +=cut + +sub continents { + my ($self) = @_; + return sort keys %{$self->{geography} || {}}; +} + +=item countries( [CONTINENTS] ) + +Return a list of countries based on those defined in F<MIRRORED.BY>. +It only returns countries for the continents you specify (as defined +in C<continents>). If you don't specify any continents, it returns all +of the countries listed in F<MIRRORED.BY>. + +=cut + +sub countries { + my ($self, @continents) = @_; + @continents = $self->continents unless @continents; + my @countries; + for my $c (@continents) { + push @countries, sort keys %{ $self->{geography}{$c} || {} }; + } + return @countries; +} + +=item mirrors( [COUNTRIES] ) + +Return a list of mirrors based on those defined in F<MIRRORED.BY>. +It only returns mirrors for the countries you specify (as defined +in C<countries>). If you don't specify any countries, it returns all +of the mirrors listed in F<MIRRORED.BY>. + +=cut + +sub mirrors { + my ($self, @countries) = @_; + return @{$self->{mirrors}} unless @countries; + my %wanted = map { $_ => 1 } @countries; + my @found; + for my $m (@{$self->{mirrors}}) { + push @found, $m if exists $wanted{$m->country}; + } + return @found; +} + +=item get_mirrors_by_countries( [COUNTRIES] ) + +A more sensible synonym for mirrors. + +=cut + +sub get_mirrors_by_countries { &mirrors } + +=item get_mirrors_by_continents( [CONTINENTS] ) + +Return a list of mirrors for all of continents you specify. If you don't +specify any continents, it returns all of the mirrors. + +You can specify a single continent or an array reference of continents. + +=cut + +sub get_mirrors_by_continents { + my ($self, $continents ) = @_; + $continents = [ $continents ] unless ref $continents; + + eval { + $self->mirrors( $self->get_countries_by_continents( @$continents ) ); + }; + } + +=item get_countries_by_continents( [CONTINENTS] ) + +A more sensible synonym for countries. + +=cut + +sub get_countries_by_continents { &countries } + +=item default_mirror + +Returns the default mirror, http://www.cpan.org/ . This mirror uses +dynamic DNS to give a close mirror. + +=cut + +sub default_mirror { + CPAN::Mirrored::By->new({ http => 'http://www.cpan.org/'}); +} + +=item best_mirrors + +C<best_mirrors> checks for the best mirrors based on the list of +continents you pass, or, without that, all continents, as defined +by C<CPAN::Mirrored::By>. It pings each mirror, up to the value of +C<how_many>. In list context, it returns up to C<how_many> mirrors. +In scalar context, it returns the single best mirror. + +Arguments + + how_many - the number of mirrors to return. Default: 1 + callback - a callback for find_best_continents + verbose - true or false on all the whining and moaning. Default: false + continents - an array ref of the continents to check + external_ping - if true, use external ping via Net::Ping::External. Default: false + +If you don't specify the continents, C<best_mirrors> calls +C<find_best_continents> to get the list of continents to check. + +If you don't have L<Net::Ping> v2.13 or later, needed for timings, +this returns the default mirror. + +C<external_ping> should be set and then C<Net::Ping::External> needs +to be installed, if the local network has a transparent proxy. + +=cut + +sub best_mirrors { + my ($self, %args) = @_; + my $how_many = $args{how_many} || 1; + my $callback = $args{callback}; + my $verbose = defined $args{verbose} ? $args{verbose} : 0; + my $continents = $args{continents} || []; + $continents = [$continents] unless ref $continents; + $args{external_ping} = 0 unless defined $args{external_ping}; + my $external_ping = $args{external_ping}; + + # Old Net::Ping did not do timings at all + my $min_version = '2.13'; + unless( CPAN::Version->vgt(Net::Ping->VERSION, $min_version) ) { + carp sprintf "Net::Ping version is %s (< %s). Returning %s", + Net::Ping->VERSION, $min_version, $self->default_mirror; + return $self->default_mirror; + } + + my $seen = {}; + + if ( ! @$continents ) { + print "Searching for the best continent ...\n" if $verbose; + my @best_continents = $self->find_best_continents( + seen => $seen, + verbose => $verbose, + callback => $callback, + external_ping => $external_ping, + ); + + # Only add enough continents to find enough mirrors + my $count = 0; + for my $continent ( @best_continents ) { + push @$continents, $continent; + $count += $self->mirrors( $self->countries($continent) ); + last if $count >= $how_many; + } + } + + return $self->default_mirror unless @$continents; + print "Scanning " . join(", ", @$continents) . " ...\n" if $verbose; + + my $trial_mirrors = $self->get_n_random_mirrors_by_continents( 3 * $how_many, $continents->[0] ); + + my $timings = $self->get_mirrors_timings( + $trial_mirrors, + $seen, + $callback, + %args, + ); + return $self->default_mirror unless @$timings; + + $how_many = @$timings if $how_many > @$timings; + + return wantarray ? @{$timings}[0 .. $how_many-1] : $timings->[0]; +} + +=item get_n_random_mirrors_by_continents( N, [CONTINENTS] ) + +Returns up to N random mirrors for the specified continents. Specify the +continents as an array reference. + +=cut + +sub get_n_random_mirrors_by_continents { + my( $self, $n, $continents ) = @_; + $n ||= 3; + $continents = [ $continents ] unless ref $continents; + + if ( $n <= 0 ) { + return wantarray ? () : []; + } + + my @long_list = $self->get_mirrors_by_continents( $continents ); + + if ( $n eq '*' or $n > @long_list ) { + return wantarray ? @long_list : \@long_list; + } + + @long_list = map {$_->[0]} + sort {$a->[1] <=> $b->[1]} + map {[$_, rand]} @long_list; + + splice @long_list, $n; # truncate + + \@long_list; +} + +=item get_mirrors_timings( MIRROR_LIST, SEEN, CALLBACK, %ARGS ); + +Pings the listed mirrors and returns a list of mirrors sorted in +ascending ping times. + +C<MIRROR_LIST> is an anonymous array of C<CPAN::Mirrored::By> objects to +ping. + +The optional argument C<SEEN> is a hash reference used to track the +mirrors you've already pinged. + +The optional argument C<CALLBACK> is a subroutine reference to call +after each ping. It gets the C<CPAN::Mirrored::By> object after each +ping. + +=cut + +sub get_mirrors_timings { + my( $self, $mirror_list, $seen, $callback, %args ) = @_; + + $seen = {} unless defined $seen; + croak "The mirror list argument must be an array reference" + unless ref $mirror_list eq ref []; + croak "The seen argument must be a hash reference" + unless ref $seen eq ref {}; + croak "callback must be a subroutine" + if( defined $callback and ref $callback ne ref sub {} ); + + my $timings = []; + for my $m ( @$mirror_list ) { + $seen->{$m->hostname} = $m; + next unless eval{ $m->http }; + + if( $self->_try_a_ping( $seen, $m, ) ) { + my $ping = $m->ping(%args); + next unless defined $ping; + # printf "m %s ping %s\n", $m, $ping; + push @$timings, $m; + $callback->( $m ) if $callback; + } + else { + push @$timings, $seen->{$m->hostname} + if defined $seen->{$m->hostname}->rtt; + } + } + + my @best = sort { + if( defined $a->rtt and defined $b->rtt ) { + $a->rtt <=> $b->rtt + } + elsif( defined $a->rtt and ! defined $b->rtt ) { + return -1; + } + elsif( ! defined $a->rtt and defined $b->rtt ) { + return 1; + } + elsif( ! defined $a->rtt and ! defined $b->rtt ) { + return 0; + } + + } @$timings; + + return wantarray ? @best : \@best; +} + +=item find_best_continents( HASH_REF ); + +C<find_best_continents> goes through each continent and pings C<N> +random mirrors on that continent. It then orders the continents by +ascending median ping time. In list context, it returns the ordered list +of continent. In scalar context, it returns the same list as an +anonymous array. + +Arguments: + + n - the number of hosts to ping for each continent. Default: 3 + seen - a hashref of cached hostname ping times + verbose - true or false for noisy or quiet. Default: false + callback - a subroutine to run after each ping. + ping_cache_limit - how long, in seconds, to reuse previous ping times. + Default: 1 day + +The C<seen> hash has hostnames as keys and anonymous arrays as values. +The anonymous array is a triplet of a C<CPAN::Mirrored::By> object, a +ping time, and the epoch time for the measurement. + +The callback subroutine gets the C<CPAN::Mirrored::By> object, the ping +time, and measurement time (the same things in the C<seen> hashref) as +arguments. C<find_best_continents> doesn't care what the callback does +and ignores the return value. + +With a low value for C<N>, a single mirror might skew the results enough +to choose a worse continent. If you have that problem, try a larger +value. + +=cut + +sub find_best_continents { + my ($self, %args) = @_; + + $args{n} ||= 3; + $args{verbose} = 0 unless defined $args{verbose}; + $args{seen} = {} unless defined $args{seen}; + croak "The seen argument must be a hash reference" + unless ref $args{seen} eq ref {}; + $args{ping_cache_limit} = 24 * 60 * 60 + unless defined $args{ping_cache_limit}; + croak "callback must be a subroutine" + if( defined $args{callback} and ref $args{callback} ne ref sub {} ); + + my %medians; + CONT: for my $c ( $self->continents ) { + my @mirrors = $self->mirrors( $self->countries($c) ); + printf "Testing %s (%d mirrors)\n", $c, scalar @mirrors + if $args{verbose}; + + next CONT unless @mirrors; + my $n = (@mirrors < $args{n}) ? @mirrors : $args{n}; + + my @tests; + my $tries = 0; + RANDOM: while ( @mirrors && @tests < $n && $tries++ < 15 ) { + my $m = splice( @mirrors, int(rand(@mirrors)), 1 ); + if( $self->_try_a_ping( + $args{seen}, $m, $args{ping_cache_limit} + )) { + $self->get_mirrors_timings( + [ $m ], + $args{seen}, + $args{callback}, + %args, + ); + next RANDOM unless defined $args{seen}{$m->hostname}->rtt; + } + printf "(%s -> %0.2f ms)", + $m->hostname, + join ' ', 1000 * $args{seen}{$m->hostname}->rtt + if $args{verbose}; + + push @tests, $args{seen}{$m->hostname}->rtt; + } + + my $median = $self->_get_median_ping_time( \@tests, $args{verbose} ); + $medians{$c} = $median if defined $median; + } + + my @best_cont = sort { $medians{$a} <=> $medians{$b} } keys %medians; + + if ( $args{verbose} ) { + print "Median result by continent:\n"; + if ( @best_cont ) { + for my $c ( @best_cont ) { + printf( " %7.2f ms %s\n", $medians{$c}*1000, $c ); + } + } else { + print " **** No results found ****\n" + } + } + + return wantarray ? @best_cont : $best_cont[0]; +} + +# retry if +sub _try_a_ping { + my ($self, $seen, $mirror, $ping_cache_limit ) = @_; + + ( ! exists $seen->{$mirror->hostname} + or + ! defined $seen->{$mirror->hostname}->rtt + or + ! defined $ping_cache_limit + or + time - $seen->{$mirror->hostname}->ping_time + > $ping_cache_limit + ) +} + +sub _get_median_ping_time { + my ($self, $tests, $verbose ) = @_; + + my @sorted = sort { $a <=> $b } @$tests; + + my $median = do { + if ( @sorted == 0 ) { undef } + elsif ( @sorted == 1 ) { $sorted[0] } + elsif ( @sorted % 2 ) { $sorted[ int(@sorted / 2) ] } + else { + my $mid_high = int(@sorted/2); + ($sorted[$mid_high-1] + $sorted[$mid_high])/2; + } + }; + + if ($verbose){ + if ($median) { + printf " => median time: %.2f ms\n", $median * 1000 + } else { + printf " => **** no median time ****\n"; + } + } + + return $median; +} + +# Adapted from Parse::CPAN::MirroredBy by Adam Kennedy +sub _parse { + my ($self, $file, $handle) = @_; + my $output = $self->{mirrors}; + my $geo = $self->{geography}; + + local $/ = "\012"; + my $line = 0; + my $mirror = undef; + while ( 1 ) { + # Next line + my $string = <$handle>; + last if ! defined $string; + $line = $line + 1; + + # Remove the useless lines + chomp( $string ); + next if $string =~ /^\s*$/; + next if $string =~ /^\s*#/; + + # Hostname or property? + if ( $string =~ /^\s/ ) { + # Property + unless ( $string =~ /^\s+(\w+)\s+=\s+\"(.*)\"$/ ) { + croak("Invalid property on line $line"); + } + my ($prop, $value) = ($1,$2); + $mirror ||= {}; + if ( $prop eq 'dst_location' ) { + my (@location,$continent,$country); + @location = (split /\s*,\s*/, $value) + and ($continent, $country) = @location[-1,-2]; + $continent =~ s/\s\(.*//; + $continent =~ s/\W+$//; # if Jarkko doesn't know latitude/longitude + $geo->{$continent}{$country} = 1 if $continent && $country; + $mirror->{continent} = $continent || "unknown"; + $mirror->{country} = $country || "unknown"; + } + elsif ( $prop eq 'dst_http' ) { + $mirror->{http} = $value; + } + elsif ( $prop eq 'dst_ftp' ) { + $mirror->{ftp} = $value; + } + elsif ( $prop eq 'dst_rsync' ) { + $mirror->{rsync} = $value; + } + else { + $prop =~ s/^dst_//; + $mirror->{$prop} = $value; + } + } else { + # Hostname + unless ( $string =~ /^([\w\.-]+)\:\s*$/ ) { + croak("Invalid host name on line $line"); + } + my $current = $mirror; + $mirror = { hostname => "$1" }; + if ( $current ) { + push @$output, CPAN::Mirrored::By->new($current); + } + } + } + if ( $mirror ) { + push @$output, CPAN::Mirrored::By->new($mirror); + } + + return; +} + +#--------------------------------------------------------------------------# + +package CPAN::Mirrored::By; +use strict; +use Net::Ping (); + +sub new { + my($self,$arg) = @_; + $arg ||= {}; + bless $arg, $self; +} +sub hostname { shift->{hostname} } +sub continent { shift->{continent} } +sub country { shift->{country} } +sub http { shift->{http} || '' } +sub ftp { shift->{ftp} || '' } +sub rsync { shift->{rsync} || '' } +sub rtt { shift->{rtt} } +sub ping_time { shift->{ping_time} } + +sub url { + my $self = shift; + return $self->{http} || $self->{ftp}; +} + +sub ping { + my($self, %args) = @_; + + my $external_ping = $args{external_ping}; + if ($external_ping) { + eval { require Net::Ping::External } + or die "Net::Ping::External required to use external ping command"; + } + my $ping = Net::Ping->new( + $external_ping ? 'external' : $^O eq 'VMS' ? 'icmp' : 'tcp', + 1 + ); + my ($proto) = $self->url =~ m{^([^:]+)}; + my $port = $proto eq 'http' ? 80 : 21; + return unless $port; + + if ( $ping->can('port_number') ) { + $ping->port_number($port); + } + else { + $ping->{'port_num'} = $port; + } + + $ping->hires(1) if $ping->can('hires'); + my ($alive,$rtt) = eval { $ping->ping($self->hostname); }; + my $verbose = $args{verbose}; + if ($verbose && !$alive) { + printf "(host %s not alive)", $self->hostname; + } + + $self->{rtt} = $alive ? $rtt : undef; + $self->{ping_time} = time; + + $self->rtt; +} + + +1; + +=back + +=head1 AUTHOR + +Andreas Koenig C<< <andk@cpan.org> >>, David Golden C<< <dagolden@cpan.org> >>, +brian d foy C<< <bdfoy@cpan.org> >> + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +See L<http://www.perl.com/perl/misc/Artistic.html> + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Module.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Module.pm new file mode 100644 index 0000000000..62ca42caf0 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Module.pm @@ -0,0 +1,702 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Module; +use strict; +@CPAN::Module::ISA = qw(CPAN::InfoObj); + +use vars qw( + $VERSION +); +$VERSION = "5.5003"; + +BEGIN { + # alarm() is not implemented in perl 5.6.x and earlier under Windows + *ALARM_IMPLEMENTED = sub () { $] >= 5.007 || $^O !~ /MSWin/ }; +} + +# Accessors +#-> sub CPAN::Module::userid +sub userid { + my $self = shift; + my $ro = $self->ro; + return unless $ro; + return $ro->{userid} || $ro->{CPAN_USERID}; +} +#-> sub CPAN::Module::description +sub description { + my $self = shift; + my $ro = $self->ro or return ""; + $ro->{description} +} + +#-> sub CPAN::Module::distribution +sub distribution { + my($self) = @_; + CPAN::Shell->expand("Distribution",$self->cpan_file); +} + +#-> sub CPAN::Module::_is_representative_module +sub _is_representative_module { + my($self) = @_; + return $self->{_is_representative_module} if defined $self->{_is_representative_module}; + my $pm = $self->cpan_file or return $self->{_is_representative_module} = 0; + $pm =~ s|.+/||; + $pm =~ s{\.(?:tar\.(bz2|gz|Z)|t(?:gz|bz)|zip)$}{}i; # see base_id + $pm =~ s|-\d+\.\d+.+$||; + $pm =~ s|-[\d\.]+$||; + $pm =~ s/-/::/g; + $self->{_is_representative_module} = $pm eq $self->{ID} ? 1 : 0; + # warn "DEBUG: $pm eq $self->{ID} => $self->{_is_representative_module}"; + $self->{_is_representative_module}; +} + +#-> sub CPAN::Module::undelay +sub undelay { + my $self = shift; + delete $self->{later}; + if ( my $dist = CPAN::Shell->expand("Distribution", $self->cpan_file) ) { + $dist->undelay; + } +} + +# mark as dirty/clean +#-> sub CPAN::Module::color_cmd_tmps ; +sub color_cmd_tmps { + my($self) = shift; + my($depth) = shift || 0; + my($color) = shift || 0; + my($ancestors) = shift || []; + # a module needs to recurse to its cpan_file + + return if exists $self->{incommandcolor} + && $color==1 + && $self->{incommandcolor}==$color; + return if $color==0 && !$self->{incommandcolor}; + if ($color>=1) { + if ( $self->uptodate ) { + $self->{incommandcolor} = $color; + return; + } elsif (my $have_version = $self->available_version) { + # maybe what we have is good enough + if (@$ancestors) { + my $who_asked_for_me = $ancestors->[-1]; + my $obj = CPAN::Shell->expandany($who_asked_for_me); + if (0) { + } elsif ($obj->isa("CPAN::Bundle")) { + # bundles cannot specify a minimum version + return; + } elsif ($obj->isa("CPAN::Distribution")) { + if (my $prereq_pm = $obj->prereq_pm) { + for my $k (keys %$prereq_pm) { + if (my $want_version = $prereq_pm->{$k}{$self->id}) { + if (CPAN::Version->vcmp($have_version,$want_version) >= 0) { + $self->{incommandcolor} = $color; + return; + } + } + } + } + } + } + } + } else { + $self->{incommandcolor} = $color; # set me before recursion, + # so we can break it + } + if ($depth>=$CPAN::MAX_RECURSION) { + my $e = CPAN::Exception::RecursiveDependency->new($ancestors); + if ($e->is_resolvable) { + return $self->{incommandcolor}=2; + } else { + die $e; + } + } + # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; + + if ( my $dist = CPAN::Shell->expand("Distribution", $self->cpan_file) ) { + $dist->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); + } + # unreached code? + # if ($color==0) { + # delete $self->{badtestcnt}; + # } + $self->{incommandcolor} = $color; +} + +#-> sub CPAN::Module::as_glimpse ; +sub as_glimpse { + my($self) = @_; + my(@m); + my $class = ref($self); + $class =~ s/^CPAN:://; + my $color_on = ""; + my $color_off = ""; + if ( + $CPAN::Shell::COLOR_REGISTERED + && + $CPAN::META->has_inst("Term::ANSIColor") + && + $self->description + ) { + $color_on = Term::ANSIColor::color("green"); + $color_off = Term::ANSIColor::color("reset"); + } + my $uptodateness = " "; + unless ($class eq "Bundle") { + my $u = $self->uptodate; + $uptodateness = $u ? "=" : "<" if defined $u; + }; + my $id = do { + my $d = $self->distribution; + $d ? $d -> pretty_id : $self->cpan_userid; + }; + push @m, sprintf("%-7s %1s %s%-22s%s (%s)\n", + $class, + $uptodateness, + $color_on, + $self->id, + $color_off, + $id, + ); + join "", @m; +} + +#-> sub CPAN::Module::dslip_status +sub dslip_status { + my($self) = @_; + my($stat); + # development status + @{$stat->{D}}{qw,i c a b R M S,} = qw,idea + pre-alpha alpha beta released + mature standard,; + # support level + @{$stat->{S}}{qw,m d u n a,} = qw,mailing-list + developer comp.lang.perl.* + none abandoned,; + # language + @{$stat->{L}}{qw,p c + o h,} = qw,perl C C++ other hybrid,; + # interface + @{$stat->{I}}{qw,f r O p h n,} = qw,functions + references+ties + object-oriented pragma + hybrid none,; + # public licence + @{$stat->{P}}{qw,p g l b a 2 o d r n,} = qw,Standard-Perl + GPL LGPL + BSD Artistic Artistic_2 + open-source + distribution_allowed + restricted_distribution + no_licence,; + for my $x (qw(d s l i p)) { + $stat->{$x}{' '} = 'unknown'; + $stat->{$x}{'?'} = 'unknown'; + } + my $ro = $self->ro; + return +{} unless $ro && $ro->{statd}; + return { + D => $ro->{statd}, + S => $ro->{stats}, + L => $ro->{statl}, + I => $ro->{stati}, + P => $ro->{statp}, + DV => $stat->{D}{$ro->{statd}}, + SV => $stat->{S}{$ro->{stats}}, + LV => $stat->{L}{$ro->{statl}}, + IV => $stat->{I}{$ro->{stati}}, + PV => $stat->{P}{$ro->{statp}}, + }; +} + +#-> sub CPAN::Module::as_string ; +sub as_string { + my($self) = @_; + my(@m); + CPAN->debug("$self entering as_string") if $CPAN::DEBUG; + my $class = ref($self); + $class =~ s/^CPAN:://; + local($^W) = 0; + push @m, $class, " id = $self->{ID}\n"; + my $sprintf = " %-12s %s\n"; + push @m, sprintf($sprintf, 'DESCRIPTION', $self->description) + if $self->description; + my $sprintf2 = " %-12s %s (%s)\n"; + my($userid); + $userid = $self->userid; + if ( $userid ) { + my $author; + if ($author = CPAN::Shell->expand('Author',$userid)) { + my $email = ""; + my $m; # old perls + if ($m = $author->email) { + $email = " <$m>"; + } + push @m, sprintf( + $sprintf2, + 'CPAN_USERID', + $userid, + $author->fullname . $email + ); + } + } + push @m, sprintf($sprintf, 'CPAN_VERSION', $self->cpan_version) + if $self->cpan_version; + if (my $cpan_file = $self->cpan_file) { + push @m, sprintf($sprintf, 'CPAN_FILE', $cpan_file); + if (my $dist = CPAN::Shell->expand("Distribution",$cpan_file)) { + my $upload_date = $dist->upload_date; + if ($upload_date) { + push @m, sprintf($sprintf, 'UPLOAD_DATE', $upload_date); + } + } + } + my $sprintf3 = " %-12s %1s%1s%1s%1s%1s (%s,%s,%s,%s,%s)\n"; + my $dslip = $self->dslip_status; + push @m, sprintf( + $sprintf3, + 'DSLIP_STATUS', + @{$dslip}{qw(D S L I P DV SV LV IV PV)}, + ) if $dslip->{D}; + my $local_file = $self->inst_file; + unless ($self->{MANPAGE}) { + my $manpage; + if ($local_file) { + $manpage = $self->manpage_headline($local_file); + } else { + # If we have already untarred it, we should look there + my $dist = $CPAN::META->instance('CPAN::Distribution', + $self->cpan_file); + # warn "dist[$dist]"; + # mff=manifest file; mfh=manifest handle + my($mff,$mfh); + if ( + $dist->{build_dir} + and + (-f ($mff = File::Spec->catfile($dist->{build_dir}, "MANIFEST"))) + and + $mfh = FileHandle->new($mff) + ) { + CPAN->debug("mff[$mff]") if $CPAN::DEBUG; + my $lfre = $self->id; # local file RE + $lfre =~ s/::/./g; + $lfre .= "\\.pm\$"; + my($lfl); # local file file + local $/ = "\n"; + my(@mflines) = <$mfh>; + for (@mflines) { + s/^\s+//; + s/\s.*//s; + } + while (length($lfre)>5 and !$lfl) { + ($lfl) = grep /$lfre/, @mflines; + CPAN->debug("lfl[$lfl]lfre[$lfre]") if $CPAN::DEBUG; + $lfre =~ s/.+?\.//; + } + $lfl =~ s/\s.*//; # remove comments + $lfl =~ s/\s+//g; # chomp would maybe be too system-specific + my $lfl_abs = File::Spec->catfile($dist->{build_dir},$lfl); + # warn "lfl_abs[$lfl_abs]"; + if (-f $lfl_abs) { + $manpage = $self->manpage_headline($lfl_abs); + } + } + } + $self->{MANPAGE} = $manpage if $manpage; + } + my($item); + for $item (qw/MANPAGE/) { + push @m, sprintf($sprintf, $item, $self->{$item}) + if exists $self->{$item}; + } + for $item (qw/CONTAINS/) { + push @m, sprintf($sprintf, $item, join(" ",@{$self->{$item}})) + if exists $self->{$item} && @{$self->{$item}}; + } + push @m, sprintf($sprintf, 'INST_FILE', + $local_file || "(not installed)"); + push @m, sprintf($sprintf, 'INST_VERSION', + $self->inst_version) if $local_file; + if (%{$CPAN::META->{is_tested}||{}}) { # XXX needs to be methodified somehow + my $available_file = $self->available_file; + if ($available_file && $available_file ne $local_file) { + push @m, sprintf($sprintf, 'AVAILABLE_FILE', $available_file); + push @m, sprintf($sprintf, 'AVAILABLE_VERSION', $self->available_version); + } + } + join "", @m, "\n"; +} + +#-> sub CPAN::Module::manpage_headline +sub manpage_headline { + my($self,$local_file) = @_; + my(@local_file) = $local_file; + $local_file =~ s/\.pm(?!\n)\Z/.pod/; + push @local_file, $local_file; + my(@result,$locf); + for $locf (@local_file) { + next unless -f $locf; + my $fh = FileHandle->new($locf) + or $Carp::Frontend->mydie("Couldn't open $locf: $!"); + my $inpod = 0; + local $/ = "\n"; + while (<$fh>) { + $inpod = m/^=(?!head1\s+NAME\s*$)/ ? 0 : + m/^=head1\s+NAME\s*$/ ? 1 : $inpod; + next unless $inpod; + next if /^=/; + next if /^\s+$/; + chomp; + push @result, $_; + } + close $fh; + last if @result; + } + for (@result) { + s/^\s+//; + s/\s+$//; + } + join " ", @result; +} + +#-> sub CPAN::Module::cpan_file ; +# Note: also inherited by CPAN::Bundle +sub cpan_file { + my $self = shift; + # CPAN->debug(sprintf "id[%s]", $self->id) if $CPAN::DEBUG; + unless ($self->ro) { + CPAN::Index->reload; + } + my $ro = $self->ro; + if ($ro && defined $ro->{CPAN_FILE}) { + return $ro->{CPAN_FILE}; + } else { + my $userid = $self->userid; + if ( $userid ) { + if ($CPAN::META->exists("CPAN::Author",$userid)) { + my $author = $CPAN::META->instance("CPAN::Author", + $userid); + my $fullname = $author->fullname; + my $email = $author->email; + unless (defined $fullname && defined $email) { + return sprintf("Contact Author %s", + $userid, + ); + } + return "Contact Author $fullname <$email>"; + } else { + return "Contact Author $userid (Email address not available)"; + } + } else { + return "N/A"; + } + } +} + +#-> sub CPAN::Module::cpan_version ; +sub cpan_version { + my $self = shift; + + my $ro = $self->ro; + unless ($ro) { + # Can happen with modules that are not on CPAN + $ro = {}; + } + $ro->{CPAN_VERSION} = 'undef' + unless defined $ro->{CPAN_VERSION}; + $ro->{CPAN_VERSION}; +} + +#-> sub CPAN::Module::force ; +sub force { + my($self) = @_; + $self->{force_update} = 1; +} + +#-> sub CPAN::Module::fforce ; +sub fforce { + my($self) = @_; + $self->{force_update} = 2; +} + +#-> sub CPAN::Module::notest ; +sub notest { + my($self) = @_; + # $CPAN::Frontend->mywarn("XDEBUG: set notest for Module"); + $self->{notest}++; +} + +#-> sub CPAN::Module::rematein ; +sub rematein { + my($self,$meth) = @_; + $CPAN::Frontend->myprint(sprintf("Running %s for module '%s'\n", + $meth, + $self->id)); + my $cpan_file = $self->cpan_file; + if ($cpan_file eq "N/A" || $cpan_file =~ /^Contact Author/) { + $CPAN::Frontend->mywarn(sprintf qq{ + The module %s isn\'t available on CPAN. + + Either the module has not yet been uploaded to CPAN, or it is + temporary unavailable. Please contact the author to find out + more about the status. Try 'i %s'. +}, + $self->id, + $self->id, + ); + return; + } + my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); + $pack->called_for($self->id); + if (exists $self->{force_update}) { + if ($self->{force_update} == 2) { + $pack->fforce($meth); + } else { + $pack->force($meth); + } + } + $pack->notest($meth) if exists $self->{notest} && $self->{notest}; + + $pack->{reqtype} ||= ""; + CPAN->debug("dist-reqtype[$pack->{reqtype}]". + "self-reqtype[$self->{reqtype}]") if $CPAN::DEBUG; + if ($pack->{reqtype}) { + if ($pack->{reqtype} eq "b" && $self->{reqtype} =~ /^[rc]$/) { + $pack->{reqtype} = $self->{reqtype}; + if ( + exists $pack->{install} + && + ( + UNIVERSAL::can($pack->{install},"failed") ? + $pack->{install}->failed : + $pack->{install} =~ /^NO/ + ) + ) { + delete $pack->{install}; + $CPAN::Frontend->mywarn + ("Promoting $pack->{ID} from 'build_requires' to 'requires'"); + } + } + } else { + $pack->{reqtype} = $self->{reqtype}; + } + + my $success = eval { + $pack->$meth(); + }; + my $err = $@; + $pack->unforce if $pack->can("unforce") && exists $self->{force_update}; + $pack->unnotest if $pack->can("unnotest") && exists $self->{notest}; + delete $self->{force_update}; + delete $self->{notest}; + if ($err) { + die $err; + } + return $success; +} + +#-> sub CPAN::Module::perldoc ; +sub perldoc { shift->rematein('perldoc') } +#-> sub CPAN::Module::readme ; +sub readme { shift->rematein('readme') } +#-> sub CPAN::Module::look ; +sub look { shift->rematein('look') } +#-> sub CPAN::Module::cvs_import ; +sub cvs_import { shift->rematein('cvs_import') } +#-> sub CPAN::Module::get ; +sub get { shift->rematein('get',@_) } +#-> sub CPAN::Module::make ; +sub make { shift->rematein('make') } +#-> sub CPAN::Module::test ; +sub test { + my $self = shift; + # $self->{badtestcnt} ||= 0; + $self->rematein('test',@_); +} + +#-> sub CPAN::Module::deprecated_in_core ; +sub deprecated_in_core { + my ($self) = @_; + return unless $CPAN::META->has_inst('Module::CoreList') && Module::CoreList->can('is_deprecated'); + return Module::CoreList::is_deprecated($self->{ID}); +} + +#-> sub CPAN::Module::inst_deprecated; +# Indicates whether the *installed* version of the module is a deprecated *and* +# installed as part of the Perl core library path +sub inst_deprecated { + my ($self) = @_; + my $inst_file = $self->inst_file or return; + return $self->deprecated_in_core && $self->_in_priv_or_arch($inst_file); +} + +#-> sub CPAN::Module::uptodate ; +sub uptodate { + my ($self) = @_; + local ($_); + my $inst = $self->inst_version or return 0; + my $cpan = $self->cpan_version; + return 0 if CPAN::Version->vgt($cpan,$inst) || $self->inst_deprecated; + CPAN->debug + (join + ("", + "returning uptodate. ", + "cpan[$cpan]inst[$inst]", + )) if $CPAN::DEBUG; + return 1; +} + +# returns true if installed in privlib or archlib +sub _in_priv_or_arch { + my($self,$inst_file) = @_; + foreach my $pair ( + [qw(sitearchexp archlibexp)], + [qw(sitelibexp privlibexp)] + ) { + my ($site, $priv) = @Config::Config{@$pair}; + if ($^O eq 'VMS') { + for my $d ($site, $priv) { $d = VMS::Filespec::unixify($d) }; + } + s!/*$!!g foreach $site, $priv; + next if $site eq $priv; + + if ($priv eq substr($inst_file,0,length($priv))) { + return 1; + } + } + return 0; +} + +#-> sub CPAN::Module::install ; +sub install { + my($self) = @_; + my($doit) = 0; + if ($self->uptodate + && + not exists $self->{force_update} + ) { + $CPAN::Frontend->myprint(sprintf("%s is up to date (%s).\n", + $self->id, + $self->inst_version, + )); + } else { + $doit = 1; + } + my $ro = $self->ro; + if ($ro && $ro->{stats} && $ro->{stats} eq "a") { + $CPAN::Frontend->mywarn(qq{ +\n\n\n ***WARNING*** + The module $self->{ID} has no active maintainer (CPAN support level flag 'abandoned').\n\n\n +}); + $CPAN::Frontend->mysleep(5); + } + return $doit ? $self->rematein('install') : 1; +} +#-> sub CPAN::Module::clean ; +sub clean { shift->rematein('clean') } + +#-> sub CPAN::Module::inst_file ; +sub inst_file { + my($self) = @_; + $self->_file_in_path([@INC]); +} + +#-> sub CPAN::Module::available_file ; +sub available_file { + my($self) = @_; + my $sep = $Config::Config{path_sep}; + my $perllib = $ENV{PERL5LIB}; + $perllib = $ENV{PERLLIB} unless defined $perllib; + my @perllib = split(/$sep/,$perllib) if defined $perllib; + my @cpan_perl5inc; + if ($CPAN::Perl5lib_tempfile) { + my $yaml = CPAN->_yaml_loadfile($CPAN::Perl5lib_tempfile); + @cpan_perl5inc = @{$yaml->[0]{inc} || []}; + } + $self->_file_in_path([@cpan_perl5inc,@perllib,@INC]); +} + +#-> sub CPAN::Module::file_in_path ; +sub _file_in_path { + my($self,$path) = @_; + my($dir,@packpath); + @packpath = split /::/, $self->{ID}; + $packpath[-1] .= ".pm"; + if (@packpath == 1 && $packpath[0] eq "readline.pm") { + unshift @packpath, "Term", "ReadLine"; # historical reasons + } + foreach $dir (@$path) { + my $pmfile = File::Spec->catfile($dir,@packpath); + if (-f $pmfile) { + return $pmfile; + } + } + return; +} + +#-> sub CPAN::Module::xs_file ; +sub xs_file { + my($self) = @_; + my($dir,@packpath); + @packpath = split /::/, $self->{ID}; + push @packpath, $packpath[-1]; + $packpath[-1] .= "." . $Config::Config{'dlext'}; + foreach $dir (@INC) { + my $xsfile = File::Spec->catfile($dir,'auto',@packpath); + if (-f $xsfile) { + return $xsfile; + } + } + return; +} + +#-> sub CPAN::Module::inst_version ; +sub inst_version { + my($self) = @_; + my $parsefile = $self->inst_file or return; + my $have = $self->parse_version($parsefile); + $have; +} + +#-> sub CPAN::Module::inst_version ; +sub available_version { + my($self) = @_; + my $parsefile = $self->available_file or return; + my $have = $self->parse_version($parsefile); + $have; +} + +#-> sub CPAN::Module::parse_version ; +sub parse_version { + my($self,$parsefile) = @_; + if (ALARM_IMPLEMENTED) { + my $timeout = (exists($CPAN::Config{'version_timeout'})) + ? $CPAN::Config{'version_timeout'} + : 15; + alarm($timeout); + } + my $have = eval { + local $SIG{ALRM} = sub { die "alarm\n" }; + MM->parse_version($parsefile); + }; + if ($@) { + $CPAN::Frontend->mywarn("Error while parsing version number in file '$parsefile'\n"); + } + alarm(0) if ALARM_IMPLEMENTED; + my $leastsanity = eval { defined $have && length $have; }; + $have = "undef" unless $leastsanity; + $have =~ s/^ //; # since the %vd hack these two lines here are needed + $have =~ s/ $//; # trailing whitespace happens all the time + + $have = CPAN::Version->readable($have); + + $have =~ s/\s*//g; # stringify to float around floating point issues + $have; # no stringify needed, \s* above matches always +} + +#-> sub CPAN::Module::reports +sub reports { + my($self) = @_; + $self->distribution->reports; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Nox.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Nox.pm new file mode 100644 index 0000000000..f7ed4a38af --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Nox.pm @@ -0,0 +1,52 @@ +package CPAN::Nox; +use strict; +use vars qw($VERSION @EXPORT); + +BEGIN{ + $CPAN::Suppress_readline=1 unless defined $CPAN::term; +} + +use Exporter (); +@CPAN::ISA = ('Exporter'); +use CPAN; + +$VERSION = "5.5001"; +$CPAN::META->has_inst('Digest::MD5','no'); +$CPAN::META->has_inst('LWP','no'); +$CPAN::META->has_inst('Compress::Zlib','no'); +@EXPORT = @CPAN::EXPORT; + +*AUTOLOAD = \&CPAN::AUTOLOAD; + +1; + +__END__ + +=head1 NAME + +CPAN::Nox - Wrapper around CPAN.pm without using any XS module + +=head1 SYNOPSIS + +Interactive mode: + + perl -MCPAN::Nox -e shell; + +=head1 DESCRIPTION + +This package has the same functionality as CPAN.pm, but tries to +prevent the usage of compiled extensions during its own +execution. Its primary purpose is a rescue in case you upgraded perl +and broke binary compatibility somehow. + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=head1 SEE ALSO + +L<CPAN> + +=cut + diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Plugin.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Plugin.pm new file mode 100644 index 0000000000..458d87aa2e --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Plugin.pm @@ -0,0 +1,145 @@ +package CPAN::Plugin; + +use strict; +use warnings; + +our $VERSION = '0.97'; + +require CPAN; + +###################################################################### + +sub new { # ; + my ($class, %params) = @_; + + my $self = +{ + (ref $class ? (%$class) : ()), + %params, + }; + + $self = bless $self, ref $class ? ref $class : $class; + + unless (ref $class) { + local $_; + no warnings 'once'; + $CPAN::META->use_inst ($_) for $self->plugin_requires; + } + + $self; +} + +###################################################################### +sub plugin_requires { # ; +} + +###################################################################### +sub distribution_object { # ; + my ($self) = @_; + $self->{distribution_object}; +} + +###################################################################### +sub distribution { # ; + my ($self) = @_; + + my $distribution = $self->distribution_object->id; + CPAN::Shell->expand("Distribution",$distribution) + or $self->frontend->mydie("Unknowns distribution '$distribution'\n"); +} + +###################################################################### +sub distribution_info { # ; + my ($self) = @_; + + CPAN::DistnameInfo->new ($self->distribution->id); +} + +###################################################################### +sub build_dir { # ; + my ($self) = @_; + + my $build_dir = $self->distribution->{build_dir} + or $self->frontend->mydie("Distribution has not been built yet, cannot proceed"); +} + +###################################################################### +sub is_xs { # + my ($self) = @_; + + my @xs = glob File::Spec->catfile ($self->build_dir, '*.xs'); # quick try + + unless (@xs) { + require ExtUtils::Manifest; + my $manifest_file = File::Spec->catfile ($self->build_dir, "MANIFEST"); + my $manifest = ExtUtils::Manifest::maniread($manifest_file); + @xs = grep /\.xs$/, keys %$manifest; + } + + scalar @xs; +} + +###################################################################### + +package CPAN::Plugin; + +1; + +__END__ + +=pod + +=head1 NAME + +CPAN::Plugin - Base class for CPAN shell extensions + +=head1 SYNOPSIS + + package CPAN::Plugin::Flurb; + use parent 'CPAN::Plugin'; + + sub post_test { + my ($self, $distribution_object) = @_; + $self = $self->new (distribution_object => $distribution_object); + ...; + } + +=head1 DESCRIPTION + +=head2 Alpha Status + +The plugin system in the CPAN shell was introduced in version 2.07 and +is still considered experimental. + +=head2 How Plugins work? + +See L<CPAN/"Plugin support">. + +=head1 METHODS + +=head2 plugin_requires + +returns list of packages given plugin requires for functionality. +This list is evaluated using C<< CPAN->use_inst >> method. + +=head2 distribution_object + +Get current distribution object. + +=head2 distribution + +=head2 distribution_info + +=head2 build_dir + +Simple delegatees for misc parameters derived from distribution + +=head2 is_xs + +Predicate to detect whether package contains XS. + +=head1 AUTHOR + +Branislav Zahradnik <barney@cpan.org> + +=cut + diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Plugin/Specfile.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Plugin/Specfile.pm new file mode 100644 index 0000000000..425c4bdb4e --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Plugin/Specfile.pm @@ -0,0 +1,263 @@ +=head1 NAME + +CPAN::Plugin::Specfile - Proof of concept implementation of a trivial CPAN::Plugin + +=head1 SYNOPSIS + + # once in the cpan shell + o conf plugin_list push CPAN::Plugin::Specfile + + # make permanent + o conf commit + + # any time in the cpan shell to write a spec file + test Acme::Meta + + # disable + # if it is the last in plugin_list: + o conf plugin_list pop + # otherwise, determine the index to splice: + o conf plugin_list + # and then use splice, e.g. to splice position 3: + o conf plugin_list splice 3 1 + +=head1 DESCRIPTION + +Implemented as a post-test hook, this plugin writes a specfile after +every successful test run. The content is also written to the +terminal. + +As a side effect, the timestamps of the written specfiles reflect the +linear order of all dependencies. + +B<WARNING:> This code is just a small demo how to use the plugin +system of the CPAN shell, not a full fledged spec file writer. Do not +expect new features in this plugin. + +=head2 OPTIONS + +The target directory to store the spec files in can be set using C<dir> +as in + + o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/specfiles-000042 + +The default directory for this is the +C<plugins/CPAN::Plugin::Specfile> directory in the I<cpan_home> +directory. + +=head1 AUTHOR + +Andreas Koenig <andk@cpan.org>, Branislav Zahradnik <barney@cpan.org> + +=cut + +package CPAN::Plugin::Specfile; + +our $VERSION = '0.02'; + +use File::Path; +use File::Spec; + +sub __accessor { + my ($class, $key) = @_; + no strict 'refs'; + *{$class . '::' . $key} = sub { + my $self = shift; + if (@_) { + $self->{$key} = shift; + } + return $self->{$key}; + }; +} +BEGIN { __PACKAGE__->__accessor($_) for qw(dir dir_default) } + +sub new { + my($class, @rest) = @_; + my $self = bless {}, $class; + while (my($arg,$val) = splice @rest, 0, 2) { + $self->$arg($val); + } + $self->dir_default(File::Spec->catdir($CPAN::Config->{cpan_home},"plugins",__PACKAGE__)); + $self; +} + +sub post_test { + my $self = shift; + my $distribution_object = shift; + my $distribution = $distribution_object->pretty_id; + unless ($CPAN::META->has_inst("CPAN::DistnameInfo")){ + $CPAN::Frontend->mydie("CPAN::DistnameInfo not installed; cannot continue"); + } + my $d = CPAN::Shell->expand("Distribution",$distribution) + or $CPAN::Frontend->mydie("Unknowns distribution '$distribution'\n"); + my $build_dir = $d->{build_dir} or $CPAN::Frontend->mydie("Distribution has not been built yet, cannot proceed"); + my %contains = map {($_ => undef)} $d->containsmods; + my @m; + my $width = 16; + my $header = sub { + my($header,$value) = @_; + push @m, sprintf("%-s:%*s%s\n", $header, $width-length($header), "", $value); + }; + my $dni = CPAN::DistnameInfo->new($distribution); + my $dist = $dni->dist; + my $summary = CPAN::Shell->_guess_manpage($d,\%contains,$dist); + $header->("Name", "perl-$dist"); + my $version = $dni->version; + $header->("Version", $version); + $header->("Release", "1%{?dist}"); +#Summary: Template processing system +#Group: Development/Libraries +#License: GPL+ or Artistic +#URL: http://www.template-toolkit.org/ +#Source0: http://search.cpan.org/CPAN/authors/id/A/AB/ABW/Template-Toolkit-%{version}.tar.gz +#Patch0: Template-2.22-SREZIC-01.patch +#BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + for my $h_tuple + ([Summary => $summary], + [Group => "Development/Libraries"], + [License =>], + [URL =>], + [BuildRoot => "%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)"], + [Requires => "perl(:MODULE_COMPAT_%(eval \"`%{__perl} -V:version`\"; echo \$version))"], + ) { + my($h,$v) = @$h_tuple; + $v = "unknown" unless defined $v; + $header->($h, $v); + } + $header->("Source0", sprintf( + "http://search.cpan.org/CPAN/authors/id/%s/%s/%s", + substr($distribution,0,1), + substr($distribution,0,2), + $distribution + )); + require POSIX; + my @xs = glob "$build_dir/*.xs"; # quick try + unless (@xs) { + require ExtUtils::Manifest; + my $manifest_file = "$build_dir/MANIFEST"; + my $manifest = ExtUtils::Manifest::maniread($manifest_file); + @xs = grep /\.xs$/, keys %$manifest; + } + if (! @xs) { + $header->('BuildArch', 'noarch'); + } + for my $k (sort keys %contains) { + my $m = CPAN::Shell->expand("Module",$k); + my $v = $contains{$k} = $m->cpan_version; + my $vspec = $v eq "undef" ? "" : " = $v"; + $header->("Provides", "perl($k)$vspec"); + } + if (my $prereq_pm = $d->{prereq_pm}) { + my %req; + for my $reqkey (keys %$prereq_pm) { + while (my($k,$v) = each %{$prereq_pm->{$reqkey}}) { + $req{$k} = $v; + } + } + if (-e "$build_dir/Build.PL" && ! exists $req{"Module::Build"}) { + $req{"Module::Build"} = 0; + } + for my $k (sort keys %req) { + next if $k eq "perl"; + my $v = $req{$k}; + my $vspec = defined $v && length $v && $v > 0 ? " >= $v" : ""; + $header->(BuildRequires => "perl($k)$vspec"); + next if $k =~ /^(Module::Build)$/; # MB is always only a + # BuildRequires; if we + # turn it into a + # Requires, then we + # would have to make it + # a BuildRequires + # everywhere we depend + # on *one* MB built + # module. + $header->(Requires => "perl($k)$vspec"); + } + } + push @m, "\n%define _use_internal_dependency_generator 0 +%define __find_requires %{nil} +%define __find_provides %{nil} +"; + push @m, "\n%description\n%{summary}.\n"; + push @m, "\n%prep\n%setup -q -n $dist-%{version}\n"; + if (-e "$build_dir/Build.PL") { + # see http://www.redhat.com/archives/rpm-list/2002-July/msg00110.html about RPM_BUILD_ROOT vs %{buildroot} + push @m, <<'EOF'; + +%build +%{__perl} Build.PL --installdirs=vendor --libdoc installvendorman3dir +./Build + +%install +rm -rf $RPM_BUILD_ROOT +./Build install destdir=$RPM_BUILD_ROOT create_packlist=0 +find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null \; +%{_fixperms} $RPM_BUILD_ROOT/* + +%check +./Build test +EOF + } elsif (-e "$build_dir/Makefile.PL") { + push @m, <<'EOF'; + +%build +%{__perl} Makefile.PL INSTALLDIRS=vendor +make %{?_smp_mflags} + +%install +rm -rf $RPM_BUILD_ROOT +make pure_install DESTDIR=$RPM_BUILD_ROOT +find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';' +find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null ';' +%{_fixperms} $RPM_BUILD_ROOT/* + +%check +make test +EOF + } else { + $CPAN::Frontend->mydie("'$distribution' has neither a Build.PL nor a Makefile.PL\n"); + } + push @m, "\n%clean\nrm -rf \$RPM_BUILD_ROOT\n"; + my $vendorlib = @xs ? "vendorarch" : "vendorlib"; + my $date = POSIX::strftime("%a %b %d %Y", gmtime); + my @doc = grep { -e "$build_dir/$_" } qw(README Changes); + my $exe_stanza = "\n"; + if (my $exe_files = $d->_exe_files) { + if (@$exe_files) { + $exe_stanza = "%{_mandir}/man1/*.1*\n"; + for my $e (@$exe_files) { + unless (CPAN->has_inst("File::Basename")) { + $CPAN::Frontend->mydie("File::Basename not installed, cannot continue"); + } + my $basename = File::Basename::basename($e); + $exe_stanza .= "/usr/bin/$basename\n"; + } + } + } + push @m, <<EOF; + +%files +%defattr(-,root,root,-) +%doc @doc +%{perl_$vendorlib}/* +%{_mandir}/man3/*.3* +$exe_stanza +%changelog +* $date <specfile\@specfile.cpan.org> - $version-1 +- autogenerated by CPAN::Plugin::Specfile() + +EOF + + my $ret = join "", @m; + $CPAN::Frontend->myprint($ret); + my $target_dir = $self->dir || $self->dir_default; + File::Path::mkpath($target_dir); + my $outfile = File::Spec->catfile($target_dir, "perl-$dist.spec"); + open my $specout, ">", $outfile + or $CPAN::Frontend->mydie("Could not open >$outfile: $!"); + print $specout $ret; + $CPAN::Frontend->myprint("Wrote $outfile"); + $ret; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Prompt.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Prompt.pm new file mode 100644 index 0000000000..7a4e2d81e1 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Prompt.pm @@ -0,0 +1,29 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::Prompt; +use overload '""' => "as_string"; +use vars qw($prompt); +use vars qw( + $VERSION +); +$VERSION = "5.5"; + + +$prompt = "cpan> "; +$CPAN::CurrentCommandId ||= 0; +sub new { + bless {}, shift; +} +sub as_string { + my $word = "cpan"; + unless ($CPAN::META->{LOCK}) { + $word = "nolock_cpan"; + } + if ($CPAN::Config->{commandnumber_in_prompt}) { + sprintf "$word\[%d]> ", $CPAN::CurrentCommandId; + } else { + "$word> "; + } +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Queue.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Queue.pm new file mode 100644 index 0000000000..259e47e05f --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Queue.pm @@ -0,0 +1,234 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +use strict; +package CPAN::Queue::Item; + +# CPAN::Queue::Item::new ; +sub new { + my($class,@attr) = @_; + my $self = bless { @attr }, $class; + return $self; +} + +sub as_string { + my($self) = @_; + $self->{qmod}; +} + +# r => requires, b => build_requires, c => commandline +sub reqtype { + my($self) = @_; + $self->{reqtype}; +} + +sub optional { + my($self) = @_; + $self->{optional}; +} + +package CPAN::Queue; + +# One use of the queue is to determine if we should or shouldn't +# announce the availability of a new CPAN module + +# Now we try to use it for dependency tracking. For that to happen +# we need to draw a dependency tree and do the leaves first. This can +# easily be reached by running CPAN.pm recursively, but we don't want +# to waste memory and run into deep recursion. So what we can do is +# this: + +# CPAN::Queue is the package where the queue is maintained. Dependencies +# often have high priority and must be brought to the head of the queue, +# possibly by jumping the queue if they are already there. My first code +# attempt tried to be extremely correct. Whenever a module needed +# immediate treatment, I either unshifted it to the front of the queue, +# or, if it was already in the queue, I spliced and let it bypass the +# others. This became a too correct model that made it impossible to put +# an item more than once into the queue. Why would you need that? Well, +# you need temporary duplicates as the manager of the queue is a loop +# that +# +# (1) looks at the first item in the queue without shifting it off +# +# (2) cares for the item +# +# (3) removes the item from the queue, *even if its agenda failed and +# even if the item isn't the first in the queue anymore* (that way +# protecting against never ending queues) +# +# So if an item has prerequisites, the installation fails now, but we +# want to retry later. That's easy if we have it twice in the queue. +# +# I also expect insane dependency situations where an item gets more +# than two lives in the queue. Simplest example is triggered by 'install +# Foo Foo Foo'. People make this kind of mistakes and I don't want to +# get in the way. I wanted the queue manager to be a dumb servant, not +# one that knows everything. +# +# Who would I tell in this model that the user wants to be asked before +# processing? I can't attach that information to the module object, +# because not modules are installed but distributions. So I'd have to +# tell the distribution object that it should ask the user before +# processing. Where would the question be triggered then? Most probably +# in CPAN::Distribution::rematein. + +use vars qw{ @All $VERSION }; +$VERSION = "5.5003"; + +# CPAN::Queue::queue_item ; +sub queue_item { + my($class,@attr) = @_; + my $item = "$class\::Item"->new(@attr); + $class->qpush($item); + return 1; +} + +# CPAN::Queue::qpush ; +sub qpush { + my($class,$obj) = @_; + push @All, $obj; + CPAN->debug(sprintf("in new All[%s]", + join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All), + )) if $CPAN::DEBUG; +} + +# CPAN::Queue::first ; +sub first { + my $obj = $All[0]; + $obj; +} + +# CPAN::Queue::delete_first ; +sub delete_first { + my($class,$what) = @_; + my $i; + for my $i (0..$#All) { + if ( $All[$i]->{qmod} eq $what ) { + splice @All, $i, 1; + last; + } + } + CPAN->debug(sprintf("after delete_first mod[%s] All[%s]", + $what, + join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) + )) if $CPAN::DEBUG; +} + +# CPAN::Queue::jumpqueue ; +sub jumpqueue { + my $class = shift; + my @what = @_; + CPAN->debug(sprintf("before jumpqueue All[%s] what[%s]", + join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All), + join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @what), + )) if $CPAN::DEBUG; + unless (defined $what[0]{reqtype}) { + # apparently it was not the Shell that sent us this enquiry, + # treat it as commandline + $what[0]{reqtype} = "c"; + } + my $inherit_reqtype = $what[0]{reqtype} =~ /^(c|r)$/ ? "r" : "b"; + WHAT: for my $what_tuple (@what) { + my($qmod,$reqtype,$optional) = @$what_tuple{qw(qmod reqtype optional)}; + if ($reqtype eq "r" + && + $inherit_reqtype eq "b" + ) { + $reqtype = "b"; + } + my $jumped = 0; + for (my $i=0; $i<$#All;$i++) { #prevent deep recursion + if ($All[$i]{qmod} eq $qmod) { + $jumped++; + } + } + # high jumped values are normal for popular modules when + # dealing with large bundles: XML::Simple, + # namespace::autoclean, UNIVERSAL::require + CPAN->debug("qmod[$qmod]jumped[$jumped]") if $CPAN::DEBUG; + my $obj = "$class\::Item"->new( + qmod => $qmod, + reqtype => $reqtype, + optional => !! $optional, + ); + unshift @All, $obj; + } + CPAN->debug(sprintf("after jumpqueue All[%s]", + join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) + )) if $CPAN::DEBUG; +} + +# CPAN::Queue::exists ; +sub exists { + my($self,$what) = @_; + my @all = map { $_->{qmod} } @All; + my $exists = grep { $_->{qmod} eq $what } @All; + # warn "in exists what[$what] all[@all] exists[$exists]"; + $exists; +} + +# CPAN::Queue::delete ; +sub delete { + my($self,$mod) = @_; + @All = grep { $_->{qmod} ne $mod } @All; + CPAN->debug(sprintf("after delete mod[%s] All[%s]", + $mod, + join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) + )) if $CPAN::DEBUG; +} + +# CPAN::Queue::nullify_queue ; +sub nullify_queue { + @All = (); +} + +# CPAN::Queue::size ; +sub size { + return scalar @All; +} + +sub reqtype_of { + my($self,$mod) = @_; + my $best = ""; + for my $item (grep { $_->{qmod} eq $mod } @All) { + my $c = $item->{reqtype}; + if ($c eq "c") { + $best = $c; + last; + } elsif ($c eq "r") { + $best = $c; + } elsif ($c eq "b") { + if ($best eq "") { + $best = $c; + } + } else { + die "Panic: in reqtype_of: reqtype[$c] seen, should never happen"; + } + } + return $best; +} + +sub iterator { + my $i = 0; + return sub { + until ($All[$i] || $i > $#All) { + $i++; + } + return if $i > $#All; + return $All[$i++] + }; +} + +1; + +__END__ + +=head1 NAME + +CPAN::Queue - internal queue support for CPAN.pm + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Shell.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Shell.pm new file mode 100644 index 0000000000..4140fb8af2 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Shell.pm @@ -0,0 +1,2072 @@ +package CPAN::Shell; +use strict; + +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: + +use vars qw( + $ADVANCED_QUERY + $AUTOLOAD + $COLOR_REGISTERED + $Help + $autoload_recursion + $reload + @ISA + @relo + $VERSION + ); +@relo = ( + "CPAN.pm", + "CPAN/Author.pm", + "CPAN/CacheMgr.pm", + "CPAN/Complete.pm", + "CPAN/Debug.pm", + "CPAN/DeferredCode.pm", + "CPAN/Distribution.pm", + "CPAN/Distroprefs.pm", + "CPAN/Distrostatus.pm", + "CPAN/Exception/RecursiveDependency.pm", + "CPAN/Exception/yaml_not_installed.pm", + "CPAN/FirstTime.pm", + "CPAN/FTP.pm", + "CPAN/FTP/netrc.pm", + "CPAN/HandleConfig.pm", + "CPAN/Index.pm", + "CPAN/InfoObj.pm", + "CPAN/Kwalify.pm", + "CPAN/LWP/UserAgent.pm", + "CPAN/Module.pm", + "CPAN/Prompt.pm", + "CPAN/Queue.pm", + "CPAN/Reporter/Config.pm", + "CPAN/Reporter/History.pm", + "CPAN/Reporter/PrereqCheck.pm", + "CPAN/Reporter.pm", + "CPAN/Shell.pm", + "CPAN/SQLite.pm", + "CPAN/Tarzip.pm", + "CPAN/Version.pm", + ); +$VERSION = "5.5009"; +# record the initial timestamp for reload. +$reload = { map {$INC{$_} ? ($_,(stat $INC{$_})[9]) : ()} @relo }; +@CPAN::Shell::ISA = qw(CPAN::Debug); +use Cwd qw(chdir); +use Carp (); +$COLOR_REGISTERED ||= 0; +$Help = { + '?' => \"help", + '!' => "eval the rest of the line as perl", + a => "whois author", + autobundle => "write inventory into a bundle file", + b => "info about bundle", + bye => \"quit", + clean => "clean up a distribution's build directory", + # cvs_import + d => "info about a distribution", + # dump + exit => \"quit", + failed => "list all failed actions within current session", + fforce => "redo a command from scratch", + force => "redo a command", + get => "download a distribution", + h => \"help", + help => "overview over commands; 'help ...' explains specific commands", + hosts => "statistics about recently used hosts", + i => "info about authors/bundles/distributions/modules", + install => "install a distribution", + install_tested => "install all distributions tested OK", + is_tested => "list all distributions tested OK", + look => "open a subshell in a distribution's directory", + ls => "list distributions matching a fileglob", + m => "info about a module", + make => "make/build a distribution", + mkmyconfig => "write current config into a CPAN/MyConfig.pm file", + notest => "run a (usually install) command but leave out the test phase", + o => "'o conf ...' for config stuff; 'o debug ...' for debugging", + perldoc => "try to get a manpage for a module", + q => \"quit", + quit => "leave the cpan shell", + r => "review upgradable modules", + readme => "display the README of a distro with a pager", + recent => "show recent uploads to the CPAN", + # recompile + reload => "'reload cpan' or 'reload index'", + report => "test a distribution and send a test report to cpantesters", + reports => "info about reported tests from cpantesters", + # scripts + # smoke + test => "test a distribution", + u => "display uninstalled modules", + upgrade => "combine 'r' command with immediate installation", + }; +{ + $autoload_recursion ||= 0; + + #-> sub CPAN::Shell::AUTOLOAD ; + sub AUTOLOAD { ## no critic + $autoload_recursion++; + my($l) = $AUTOLOAD; + my $class = shift(@_); + # warn "autoload[$l] class[$class]"; + $l =~ s/.*:://; + if ($CPAN::Signal) { + warn "Refusing to autoload '$l' while signal pending"; + $autoload_recursion--; + return; + } + if ($autoload_recursion > 1) { + my $fullcommand = join " ", map { "'$_'" } $l, @_; + warn "Refusing to autoload $fullcommand in recursion\n"; + $autoload_recursion--; + return; + } + if ($l =~ /^w/) { + # XXX needs to be reconsidered + if ($CPAN::META->has_inst('CPAN::WAIT')) { + CPAN::WAIT->$l(@_); + } else { + $CPAN::Frontend->mywarn(qq{ +Commands starting with "w" require CPAN::WAIT to be installed. +Please consider installing CPAN::WAIT to use the fulltext index. +For this you just need to type + install CPAN::WAIT +}); + } + } else { + $CPAN::Frontend->mywarn(qq{Unknown shell command '$l'. }. + qq{Type ? for help. +}); + } + $autoload_recursion--; + } +} + + +#-> sub CPAN::Shell::h ; +sub h { + my($class,$about) = @_; + if (defined $about) { + my $help; + if (exists $Help->{$about}) { + if (ref $Help->{$about}) { # aliases + $about = ${$Help->{$about}}; + } + $help = $Help->{$about}; + } else { + $help = "No help available"; + } + $CPAN::Frontend->myprint("$about\: $help\n"); + } else { + my $filler = " " x (80 - 28 - length($CPAN::VERSION)); + $CPAN::Frontend->myprint(qq{ +Display Information $filler (ver $CPAN::VERSION) + command argument description + a,b,d,m WORD or /REGEXP/ about authors, bundles, distributions, modules + i WORD or /REGEXP/ about any of the above + ls AUTHOR or GLOB about files in the author's directory + (with WORD being a module, bundle or author name or a distribution + name of the form AUTHOR/DISTRIBUTION) + +Download, Test, Make, Install... + get download clean make clean + make make (implies get) look open subshell in dist directory + test make test (implies make) readme display these README files + install make install (implies test) perldoc display POD documentation + +Upgrade installed modules + r WORDs or /REGEXP/ or NONE report updates for some/matching/all + upgrade WORDs or /REGEXP/ or NONE upgrade some/matching/all modules + +Pragmas + force CMD try hard to do command fforce CMD try harder + notest CMD skip testing + +Other + h,? display this menu ! perl-code eval a perl command + o conf [opt] set and query options q quit the cpan shell + reload cpan load CPAN.pm again reload index load newer indices + autobundle Snapshot recent latest CPAN uploads}); +} +} + +*help = \&h; + +#-> sub CPAN::Shell::a ; +sub a { + my($self,@arg) = @_; + # authors are always UPPERCASE + for (@arg) { + $_ = uc $_ unless /=/; + } + $CPAN::Frontend->myprint($self->format_result('Author',@arg)); +} + +#-> sub CPAN::Shell::globls ; +sub globls { + my($self,$s,$pragmas) = @_; + # ls is really very different, but we had it once as an ordinary + # command in the Shell (up to rev. 321) and we could not handle + # force well then + my(@accept,@preexpand); + if ($s =~ /[\*\?\/]/) { + if ($CPAN::META->has_inst("Text::Glob")) { + if (my($au,$pathglob) = $s =~ m|(.*?)/(.*)|) { + my $rau = Text::Glob::glob_to_regex(uc $au); + CPAN::Shell->debug("au[$au]pathglob[$pathglob]rau[$rau]") + if $CPAN::DEBUG; + push @preexpand, map { $_->id . "/" . $pathglob } + CPAN::Shell->expand_by_method('CPAN::Author',['id'],"/$rau/"); + } else { + my $rau = Text::Glob::glob_to_regex(uc $s); + push @preexpand, map { $_->id } + CPAN::Shell->expand_by_method('CPAN::Author', + ['id'], + "/$rau/"); + } + } else { + $CPAN::Frontend->mydie("Text::Glob not installed, cannot proceed"); + } + } else { + push @preexpand, uc $s; + } + for (@preexpand) { + unless (/^[A-Z0-9\-]+(\/|$)/i) { + $CPAN::Frontend->mywarn("ls command rejects argument $_: not an author\n"); + next; + } + push @accept, $_; + } + my $silent = @accept>1; + my $last_alpha = ""; + my @results; + for my $a (@accept) { + my($author,$pathglob); + if ($a =~ m|(.*?)/(.*)|) { + my $a2 = $1; + $pathglob = $2; + $author = CPAN::Shell->expand_by_method('CPAN::Author', + ['id'], + $a2) + or $CPAN::Frontend->mydie("No author found for $a2\n"); + } else { + $author = CPAN::Shell->expand_by_method('CPAN::Author', + ['id'], + $a) + or $CPAN::Frontend->mydie("No author found for $a\n"); + } + if ($silent) { + my $alpha = substr $author->id, 0, 1; + my $ad; + if ($alpha eq $last_alpha) { + $ad = ""; + } else { + $ad = "[$alpha]"; + $last_alpha = $alpha; + } + $CPAN::Frontend->myprint($ad); + } + for my $pragma (@$pragmas) { + if ($author->can($pragma)) { + $author->$pragma(); + } + } + CPAN->debug("author[$author]pathglob[$pathglob]silent[$silent]") if $CPAN::DEBUG; + push @results, $author->ls($pathglob,$silent); # silent if + # more than one + # author + for my $pragma (@$pragmas) { + my $unpragma = "un$pragma"; + if ($author->can($unpragma)) { + $author->$unpragma(); + } + } + } + @results; +} + +#-> sub CPAN::Shell::local_bundles ; +sub local_bundles { + my($self,@which) = @_; + my($incdir,$bdir,$dh); + foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) { + my @bbase = "Bundle"; + while (my $bbase = shift @bbase) { + $bdir = File::Spec->catdir($incdir,split /::/, $bbase); + CPAN->debug("bdir[$bdir]\@bbase[@bbase]") if $CPAN::DEBUG; + if ($dh = DirHandle->new($bdir)) { # may fail + my($entry); + for $entry ($dh->read) { + next if $entry =~ /^\./; + next unless $entry =~ /^\w+(\.pm)?(?!\n)\Z/; + if (-d File::Spec->catdir($bdir,$entry)) { + push @bbase, "$bbase\::$entry"; + } else { + next unless $entry =~ s/\.pm(?!\n)\Z//; + $CPAN::META->instance('CPAN::Bundle',"$bbase\::$entry"); + } + } + } + } + } +} + +#-> sub CPAN::Shell::b ; +sub b { + my($self,@which) = @_; + CPAN->debug("which[@which]") if $CPAN::DEBUG; + $self->local_bundles; + $CPAN::Frontend->myprint($self->format_result('Bundle',@which)); +} + +#-> sub CPAN::Shell::d ; +sub d { $CPAN::Frontend->myprint(shift->format_result('Distribution',@_));} + +#-> sub CPAN::Shell::m ; +sub m { # emacs confused here }; sub mimimimimi { # emacs in sync here + my $self = shift; + my @m = @_; + for (@m) { + if (m|(?:\w+/)*\w+\.pm$|) { # same regexp in expandany + s/.pm$//; + s|/|::|g; + } + } + $CPAN::Frontend->myprint($self->format_result('Module',@m)); +} + +#-> sub CPAN::Shell::i ; +sub i { + my($self) = shift; + my(@args) = @_; + @args = '/./' unless @args; + my(@result); + for my $type (qw/Bundle Distribution Module/) { + push @result, $self->expand($type,@args); + } + # Authors are always uppercase. + push @result, $self->expand("Author", map { uc $_ } @args); + + my $result = @result == 1 ? + $result[0]->as_string : + @result == 0 ? + "No objects found of any type for argument @args\n" : + join("", + (map {$_->as_glimpse} @result), + scalar @result, " items found\n", + ); + $CPAN::Frontend->myprint($result); +} + +#-> sub CPAN::Shell::o ; + +# CPAN::Shell::o and CPAN::HandleConfig::edit are closely related. 'o +# conf' calls through to CPAN::HandleConfig::edit. 'o conf' should +# probably have been called 'set' and 'o debug' maybe 'set debug' or +# 'debug'; 'o conf ARGS' calls ->edit in CPAN/HandleConfig.pm +sub o { + my($self,$o_type,@o_what) = @_; + $o_type ||= ""; + CPAN->debug("o_type[$o_type] o_what[".join(" | ",@o_what)."]\n"); + if ($o_type eq 'conf') { + my($cfilter); + ($cfilter) = $o_what[0] =~ m|^/(.*)/$| if @o_what; + if (!@o_what or $cfilter) { # print all things, "o conf" + $cfilter ||= ""; + my $qrfilter = eval 'qr/$cfilter/'; + if ($@) { + $CPAN::Frontend->mydie("Cannot parse commandline: $@"); + } + my($k,$v); + my $configpm = CPAN::HandleConfig->require_myconfig_or_config; + $CPAN::Frontend->myprint("\$CPAN::Config options from $configpm\:\n"); + for $k (sort keys %CPAN::HandleConfig::can) { + next unless $k =~ /$qrfilter/; + $v = $CPAN::HandleConfig::can{$k}; + $CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v); + } + $CPAN::Frontend->myprint("\n"); + for $k (sort keys %CPAN::HandleConfig::keys) { + next unless $k =~ /$qrfilter/; + CPAN::HandleConfig->prettyprint($k); + } + $CPAN::Frontend->myprint("\n"); + } else { + if (CPAN::HandleConfig->edit(@o_what)) { + } else { + $CPAN::Frontend->myprint(qq{Type 'o conf' to view all configuration }. + qq{items\n\n}); + } + } + } elsif ($o_type eq 'debug') { + my(%valid); + @o_what = () if defined $o_what[0] && $o_what[0] =~ /help/i; + if (@o_what) { + while (@o_what) { + my($what) = shift @o_what; + if ($what =~ s/^-// && exists $CPAN::DEBUG{$what}) { + $CPAN::DEBUG &= $CPAN::DEBUG ^ $CPAN::DEBUG{$what}; + next; + } + if ( exists $CPAN::DEBUG{$what} ) { + $CPAN::DEBUG |= $CPAN::DEBUG{$what}; + } elsif ($what =~ /^\d/) { + $CPAN::DEBUG = $what; + } elsif (lc $what eq 'all') { + my($max) = 0; + for (values %CPAN::DEBUG) { + $max += $_; + } + $CPAN::DEBUG = $max; + } else { + my($known) = 0; + for (keys %CPAN::DEBUG) { + next unless lc($_) eq lc($what); + $CPAN::DEBUG |= $CPAN::DEBUG{$_}; + $known = 1; + } + $CPAN::Frontend->myprint("unknown argument [$what]\n") + unless $known; + } + } + } else { + my $raw = "Valid options for debug are ". + join(", ",sort(keys %CPAN::DEBUG), 'all'). + qq{ or a number. Completion works on the options. }. + qq{Case is ignored.}; + require Text::Wrap; + $CPAN::Frontend->myprint(Text::Wrap::fill("","",$raw)); + $CPAN::Frontend->myprint("\n\n"); + } + if ($CPAN::DEBUG) { + $CPAN::Frontend->myprint("Options set for debugging ($CPAN::DEBUG):\n"); + my($k,$v); + for $k (sort {$CPAN::DEBUG{$a} <=> $CPAN::DEBUG{$b}} keys %CPAN::DEBUG) { + $v = $CPAN::DEBUG{$k}; + $CPAN::Frontend->myprint(sprintf " %-14s(%s)\n", $k, $v) + if $v & $CPAN::DEBUG; + } + } else { + $CPAN::Frontend->myprint("Debugging turned off completely.\n"); + } + } else { + $CPAN::Frontend->myprint(qq{ +Known options: + conf set or get configuration variables + debug set or get debugging options +}); + } +} + +# CPAN::Shell::paintdots_onreload +sub paintdots_onreload { + my($ref) = shift; + sub { + if ( $_[0] =~ /[Ss]ubroutine ([\w:]+) redefined/ ) { + my($subr) = $1; + ++$$ref; + local($|) = 1; + # $CPAN::Frontend->myprint(".($subr)"); + $CPAN::Frontend->myprint("."); + if ($subr =~ /\bshell\b/i) { + # warn "debug[$_[0]]"; + + # It would be nice if we could detect that a + # subroutine has actually changed, but for now we + # practically always set the GOTOSHELL global + + $CPAN::GOTOSHELL=1; + } + return; + } + warn @_; + }; +} + +#-> sub CPAN::Shell::hosts ; +sub hosts { + my($self) = @_; + my $fullstats = CPAN::FTP->_ftp_statistics(); + my $history = $fullstats->{history} || []; + my %S; # statistics + while (my $last = pop @$history) { + my $attempts = $last->{attempts} or next; + my $start; + if (@$attempts) { + $start = $attempts->[-1]{start}; + if ($#$attempts > 0) { + for my $i (0..$#$attempts-1) { + my $url = $attempts->[$i]{url} or next; + $S{no}{$url}++; + } + } + } else { + $start = $last->{start}; + } + next unless $last->{thesiteurl}; # C-C? bad filenames? + $S{start} = $start; + $S{end} ||= $last->{end}; + my $dltime = $last->{end} - $start; + my $dlsize = $last->{filesize} || 0; + my $url = ref $last->{thesiteurl} ? $last->{thesiteurl}->text : $last->{thesiteurl}; + my $s = $S{ok}{$url} ||= {}; + $s->{n}++; + $s->{dlsize} ||= 0; + $s->{dlsize} += $dlsize/1024; + $s->{dltime} ||= 0; + $s->{dltime} += $dltime; + } + my $res; + for my $url (sort keys %{$S{ok}}) { + next if $S{ok}{$url}{dltime} == 0; # div by zero + push @{$res->{ok}}, [@{$S{ok}{$url}}{qw(n dlsize dltime)}, + $S{ok}{$url}{dlsize}/$S{ok}{$url}{dltime}, + $url, + ]; + } + for my $url (sort keys %{$S{no}}) { + push @{$res->{no}}, [$S{no}{$url}, + $url, + ]; + } + my $R = ""; # report + if ($S{start} && $S{end}) { + $R .= sprintf "Log starts: %s\n", $S{start} ? scalar(localtime $S{start}) : "unknown"; + $R .= sprintf "Log ends : %s\n", $S{end} ? scalar(localtime $S{end}) : "unknown"; + } + if ($res->{ok} && @{$res->{ok}}) { + $R .= sprintf "\nSuccessful downloads: + N kB secs kB/s url\n"; + my $i = 20; + for (sort { $b->[3] <=> $a->[3] } @{$res->{ok}}) { + $R .= sprintf "%4d %8d %5d %9.1f %s\n", @$_; + last if --$i<=0; + } + } + if ($res->{no} && @{$res->{no}}) { + $R .= sprintf "\nUnsuccessful downloads:\n"; + my $i = 20; + for (sort { $b->[0] <=> $a->[0] } @{$res->{no}}) { + $R .= sprintf "%4d %s\n", @$_; + last if --$i<=0; + } + } + $CPAN::Frontend->myprint($R); +} + +# here is where 'reload cpan' is done +#-> sub CPAN::Shell::reload ; +sub reload { + my($self,$command,@arg) = @_; + $command ||= ""; + $self->debug("self[$self]command[$command]arg[@arg]") if $CPAN::DEBUG; + if ($command =~ /^cpan$/i) { + my $redef = 0; + chdir "$CPAN::iCwd" if $CPAN::iCwd; # may fail + my $failed; + MFILE: for my $f (@relo) { + next unless exists $INC{$f}; + my $p = $f; + $p =~ s/\.pm$//; + $p =~ s|/|::|g; + $CPAN::Frontend->myprint("($p"); + local($SIG{__WARN__}) = paintdots_onreload(\$redef); + $self->_reload_this($f) or $failed++; + my $v = eval "$p\::->VERSION"; + $CPAN::Frontend->myprint("v$v)"); + } + $CPAN::Frontend->myprint("\n$redef subroutines redefined\n"); + if ($failed) { + my $errors = $failed == 1 ? "error" : "errors"; + $CPAN::Frontend->mywarn("\n$failed $errors during reload. You better quit ". + "this session.\n"); + } + } elsif ($command =~ /^index$/i) { + CPAN::Index->force_reload; + } else { + $CPAN::Frontend->myprint(qq{cpan re-evals the CPAN modules +index re-reads the index files\n}); + } +} + +# reload means only load again what we have loaded before +#-> sub CPAN::Shell::_reload_this ; +sub _reload_this { + my($self,$f,$args) = @_; + CPAN->debug("f[$f]") if $CPAN::DEBUG; + return 1 unless $INC{$f}; # we never loaded this, so we do not + # reload but say OK + my $pwd = CPAN::anycwd(); + CPAN->debug("pwd[$pwd]") if $CPAN::DEBUG; + my($file); + for my $inc (@INC) { + $file = File::Spec->catfile($inc,split /\//, $f); + last if -f $file; + $file = ""; + } + CPAN->debug("file[$file]") if $CPAN::DEBUG; + my @inc = @INC; + unless ($file && -f $file) { + # this thingy is not in the INC path, maybe CPAN/MyConfig.pm? + $file = $INC{$f}; + unless (CPAN->has_inst("File::Basename")) { + @inc = File::Basename::dirname($file); + } else { + # do we ever need this? + @inc = substr($file,0,-length($f)-1); # bring in back to me! + } + } + CPAN->debug("file[$file]inc[@inc]") if $CPAN::DEBUG; + unless (-f $file) { + $CPAN::Frontend->mywarn("Found no file to reload for '$f'\n"); + return; + } + my $mtime = (stat $file)[9]; + $reload->{$f} ||= -1; + my $must_reload = $mtime != $reload->{$f}; + $args ||= {}; + $must_reload ||= $args->{reloforce}; # o conf defaults needs this + if ($must_reload) { + my $fh = FileHandle->new($file) or + $CPAN::Frontend->mydie("Could not open $file: $!"); + my $content; + { + local($/); + local $^W = 1; + $content = <$fh>; + } + CPAN->debug(sprintf("reload file[%s] content[%s...]",$file,substr($content,0,128))) + if $CPAN::DEBUG; + my $includefile; + if ($includefile = $INC{$f} and -e $includefile) { + $f = $includefile; + } + delete $INC{$f}; + local @INC = @inc; + eval "require '$f'"; + if ($@) { + warn $@; + return; + } + $reload->{$f} = $mtime; + } else { + $CPAN::Frontend->myprint("__unchanged__"); + } + return 1; +} + +#-> sub CPAN::Shell::mkmyconfig ; +sub mkmyconfig { + my($self) = @_; + if ( my $configpm = $INC{'CPAN/MyConfig.pm'} ) { + $CPAN::Frontend->myprint( + "CPAN::MyConfig already exists as $configpm.\n" . + "Running configuration again...\n" + ); + require CPAN::FirstTime; + CPAN::FirstTime::init($configpm); + } + else { + # force some missing values to be filled in with defaults + delete $CPAN::Config->{$_} + for qw/build_dir cpan_home keep_source_where histfile/; + CPAN::HandleConfig->load( make_myconfig => 1 ); + } +} + +#-> sub CPAN::Shell::_binary_extensions ; +sub _binary_extensions { + my($self) = shift @_; + my(@result,$module,%seen,%need,$headerdone); + for $module ($self->expand('Module','/./')) { + my $file = $module->cpan_file; + next if $file eq "N/A"; + next if $file =~ /^Contact Author/; + my $dist = $CPAN::META->instance('CPAN::Distribution',$file); + next if $dist->isa_perl; + next unless $module->xs_file; + local($|) = 1; + $CPAN::Frontend->myprint("."); + push @result, $module; + } +# print join " | ", @result; + $CPAN::Frontend->myprint("\n"); + return @result; +} + +#-> sub CPAN::Shell::recompile ; +sub recompile { + my($self) = shift @_; + my($module,@module,$cpan_file,%dist); + @module = $self->_binary_extensions(); + for $module (@module) { # we force now and compile later, so we + # don't do it twice + $cpan_file = $module->cpan_file; + my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); + $pack->force; + $dist{$cpan_file}++; + } + for $cpan_file (sort keys %dist) { + $CPAN::Frontend->myprint(" CPAN: Recompiling $cpan_file\n\n"); + my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); + $pack->install; + $CPAN::Signal = 0; # it's tempting to reset Signal, so we can + # stop a package from recompiling, + # e.g. IO-1.12 when we have perl5.003_10 + } +} + +#-> sub CPAN::Shell::scripts ; +sub scripts { + my($self, $arg) = @_; + $CPAN::Frontend->mywarn(">>>> experimental command, currently unsupported <<<<\n\n"); + + for my $req (qw( HTML::LinkExtor Sort::Versions List::Util )) { + unless ($CPAN::META->has_inst($req)) { + $CPAN::Frontend->mywarn(" $req not available\n"); + } + } + my $p = HTML::LinkExtor->new(); + my $indexfile = "/home/ftp/pub/PAUSE/scripts/new/index.html"; + unless (-f $indexfile) { + $CPAN::Frontend->mydie("found no indexfile[$indexfile]\n"); + } + $p->parse_file($indexfile); + my @hrefs; + my $qrarg; + if ($arg =~ s|^/(.+)/$|$1|) { + $qrarg = eval 'qr/$arg/'; # hide construct from 5.004 + } + for my $l ($p->links) { + my $tag = shift @$l; + next unless $tag eq "a"; + my %att = @$l; + my $href = $att{href}; + next unless $href =~ s|^\.\./authors/id/./../||; + if ($arg) { + if ($qrarg) { + if ($href =~ $qrarg) { + push @hrefs, $href; + } + } else { + if ($href =~ /\Q$arg\E/) { + push @hrefs, $href; + } + } + } else { + push @hrefs, $href; + } + } + # now filter for the latest version if there is more than one of a name + my %stems; + for (sort @hrefs) { + my $href = $_; + s/-v?\d.*//; + my $stem = $_; + $stems{$stem} ||= []; + push @{$stems{$stem}}, $href; + } + for (sort keys %stems) { + my $highest; + if (@{$stems{$_}} > 1) { + $highest = List::Util::reduce { + Sort::Versions::versioncmp($a,$b) > 0 ? $a : $b + } @{$stems{$_}}; + } else { + $highest = $stems{$_}[0]; + } + $CPAN::Frontend->myprint("$highest\n"); + } +} + +sub _guess_manpage { + my($self,$d,$contains,$dist) = @_; + $dist =~ s/-/::/g; + my $module; + if (exists $contains->{$dist}) { + $module = $dist; + } elsif (1 == keys %$contains) { + ($module) = keys %$contains; + } + my $manpage; + if ($module) { + my $m = $self->expand("Module",$module); + $m->as_string; # called for side-effects, shame + $manpage = $m->{MANPAGE}; + } else { + $manpage = "unknown"; + } + return $manpage; +} + +#-> sub CPAN::Shell::_specfile ; +sub _specfile { + die "CPAN::Shell::_specfile() has been moved to CPAN::Plugin::Specfile::post_test()"; +} + +#-> sub CPAN::Shell::report ; +sub report { + my($self,@args) = @_; + unless ($CPAN::META->has_inst("CPAN::Reporter")) { + $CPAN::Frontend->mydie("CPAN::Reporter not installed; cannot continue"); + } + local $CPAN::Config->{test_report} = 1; + $self->force("test",@args); # force is there so that the test be + # re-run (as documented) +} + +# compare with is_tested +#-> sub CPAN::Shell::install_tested +sub install_tested { + my($self,@some) = @_; + $CPAN::Frontend->mywarn("install_tested() must not be called with arguments.\n"), + return if @some; + CPAN::Index->reload; + + for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { + my $yaml = "$b.yml"; + unless (-f $yaml) { + $CPAN::Frontend->mywarn("No YAML file for $b available, skipping\n"); + next; + } + my $yaml_content = CPAN->_yaml_loadfile($yaml); + my $id = $yaml_content->[0]{distribution}{ID}; + unless ($id) { + $CPAN::Frontend->mywarn("No ID found in '$yaml', skipping\n"); + next; + } + my $do = CPAN::Shell->expandany($id); + unless ($do) { + $CPAN::Frontend->mywarn("Could not expand ID '$id', skipping\n"); + next; + } + unless ($do->{build_dir}) { + $CPAN::Frontend->mywarn("Distro '$id' has no build_dir, skipping\n"); + next; + } + unless ($do->{build_dir} eq $b) { + $CPAN::Frontend->mywarn("Distro '$id' has build_dir '$do->{build_dir}' but expected '$b', skipping\n"); + next; + } + push @some, $do; + } + + $CPAN::Frontend->mywarn("No tested distributions found.\n"), + return unless @some; + + @some = grep { $_->{make_test} && ! $_->{make_test}->failed } @some; + $CPAN::Frontend->mywarn("No distributions tested with this build of perl found.\n"), + return unless @some; + + # @some = grep { not $_->uptodate } @some; + # $CPAN::Frontend->mywarn("No non-uptodate distributions tested with this build of perl found.\n"), + # return unless @some; + + CPAN->debug("some[@some]"); + for my $d (@some) { + my $id = $d->can("pretty_id") ? $d->pretty_id : $d->id; + $CPAN::Frontend->myprint("install_tested: Running for $id\n"); + $CPAN::Frontend->mysleep(1); + $self->install($d); + } +} + +#-> sub CPAN::Shell::upgrade ; +sub upgrade { + my($self,@args) = @_; + $self->install($self->r(@args)); +} + +#-> sub CPAN::Shell::_u_r_common ; +sub _u_r_common { + my($self) = shift @_; + my($what) = shift @_; + CPAN->debug("self[$self] what[$what] args[@_]") if $CPAN::DEBUG; + Carp::croak "Usage: \$obj->_u_r_common(a|r|u)" unless + $what && $what =~ /^[aru]$/; + my(@args) = @_; + @args = '/./' unless @args; + my(@result,$module,%seen,%need,$headerdone, + $version_undefs,$version_zeroes, + @version_undefs,@version_zeroes); + $version_undefs = $version_zeroes = 0; + my $sprintf = "%s%-25s%s %9s %9s %s\n"; + my @expand = $self->expand('Module',@args); + if ($CPAN::DEBUG) { # Looks like noise to me, was very useful for debugging + # for metadata cache + my $expand = scalar @expand; + $CPAN::Frontend->myprint(sprintf "%d matches in the database, time[%d]\n", $expand, time); + } + my @sexpand; + if ($] < 5.008) { + # hard to believe that the more complex sorting can lead to + # stack curruptions on older perl + @sexpand = sort {$a->id cmp $b->id} @expand; + } else { + @sexpand = map { + $_->[1] + } sort { + $b->[0] <=> $a->[0] + || + $a->[1]{ID} cmp $b->[1]{ID}, + } map { + [$_->_is_representative_module, + $_ + ] + } @expand; + } + if ($CPAN::DEBUG) { + $CPAN::Frontend->myprint(sprintf "sorted at time[%d]\n", time); + sleep 1; + } + MODULE: for $module (@sexpand) { + my $file = $module->cpan_file; + next MODULE unless defined $file; # ?? + $file =~ s!^./../!!; + my($latest) = $module->cpan_version; + my($inst_file) = $module->inst_file; + CPAN->debug("file[$file]latest[$latest]") if $CPAN::DEBUG; + my($have); + return if $CPAN::Signal; + my($next_MODULE); + eval { # version.pm involved! + if ($inst_file) { + if ($what eq "a") { + $have = $module->inst_version; + } elsif ($what eq "r") { + $have = $module->inst_version; + local($^W) = 0; + if ($have eq "undef") { + $version_undefs++; + push @version_undefs, $module->as_glimpse; + } elsif (CPAN::Version->vcmp($have,0)==0) { + $version_zeroes++; + push @version_zeroes, $module->as_glimpse; + } + ++$next_MODULE unless CPAN::Version->vgt($latest, $have); + # to be pedantic we should probably say: + # && !($have eq "undef" && $latest ne "undef" && $latest gt ""); + # to catch the case where CPAN has a version 0 and we have a version undef + } elsif ($what eq "u") { + ++$next_MODULE; + } + } else { + if ($what eq "a") { + ++$next_MODULE; + } elsif ($what eq "r") { + ++$next_MODULE; + } elsif ($what eq "u") { + $have = "-"; + } + } + }; + next MODULE if $next_MODULE; + if ($@) { + $CPAN::Frontend->mywarn + (sprintf("Error while comparing cpan/installed versions of '%s': +INST_FILE: %s +INST_VERSION: %s %s +CPAN_VERSION: %s %s +", + $module->id, + $inst_file || "", + (defined $have ? $have : "[UNDEFINED]"), + (ref $have ? ref $have : ""), + $latest, + (ref $latest ? ref $latest : ""), + )); + next MODULE; + } + return if $CPAN::Signal; # this is sometimes lengthy + $seen{$file} ||= 0; + if ($what eq "a") { + push @result, sprintf "%s %s\n", $module->id, $have; + } elsif ($what eq "r") { + push @result, $module->id; + next MODULE if $seen{$file}++; + } elsif ($what eq "u") { + push @result, $module->id; + next MODULE if $seen{$file}++; + next MODULE if $file =~ /^Contact/; + } + unless ($headerdone++) { + $CPAN::Frontend->myprint("\n"); + $CPAN::Frontend->myprint(sprintf( + $sprintf, + "", + "Package namespace", + "", + "installed", + "latest", + "in CPAN file" + )); + } + my $color_on = ""; + my $color_off = ""; + if ( + $COLOR_REGISTERED + && + $CPAN::META->has_inst("Term::ANSIColor") + && + $module->description + ) { + $color_on = Term::ANSIColor::color("green"); + $color_off = Term::ANSIColor::color("reset"); + } + $CPAN::Frontend->myprint(sprintf $sprintf, + $color_on, + $module->id, + $color_off, + $have, + $latest, + $file); + $need{$module->id}++; + } + unless (%need) { + if (!@expand || $what eq "u") { + $CPAN::Frontend->myprint("No modules found for @args\n"); + } elsif ($what eq "r") { + $CPAN::Frontend->myprint("All modules are up to date for @args\n"); + } + } + if ($what eq "r") { + if ($version_zeroes) { + my $s_has = $version_zeroes > 1 ? "s have" : " has"; + $CPAN::Frontend->myprint(qq{$version_zeroes installed module$s_has }. + qq{a version number of 0\n}); + if ($CPAN::Config->{show_zero_versions}) { + local $" = "\t"; + $CPAN::Frontend->myprint(qq{ they are\n\t@version_zeroes\n}); + $CPAN::Frontend->myprint(qq{(use 'o conf show_zero_versions 0' }. + qq{to hide them)\n}); + } else { + $CPAN::Frontend->myprint(qq{(use 'o conf show_zero_versions 1' }. + qq{to show them)\n}); + } + } + if ($version_undefs) { + my $s_has = $version_undefs > 1 ? "s have" : " has"; + $CPAN::Frontend->myprint(qq{$version_undefs installed module$s_has no }. + qq{parsable version number\n}); + if ($CPAN::Config->{show_unparsable_versions}) { + local $" = "\t"; + $CPAN::Frontend->myprint(qq{ they are\n\t@version_undefs\n}); + $CPAN::Frontend->myprint(qq{(use 'o conf show_unparsable_versions 0' }. + qq{to hide them)\n}); + } else { + $CPAN::Frontend->myprint(qq{(use 'o conf show_unparsable_versions 1' }. + qq{to show them)\n}); + } + } + } + @result; +} + +#-> sub CPAN::Shell::r ; +sub r { + shift->_u_r_common("r",@_); +} + +#-> sub CPAN::Shell::u ; +sub u { + shift->_u_r_common("u",@_); +} + +#-> sub CPAN::Shell::failed ; +sub failed { + my($self,$only_id,$silent) = @_; + my @failed = $self->find_failed($only_id); + my $scope; + if ($only_id) { + $scope = "this command"; + } elsif ($CPAN::Index::HAVE_REANIMATED) { + $scope = "this or a previous session"; + # it might be nice to have a section for previous session and + # a second for this + } else { + $scope = "this session"; + } + if (@failed) { + my $print; + my $debug = 0; + if ($debug) { + $print = join "", + map { sprintf "%5d %-45s: %s %s\n", @$_ } + sort { $a->[0] <=> $b->[0] } @failed; + } else { + $print = join "", + map { sprintf " %-45s: %s %s\n", @$_[1..3] } + sort { + $a->[0] <=> $b->[0] + || + $a->[4] <=> $b->[4] + } @failed; + } + $CPAN::Frontend->myprint("Failed during $scope:\n$print"); + } elsif (!$only_id || !$silent) { + $CPAN::Frontend->myprint("Nothing failed in $scope\n"); + } +} + +sub find_failed { + my($self,$only_id) = @_; + my @failed; + DIST: for my $d (sort { $a->id cmp $b->id } $CPAN::META->all_objects("CPAN::Distribution")) { + my $failed = ""; + NAY: for my $nosayer ( # order matters! + "unwrapped", + "writemakefile", + "signature_verify", + "make", + "make_test", + "install", + "make_clean", + ) { + next unless exists $d->{$nosayer}; + next unless defined $d->{$nosayer}; + next unless ( + UNIVERSAL::can($d->{$nosayer},"failed") ? + $d->{$nosayer}->failed : + $d->{$nosayer} =~ /^NO/ + ); + next NAY if $only_id && $only_id != ( + UNIVERSAL::can($d->{$nosayer},"commandid") + ? + $d->{$nosayer}->commandid + : + $CPAN::CurrentCommandId + ); + $failed = $nosayer; + last; + } + next DIST unless $failed; + my $id = $d->id; + $id =~ s|^./../||; + ### XXX need to flag optional modules as '(optional)' if they are + # from recommends/suggests -- i.e. *show* failure, but make it clear + # it was failure of optional module -- xdg, 2012-04-01 + $id = "(optional) $id" if ! $d->{mandatory}; + #$print .= sprintf( + # " %-45s: %s %s\n", + push @failed, + ( + UNIVERSAL::can($d->{$failed},"failed") ? + [ + $d->{$failed}->commandid, + $id, + $failed, + $d->{$failed}->text, + $d->{$failed}{TIME}||0, + !! $d->{mandatory}, + ] : + [ + 1, + $id, + $failed, + $d->{$failed}, + 0, + !! $d->{mandatory}, + ] + ); + } + return @failed; +} + +sub mandatory_dist_failed { + my ($self) = @_; + return grep { $_->[5] } $self->find_failed($CPAN::CurrentCommandID); +} + +# XXX intentionally undocumented because completely bogus, unportable, +# useless, etc. + +#-> sub CPAN::Shell::status ; +sub status { + my($self) = @_; + require Devel::Size; + my $ps = FileHandle->new; + open $ps, "/proc/$$/status"; + my $vm = 0; + while (<$ps>) { + next unless /VmSize:\s+(\d+)/; + $vm = $1; + last; + } + $CPAN::Frontend->mywarn(sprintf( + "%-27s %6d\n%-27s %6d\n", + "vm", + $vm, + "CPAN::META", + Devel::Size::total_size($CPAN::META)/1024, + )); + for my $k (sort keys %$CPAN::META) { + next unless substr($k,0,4) eq "read"; + warn sprintf " %-26s %6d\n", $k, Devel::Size::total_size($CPAN::META->{$k})/1024; + for my $k2 (sort keys %{$CPAN::META->{$k}}) { + warn sprintf " %-25s %6d (keys: %6d)\n", + $k2, + Devel::Size::total_size($CPAN::META->{$k}{$k2})/1024, + scalar keys %{$CPAN::META->{$k}{$k2}}; + } + } +} + +# compare with install_tested +#-> sub CPAN::Shell::is_tested +sub is_tested { + my($self) = @_; + CPAN::Index->reload; + for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { + my $time; + if ($CPAN::META->{is_tested}{$b}) { + $time = scalar(localtime $CPAN::META->{is_tested}{$b}); + } else { + $time = scalar localtime; + $time =~ s/\S/?/g; + } + $CPAN::Frontend->myprint(sprintf "%s %s\n", $time, $b); + } +} + +#-> sub CPAN::Shell::autobundle ; +sub autobundle { + my($self) = shift; + CPAN::HandleConfig->load unless $CPAN::Config_loaded++; + my(@bundle) = $self->_u_r_common("a",@_); + my($todir) = File::Spec->catdir($CPAN::Config->{'cpan_home'},"Bundle"); + File::Path::mkpath($todir); + unless (-d $todir) { + $CPAN::Frontend->myprint("Couldn't mkdir $todir for some reason\n"); + return; + } + my($y,$m,$d) = (localtime)[5,4,3]; + $y+=1900; + $m++; + my($c) = 0; + my($me) = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, $c; + my($to) = File::Spec->catfile($todir,"$me.pm"); + while (-f $to) { + $me = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, ++$c; + $to = File::Spec->catfile($todir,"$me.pm"); + } + my($fh) = FileHandle->new(">$to") or Carp::croak "Can't open >$to: $!"; + $fh->print( + "package Bundle::$me;\n\n", + "\$","VERSION = '0.01';\n\n", # hide from perl-reversion + "1;\n\n", + "__END__\n\n", + "=head1 NAME\n\n", + "Bundle::$me - Snapshot of installation on ", + $Config::Config{'myhostname'}, + " on ", + scalar(localtime), + "\n\n=head1 SYNOPSIS\n\n", + "perl -MCPAN -e 'install Bundle::$me'\n\n", + "=head1 CONTENTS\n\n", + join("\n", @bundle), + "\n\n=head1 CONFIGURATION\n\n", + Config->myconfig, + "\n\n=head1 AUTHOR\n\n", + "This Bundle has been generated automatically ", + "by the autobundle routine in CPAN.pm.\n", + ); + $fh->close; + $CPAN::Frontend->myprint("\nWrote bundle file + $to\n\n"); + return $to; +} + +#-> sub CPAN::Shell::expandany ; +sub expandany { + my($self,$s) = @_; + CPAN->debug("s[$s]") if $CPAN::DEBUG; + my $module_as_path = ""; + if ($s =~ m|(?:\w+/)*\w+\.pm$|) { # same regexp in sub m + $module_as_path = $s; + $module_as_path =~ s/.pm$//; + $module_as_path =~ s|/|::|g; + } + if ($module_as_path) { + if ($module_as_path =~ m|^Bundle::|) { + $self->local_bundles; + return $self->expand('Bundle',$module_as_path); + } else { + return $self->expand('Module',$module_as_path) + if $CPAN::META->exists('CPAN::Module',$module_as_path); + } + } elsif ($s =~ m|/| or substr($s,-1,1) eq ".") { # looks like a file or a directory + $s = CPAN::Distribution->normalize($s); + return $CPAN::META->instance('CPAN::Distribution',$s); + # Distributions spring into existence, not expand + } elsif ($s =~ m|^Bundle::|) { + $self->local_bundles; # scanning so late for bundles seems + # both attractive and crumpy: always + # current state but easy to forget + # somewhere + return $self->expand('Bundle',$s); + } else { + return $self->expand('Module',$s) + if $CPAN::META->exists('CPAN::Module',$s); + } + return; +} + +#-> sub CPAN::Shell::expand ; +sub expand { + my $self = shift; + my($type,@args) = @_; + CPAN->debug("type[$type]args[@args]") if $CPAN::DEBUG; + my $class = "CPAN::$type"; + my $methods = ['id']; + for my $meth (qw(name)) { + next unless $class->can($meth); + push @$methods, $meth; + } + $self->expand_by_method($class,$methods,@args); +} + +#-> sub CPAN::Shell::expand_by_method ; +sub expand_by_method { + my $self = shift; + my($class,$methods,@args) = @_; + my($arg,@m); + for $arg (@args) { + my($regex,$command); + if ($arg =~ m|^/(.*)/$|) { + $regex = $1; +# FIXME: there seem to be some ='s in the author data, which trigger +# a failure here. This needs to be contemplated. +# } elsif ($arg =~ m/=/) { +# $command = 1; + } + my $obj; + CPAN->debug(sprintf "class[%s]regex[%s]command[%s]", + $class, + defined $regex ? $regex : "UNDEFINED", + defined $command ? $command : "UNDEFINED", + ) if $CPAN::DEBUG; + if (defined $regex) { + if (CPAN::_sqlite_running()) { + CPAN::Index->reload; + $CPAN::SQLite->search($class, $regex); + } + for $obj ( + $CPAN::META->all_objects($class) + ) { + unless ($obj && UNIVERSAL::can($obj,"id") && $obj->id) { + # BUG, we got an empty object somewhere + require Data::Dumper; + CPAN->debug(sprintf( + "Bug in CPAN: Empty id on obj[%s][%s]", + $obj, + Data::Dumper::Dumper($obj) + )) if $CPAN::DEBUG; + next; + } + for my $method (@$methods) { + my $match = eval {$obj->$method() =~ /$regex/i}; + if ($@) { + my($err) = $@ =~ /^(.+) at .+? line \d+\.$/; + $err ||= $@; # if we were too restrictive above + $CPAN::Frontend->mydie("$err\n"); + } elsif ($match) { + push @m, $obj; + last; + } + } + } + } elsif ($command) { + die "equal sign in command disabled (immature interface), ". + "you can set + ! \$CPAN::Shell::ADVANCED_QUERY=1 +to enable it. But please note, this is HIGHLY EXPERIMENTAL code +that may go away anytime.\n" + unless $ADVANCED_QUERY; + my($method,$criterion) = $arg =~ /(.+?)=(.+)/; + my($matchcrit) = $criterion =~ m/^~(.+)/; + for my $self ( + sort + {$a->id cmp $b->id} + $CPAN::META->all_objects($class) + ) { + my $lhs = $self->$method() or next; # () for 5.00503 + if ($matchcrit) { + push @m, $self if $lhs =~ m/$matchcrit/; + } else { + push @m, $self if $lhs eq $criterion; + } + } + } else { + my($xarg) = $arg; + if ( $class eq 'CPAN::Bundle' ) { + $xarg =~ s/^(Bundle::)?(.*)/Bundle::$2/; + } elsif ($class eq "CPAN::Distribution") { + $xarg = CPAN::Distribution->normalize($arg); + } else { + $xarg =~ s/:+/::/g; + } + if ($CPAN::META->exists($class,$xarg)) { + $obj = $CPAN::META->instance($class,$xarg); + } elsif ($CPAN::META->exists($class,$arg)) { + $obj = $CPAN::META->instance($class,$arg); + } else { + next; + } + push @m, $obj; + } + } + @m = sort {$a->id cmp $b->id} @m; + if ( $CPAN::DEBUG ) { + my $wantarray = wantarray; + my $join_m = join ",", map {$_->id} @m; + # $self->debug("wantarray[$wantarray]join_m[$join_m]"); + my $count = scalar @m; + $self->debug("class[$class]wantarray[$wantarray]count m[$count]"); + } + return wantarray ? @m : $m[0]; +} + +#-> sub CPAN::Shell::format_result ; +sub format_result { + my($self) = shift; + my($type,@args) = @_; + @args = '/./' unless @args; + my(@result) = $self->expand($type,@args); + my $result = @result == 1 ? + $result[0]->as_string : + @result == 0 ? + "No objects of type $type found for argument @args\n" : + join("", + (map {$_->as_glimpse} @result), + scalar @result, " items found\n", + ); + $result; +} + +#-> sub CPAN::Shell::report_fh ; +{ + my $installation_report_fh; + my $previously_noticed = 0; + + sub report_fh { + return $installation_report_fh if $installation_report_fh; + if ($CPAN::META->has_usable("File::Temp")) { + $installation_report_fh + = File::Temp->new( + dir => File::Spec->tmpdir, + template => 'cpan_install_XXXX', + suffix => '.txt', + unlink => 0, + ); + } + unless ( $installation_report_fh ) { + warn("Couldn't open installation report file; " . + "no report file will be generated." + ) unless $previously_noticed++; + } + } +} + + +# The only reason for this method is currently to have a reliable +# debugging utility that reveals which output is going through which +# channel. No, I don't like the colors ;-) + +# to turn colordebugging on, write +# cpan> o conf colorize_output 1 + +#-> sub CPAN::Shell::colorize_output ; +{ + my $print_ornamented_have_warned = 0; + sub colorize_output { + my $colorize_output = $CPAN::Config->{colorize_output}; + if ($colorize_output && $^O eq 'MSWin32' && !$CPAN::META->has_inst("Win32::Console::ANSI")) { + unless ($print_ornamented_have_warned++) { + # no myprint/mywarn within myprint/mywarn! + warn "Colorize_output is set to true but Win32::Console::ANSI is not +installed. To activate colorized output, please install Win32::Console::ANSI.\n\n"; + } + $colorize_output = 0; + } + if ($colorize_output && !$CPAN::META->has_inst("Term::ANSIColor")) { + unless ($print_ornamented_have_warned++) { + # no myprint/mywarn within myprint/mywarn! + warn "Colorize_output is set to true but Term::ANSIColor is not +installed. To activate colorized output, please install Term::ANSIColor.\n\n"; + } + $colorize_output = 0; + } + return $colorize_output; + } +} + + +#-> sub CPAN::Shell::print_ornamented ; +sub print_ornamented { + my($self,$what,$ornament) = @_; + return unless defined $what; + + local $| = 1; # Flush immediately + if ( $CPAN::Be_Silent ) { + # WARNING: variable Be_Silent is poisoned and must be eliminated. + print {report_fh()} $what; + return; + } + my $swhat = "$what"; # stringify if it is an object + if ($CPAN::Config->{term_is_latin}) { + # note: deprecated, need to switch to $LANG and $LC_* + # courtesy jhi: + $swhat + =~ s{([\xC0-\xDF])([\x80-\xBF])}{chr(ord($1)<<6&0xC0|ord($2)&0x3F)}eg; #}; + } + if ($self->colorize_output) { + if ( $CPAN::DEBUG && $swhat =~ /^Debug\(/ ) { + # if you want to have this configurable, please file a bug report + $ornament = $CPAN::Config->{colorize_debug} || "black on_cyan"; + } + my $color_on = eval { Term::ANSIColor::color($ornament) } || ""; + if ($@) { + print "Term::ANSIColor rejects color[$ornament]: $@\n +Please choose a different color (Hint: try 'o conf init /color/')\n"; + } + # GGOLDBACH/Test-GreaterVersion-0.008 broke without this + # $trailer construct. We want the newline be the last thing if + # there is a newline at the end ensuring that the next line is + # empty for other players + my $trailer = ""; + $trailer = $1 if $swhat =~ s/([\r\n]+)\z//; + print $color_on, + $swhat, + Term::ANSIColor::color("reset"), + $trailer; + } else { + print $swhat; + } +} + +#-> sub CPAN::Shell::myprint ; + +# where is myprint/mywarn/Frontend/etc. documented? Where to use what? +# I think, we send everything to STDOUT and use print for normal/good +# news and warn for news that need more attention. Yes, this is our +# working contract for now. +sub myprint { + my($self,$what) = @_; + $self->print_ornamented($what, + $CPAN::Config->{colorize_print}||'bold blue on_white', + ); +} + +my %already_printed; +#-> sub CPAN::Shell::mywarnonce ; +sub myprintonce { + my($self,$what) = @_; + $self->myprint($what) unless $already_printed{$what}++; +} + +sub optprint { + my($self,$category,$what) = @_; + my $vname = $category . "_verbosity"; + CPAN::HandleConfig->load unless $CPAN::Config_loaded++; + if (!$CPAN::Config->{$vname} + || $CPAN::Config->{$vname} =~ /^v/ + ) { + $CPAN::Frontend->myprint($what); + } +} + +#-> sub CPAN::Shell::myexit ; +sub myexit { + my($self,$what) = @_; + $self->myprint($what); + exit; +} + +#-> sub CPAN::Shell::mywarn ; +sub mywarn { + my($self,$what) = @_; + $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white'); +} + +my %already_warned; +#-> sub CPAN::Shell::mywarnonce ; +sub mywarnonce { + my($self,$what) = @_; + $self->mywarn($what) unless $already_warned{$what}++; +} + +# only to be used for shell commands +#-> sub CPAN::Shell::mydie ; +sub mydie { + my($self,$what) = @_; + $self->mywarn($what); + + # If it is the shell, we want the following die to be silent, + # but if it is not the shell, we would need a 'die $what'. We need + # to take care that only shell commands use mydie. Is this + # possible? + + die "\n"; +} + +# sub CPAN::Shell::colorable_makemaker_prompt ; +sub colorable_makemaker_prompt { + my($foo,$bar,$ornament) = @_; + $ornament ||= "colorize_print"; + if (CPAN::Shell->colorize_output) { + my $ornament = $CPAN::Config->{$ornament}||'bold blue on_white'; + my $color_on = eval { Term::ANSIColor::color($ornament); } || ""; + print $color_on; + } + my $ans = ExtUtils::MakeMaker::prompt($foo,$bar); + if (CPAN::Shell->colorize_output) { + print Term::ANSIColor::color('reset'); + } + return $ans; +} + +# use this only for unrecoverable errors! +#-> sub CPAN::Shell::unrecoverable_error ; +sub unrecoverable_error { + my($self,$what) = @_; + my @lines = split /\n/, $what; + my $longest = 0; + for my $l (@lines) { + $longest = length $l if length $l > $longest; + } + $longest = 62 if $longest > 62; + for my $l (@lines) { + if ($l =~ /^\s*$/) { + $l = "\n"; + next; + } + $l = "==> $l"; + if (length $l < 66) { + $l = pack "A66 A*", $l, "<=="; + } + $l .= "\n"; + } + unshift @lines, "\n"; + $self->mydie(join "", @lines); +} + +#-> sub CPAN::Shell::mysleep ; +sub mysleep { + return if $ENV{AUTOMATED_TESTING} || ! -t STDOUT; + my($self, $sleep) = @_; + if (CPAN->has_inst("Time::HiRes")) { + Time::HiRes::sleep($sleep); + } else { + sleep($sleep < 1 ? 1 : int($sleep + 0.5)); + } +} + +#-> sub CPAN::Shell::setup_output ; +sub setup_output { + return if -t STDOUT; + my $odef = select STDERR; + $| = 1; + select STDOUT; + $| = 1; + select $odef; +} + +#-> sub CPAN::Shell::rematein ; +# RE-adme||MA-ke||TE-st||IN-stall : nearly everything runs through here +sub rematein { + my $self = shift; + # this variable was global and disturbed programmers, so localize: + local $CPAN::Distrostatus::something_has_failed_at; + my($meth,@some) = @_; + my @pragma; + while($meth =~ /^(ff?orce|notest)$/) { + push @pragma, $meth; + $meth = shift @some or + $CPAN::Frontend->mydie("Pragma $pragma[-1] used without method: ". + "cannot continue"); + } + setup_output(); + CPAN->debug("pragma[@pragma]meth[$meth]some[@some]") if $CPAN::DEBUG; + + # Here is the place to set "test_count" on all involved parties to + # 0. We then can pass this counter on to the involved + # distributions and those can refuse to test if test_count > X. In + # the first stab at it we could use a 1 for "X". + + # But when do I reset the distributions to start with 0 again? + # Jost suggested to have a random or cycling interaction ID that + # we pass through. But the ID is something that is just left lying + # around in addition to the counter, so I'd prefer to set the + # counter to 0 now, and repeat at the end of the loop. But what + # about dependencies? They appear later and are not reset, they + # enter the queue but not its copy. How do they get a sensible + # test_count? + + # With configure_requires, "get" is vulnerable in recursion. + + my $needs_recursion_protection = "get|make|test|install"; + + # construct the queue + my($s,@s,@qcopy); + STHING: foreach $s (@some) { + my $obj; + if (ref $s) { + CPAN->debug("s is an object[$s]") if $CPAN::DEBUG; + $obj = $s; + } elsif ($s =~ m|[\$\@\%]|) { # looks like a perl variable + } elsif ($s =~ m|^/|) { # looks like a regexp + if (substr($s,-1,1) eq ".") { + $obj = CPAN::Shell->expandany($s); + } else { + my @obj; + CLASS: for my $class (qw(Distribution Bundle Module)) { + if (@obj = $self->expand($class,$s)) { + last CLASS; + } + } + if (@obj) { + if (1==@obj) { + $obj = $obj[0]; + } else { + $CPAN::Frontend->mywarn("Sorry, $meth with a regular expression is ". + "only supported when unambiguous.\nRejecting argument '$s'\n"); + $CPAN::Frontend->mysleep(2); + next STHING; + } + } + } + } elsif ($meth eq "ls") { + $self->globls($s,\@pragma); + next STHING; + } else { + CPAN->debug("calling expandany [$s]") if $CPAN::DEBUG; + $obj = CPAN::Shell->expandany($s); + } + if (0) { + } elsif (ref $obj) { + if ($meth =~ /^($needs_recursion_protection)$/) { + # it would be silly to check for recursion for look or dump + # (we are in CPAN::Shell::rematein) + CPAN->debug("Testing against recursion") if $CPAN::DEBUG; + eval { $obj->color_cmd_tmps(0,1); }; + if ($@) { + if (ref $@ + and $@->isa("CPAN::Exception::RecursiveDependency")) { + $CPAN::Frontend->mywarn($@); + } else { + if (0) { + require Carp; + Carp::confess(sprintf "DEBUG: \$\@[%s]ref[%s]", $@, ref $@); + } + die; + } + } + } + CPAN::Queue->queue_item(qmod => $obj->id, reqtype => "c", optional => ''); + push @qcopy, $obj; + } elsif ($CPAN::META->exists('CPAN::Author',uc($s))) { + $obj = $CPAN::META->instance('CPAN::Author',uc($s)); + if ($meth =~ /^(dump|ls|reports)$/) { + $obj->$meth(); + } else { + $CPAN::Frontend->mywarn( + join "", + "Don't be silly, you can't $meth ", + $obj->fullname, + " ;-)\n" + ); + $CPAN::Frontend->mysleep(2); + } + } elsif ($s =~ m|[\$\@\%]| && $meth eq "dump") { + CPAN::InfoObj->dump($s); + } else { + $CPAN::Frontend + ->mywarn(qq{Warning: Cannot $meth $s, }. + qq{don't know what it is. +Try the command + + i /$s/ + +to find objects with matching identifiers. +}); + $CPAN::Frontend->mysleep(2); + } + } + + # queuerunner (please be warned: when I started to change the + # queue to hold objects instead of names, I made one or two + # mistakes and never found which. I reverted back instead) + QITEM: while (my $q = CPAN::Queue->first) { + my $obj; + my $s = $q->as_string; + my $reqtype = $q->reqtype || ""; + my $optional = $q->optional || ""; + $obj = CPAN::Shell->expandany($s); + unless ($obj) { + # don't know how this can happen, maybe we should panic, + # but maybe we get a solution from the first user who hits + # this unfortunate exception? + $CPAN::Frontend->mywarn("Warning: Could not expand string '$s' ". + "to an object. Skipping.\n"); + $CPAN::Frontend->mysleep(5); + CPAN::Queue->delete_first($s); + next QITEM; + } + $obj->{reqtype} ||= ""; + my $type = ref $obj; + if ( $type eq 'CPAN::Distribution' || $type eq 'CPAN::Bundle' ) { + $obj->{mandatory} ||= ! $optional; # once mandatory, always mandatory + } + elsif ( $type eq 'CPAN::Module' ) { + $obj->{mandatory} ||= ! $optional; # once mandatory, always mandatory + if (my $d = $obj->distribution) { + $d->{mandatory} ||= ! $optional; # once mandatory, always mandatory + } elsif ($optional) { + # the queue object does not know who was recommending/suggesting us:( + # So we only vaguely write "optional". + $CPAN::Frontend->mywarn("Warning: optional module '$s' ". + "not known. Skipping.\n"); + CPAN::Queue->delete_first($s); + next QITEM; + } + } + { + # force debugging because CPAN::SQLite somehow delivers us + # an empty object; + + # local $CPAN::DEBUG = 1024; # Shell; probably fixed now + + CPAN->debug("s[$s]obj-reqtype[$obj->{reqtype}]". + "q-reqtype[$reqtype]") if $CPAN::DEBUG; + } + if ($obj->{reqtype}) { + if ($obj->{reqtype} eq "b" && $reqtype =~ /^[rc]$/) { + $obj->{reqtype} = $reqtype; + if ( + exists $obj->{install} + && + ( + UNIVERSAL::can($obj->{install},"failed") ? + $obj->{install}->failed : + $obj->{install} =~ /^NO/ + ) + ) { + delete $obj->{install}; + $CPAN::Frontend->mywarn + ("Promoting $obj->{ID} from 'build_requires' to 'requires'"); + } + } + } else { + $obj->{reqtype} = $reqtype; + } + + for my $pragma (@pragma) { + if ($pragma + && + $obj->can($pragma)) { + $obj->$pragma($meth); + } + } + if (UNIVERSAL::can($obj, 'called_for')) { + $obj->called_for($s) unless $obj->called_for; + } + CPAN->debug(qq{pragma[@pragma]meth[$meth]}. + qq{ID[$obj->{ID}]}) if $CPAN::DEBUG; + + push @qcopy, $obj; + if ($meth =~ /^(report)$/) { # they came here with a pragma? + $self->$meth($obj); + } elsif (! UNIVERSAL::can($obj,$meth)) { + # Must never happen + my $serialized = ""; + if (0) { + } elsif ($CPAN::META->has_inst("YAML::Syck")) { + $serialized = YAML::Syck::Dump($obj); + } elsif ($CPAN::META->has_inst("YAML")) { + $serialized = YAML::Dump($obj); + } elsif ($CPAN::META->has_inst("Data::Dumper")) { + $serialized = Data::Dumper::Dumper($obj); + } else { + require overload; + $serialized = overload::StrVal($obj); + } + CPAN->debug("Going to panic. meth[$meth]s[$s]") if $CPAN::DEBUG; + $CPAN::Frontend->mydie("Panic: obj[$serialized] cannot meth[$meth]"); + } else { + my $upgraded_meth = $meth; + if ( $meth eq "make" and $obj->{reqtype} eq "b" ) { + # rt 86915 + $upgraded_meth = "test"; + } + if ($obj->$upgraded_meth()) { + CPAN::Queue->delete($s); + CPAN->debug("Succeeded and deleted from queue. pragma[@pragma]meth[$meth][s][$s]") if $CPAN::DEBUG; + } else { + CPAN->debug("Failed. pragma[@pragma]meth[$meth]s[$s]") if $CPAN::DEBUG; + } + } + + $obj->undelay; + for my $pragma (@pragma) { + my $unpragma = "un$pragma"; + if ($obj->can($unpragma)) { + $obj->$unpragma(); + } + } + # if any failures occurred and the current object is mandatory, we + # still don't know if *it* failed or if it was another (optional) + # module, so we have to check that explicitly (and expensively) + if ( $CPAN::Config->{halt_on_failure} + && $obj->{mandatory} + && CPAN::Distrostatus::something_has_just_failed() + && $self->mandatory_dist_failed() + ) { + $CPAN::Frontend->mywarn("Stopping: '$meth' failed for '$s'.\n"); + CPAN::Queue->nullify_queue; + last QITEM; + } + CPAN::Queue->delete_first($s); + } + if ($meth =~ /^($needs_recursion_protection)$/) { + for my $obj (@qcopy) { + $obj->color_cmd_tmps(0,0); + } + } +} + +#-> sub CPAN::Shell::recent ; +sub recent { + my($self) = @_; + if ($CPAN::META->has_inst("XML::LibXML")) { + my $url = $CPAN::Defaultrecent; + $CPAN::Frontend->myprint("Fetching '$url'\n"); + unless ($CPAN::META->has_usable("LWP")) { + $CPAN::Frontend->mydie("LWP not installed; cannot continue"); + } + CPAN::LWP::UserAgent->config; + my $Ua; + eval { $Ua = CPAN::LWP::UserAgent->new; }; + if ($@) { + $CPAN::Frontend->mydie("CPAN::LWP::UserAgent->new dies with $@\n"); + } + my $resp = $Ua->get($url); + unless ($resp->is_success) { + $CPAN::Frontend->mydie(sprintf "Could not download '%s': %s\n", $url, $resp->code); + } + $CPAN::Frontend->myprint("DONE\n\n"); + my $xml = XML::LibXML->new->parse_string($resp->content); + if (0) { + my $s = $xml->serialize(2); + $s =~ s/\n\s*\n/\n/g; + $CPAN::Frontend->myprint($s); + return; + } + my @distros; + if ($url =~ /winnipeg/) { + my $pubdate = $xml->findvalue("/rss/channel/pubDate"); + $CPAN::Frontend->myprint(" pubDate: $pubdate\n\n"); + for my $eitem ($xml->findnodes("/rss/channel/item")) { + my $distro = $eitem->findvalue("enclosure/\@url"); + $distro =~ s|.*?/authors/id/./../||; + my $size = $eitem->findvalue("enclosure/\@length"); + my $desc = $eitem->findvalue("description"); + $desc =~ s/.+? - //; + $CPAN::Frontend->myprint("$distro [$size b]\n $desc\n"); + push @distros, $distro; + } + } elsif ($url =~ /search.*uploads.rdf/) { + # xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + # xmlns="http://purl.org/rss/1.0/" + # xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" + # xmlns:dc="http://purl.org/dc/elements/1.1/" + # xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" + # xmlns:admin="http://webns.net/mvcb/" + + + my $dc_date = $xml->findvalue("//*[local-name(.) = 'RDF']/*[local-name(.) = 'channel']/*[local-name(.) = 'date']"); + $CPAN::Frontend->myprint(" dc:date: $dc_date\n\n"); + my $finish_eitem = 0; + local $SIG{INT} = sub { $finish_eitem = 1 }; + EITEM: for my $eitem ($xml->findnodes("//*[local-name(.) = 'RDF']/*[local-name(.) = 'item']")) { + my $distro = $eitem->findvalue("\@rdf:about"); + $distro =~ s|.*~||; # remove up to the tilde before the name + $distro =~ s|/$||; # remove trailing slash + $distro =~ s|([^/]+)|\U$1\E|; # upcase the name + my $author = uc $1 or die "distro[$distro] without author, cannot continue"; + my $desc = $eitem->findvalue("*[local-name(.) = 'description']"); + my $i = 0; + SUBDIRTEST: while () { + last SUBDIRTEST if ++$i >= 6; # half a dozen must do! + if (my @ret = $self->globls("$distro*")) { + @ret = grep {$_->[2] !~ /meta/} @ret; + @ret = grep {length $_->[2]} @ret; + if (@ret) { + $distro = "$author/$ret[0][2]"; + last SUBDIRTEST; + } + } + $distro =~ s|/|/*/|; # allow it to reside in a subdirectory + } + + next EITEM if $distro =~ m|\*|; # did not find the thing + $CPAN::Frontend->myprint("____$desc\n"); + push @distros, $distro; + last EITEM if $finish_eitem; + } + } + return \@distros; + } else { + # deprecated old version + $CPAN::Frontend->mydie("no XML::LibXML installed, cannot continue\n"); + } +} + +#-> sub CPAN::Shell::smoke ; +sub smoke { + my($self) = @_; + my $distros = $self->recent; + DISTRO: for my $distro (@$distros) { + next if $distro =~ m|/Bundle-|; # XXX crude heuristic to skip bundles + $CPAN::Frontend->myprint(sprintf "Downloading and testing '$distro'\n"); + { + my $skip = 0; + local $SIG{INT} = sub { $skip = 1 }; + for (0..9) { + $CPAN::Frontend->myprint(sprintf "\r%2d (Hit ^C to skip)", 10-$_); + sleep 1; + if ($skip) { + $CPAN::Frontend->myprint(" skipped\n"); + next DISTRO; + } + } + } + $CPAN::Frontend->myprint("\r \n"); # leave the dirty line with a newline + $self->test($distro); + } +} + +{ + # set up the dispatching methods + no strict "refs"; + for my $command (qw( + clean + cvs_import + dump + force + fforce + get + install + look + ls + make + notest + perldoc + readme + reports + test + )) { + *$command = sub { shift->rematein($command, @_); }; + } +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Tarzip.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Tarzip.pm new file mode 100644 index 0000000000..6517cb8fd7 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Tarzip.pm @@ -0,0 +1,479 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +package CPAN::Tarzip; +use strict; +use vars qw($VERSION @ISA $BUGHUNTING); +use CPAN::Debug; +use File::Basename qw(basename); +$VERSION = "5.5013"; +# module is internal to CPAN.pm + +@ISA = qw(CPAN::Debug); ## no critic +$BUGHUNTING ||= 0; # released code must have turned off + +# it's ok if file doesn't exist, it just matters if it is .gz or .bz2 +sub new { + my($class,$file) = @_; + $CPAN::Frontend->mydie("CPAN::Tarzip->new called without arg") unless defined $file; + my $me = { FILE => $file }; + if ($file =~ /\.(bz2|gz|zip|tbz|tgz)$/i) { + $me->{ISCOMPRESSED} = 1; + } else { + $me->{ISCOMPRESSED} = 0; + } + if (0) { + } elsif ($file =~ /\.(?:bz2|tbz)$/i) { + unless ($me->{UNGZIPPRG} = $CPAN::Config->{bzip2}) { + my $bzip2 = _my_which("bzip2"); + if ($bzip2) { + $me->{UNGZIPPRG} = $bzip2; + } else { + $CPAN::Frontend->mydie(qq{ +CPAN.pm needs the external program bzip2 in order to handle '$file'. +Please install it now and run 'o conf init bzip2' from the +CPAN shell prompt to register it as external program. +}); + } + } + } else { + $me->{UNGZIPPRG} = _my_which("gzip"); + } + $me->{TARPRG} = _my_which("tar") || _my_which("gtar"); + bless $me, $class; +} + +sub _zlib_ok () { + $CPAN::META->has_inst("Compress::Zlib") or return; + Compress::Zlib->can('gzopen'); +} + +sub _my_which { + my($what) = @_; + if ($CPAN::Config->{$what}) { + return $CPAN::Config->{$what}; + } + if ($CPAN::META->has_inst("File::Which")) { + return File::Which::which($what); + } + my @cand = MM->maybe_command($what); + return $cand[0] if @cand; + require File::Spec; + my $component; + PATH_COMPONENT: foreach $component (File::Spec->path()) { + next unless defined($component) && $component; + my($abs) = File::Spec->catfile($component,$what); + if (MM->maybe_command($abs)) { + return $abs; + } + } + return; +} + +sub gzip { + my($self,$read) = @_; + my $write = $self->{FILE}; + if (_zlib_ok) { + my($buffer,$fhw); + $fhw = FileHandle->new($read) + or $CPAN::Frontend->mydie("Could not open $read: $!"); + my $cwd = `pwd`; + my $gz = Compress::Zlib::gzopen($write, "wb") + or $CPAN::Frontend->mydie("Cannot gzopen $write: $! (pwd is $cwd)\n"); + binmode($fhw); + $gz->gzwrite($buffer) + while read($fhw,$buffer,4096) > 0 ; + $gz->gzclose() ; + $fhw->close; + return 1; + } else { + my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); + system(qq{$command -c "$read" > "$write"})==0; + } +} + + +sub gunzip { + my($self,$write) = @_; + my $read = $self->{FILE}; + if (_zlib_ok) { + my($buffer,$fhw); + $fhw = FileHandle->new(">$write") + or $CPAN::Frontend->mydie("Could not open >$write: $!"); + my $gz = Compress::Zlib::gzopen($read, "rb") + or $CPAN::Frontend->mydie("Cannot gzopen $read: $!\n"); + binmode($fhw); + $fhw->print($buffer) + while $gz->gzread($buffer) > 0 ; + $CPAN::Frontend->mydie("Error reading from $read: $!\n") + if $gz->gzerror != Compress::Zlib::Z_STREAM_END(); + $gz->gzclose() ; + $fhw->close; + return 1; + } else { + my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); + system(qq{$command -d -c "$read" > "$write"})==0; + } +} + + +sub gtest { + my($self) = @_; + return $self->{GTEST} if exists $self->{GTEST}; + defined $self->{FILE} or $CPAN::Frontend->mydie("gtest called but no FILE specified"); + my $read = $self->{FILE}; + my $success; + if ($read=~/\.(?:bz2|tbz)$/ && $CPAN::META->has_inst("Compress::Bzip2")) { + my($buffer,$len); + $len = 0; + my $gz = Compress::Bzip2::bzopen($read, "rb") + or $CPAN::Frontend->mydie(sprintf("Cannot bzopen %s: %s\n", + $read, + $Compress::Bzip2::bzerrno)); + while ($gz->bzread($buffer) > 0 ) { + $len += length($buffer); + $buffer = ""; + } + my $err = $gz->bzerror; + $success = ! $err || $err == Compress::Bzip2::BZ_STREAM_END(); + if ($len == -s $read) { + $success = 0; + CPAN->debug("hit an uncompressed file") if $CPAN::DEBUG; + } + $gz->gzclose(); + CPAN->debug("err[$err]success[$success]") if $CPAN::DEBUG; + } elsif ( $read=~/\.(?:gz|tgz)$/ && _zlib_ok ) { + # After I had reread the documentation in zlib.h, I discovered that + # uncompressed files do not lead to an gzerror (anymore?). + my($buffer,$len); + $len = 0; + my $gz = Compress::Zlib::gzopen($read, "rb") + or $CPAN::Frontend->mydie(sprintf("Cannot gzopen %s: %s\n", + $read, + $Compress::Zlib::gzerrno)); + while ($gz->gzread($buffer) > 0 ) { + $len += length($buffer); + $buffer = ""; + } + my $err = $gz->gzerror; + $success = ! $err || $err == Compress::Zlib::Z_STREAM_END(); + if ($len == -s $read) { + $success = 0; + CPAN->debug("hit an uncompressed file") if $CPAN::DEBUG; + } + $gz->gzclose(); + CPAN->debug("err[$err]success[$success]") if $CPAN::DEBUG; + } elsif (!$self->{ISCOMPRESSED}) { + $success = 0; + } else { + my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); + $success = 0==system(qq{$command -qdt "$read"}); + } + return $self->{GTEST} = $success; +} + + +sub TIEHANDLE { + my($class,$file) = @_; + my $ret; + $class->debug("file[$file]"); + my $self = $class->new($file); + if (0) { + } elsif (!$self->gtest) { + my $fh = FileHandle->new($file) + or $CPAN::Frontend->mydie("Could not open file[$file]: $!"); + binmode $fh; + $self->{FH} = $fh; + $class->debug("via uncompressed FH"); + } elsif ($file =~ /\.(?:bz2|tbz)$/ && $CPAN::META->has_inst("Compress::Bzip2")) { + my $gz = Compress::Bzip2::bzopen($file,"rb") or + $CPAN::Frontend->mydie("Could not bzopen $file"); + $self->{GZ} = $gz; + $class->debug("via Compress::Bzip2"); + } elsif ($file =~/\.(?:gz|tgz)$/ && _zlib_ok) { + my $gz = Compress::Zlib::gzopen($file,"rb") or + $CPAN::Frontend->mydie("Could not gzopen $file"); + $self->{GZ} = $gz; + $class->debug("via Compress::Zlib"); + } else { + my $gzip = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); + my $pipe = "$gzip -d -c $file |"; + my $fh = FileHandle->new($pipe) or $CPAN::Frontend->mydie("Could not pipe[$pipe]: $!"); + binmode $fh; + $self->{FH} = $fh; + $class->debug("via external $gzip"); + } + $self; +} + + +sub READLINE { + my($self) = @_; + if (exists $self->{GZ}) { + my $gz = $self->{GZ}; + my($line,$bytesread); + $bytesread = $gz->gzreadline($line); + return undef if $bytesread <= 0; + return $line; + } else { + my $fh = $self->{FH}; + return scalar <$fh>; + } +} + + +sub READ { + my($self,$ref,$length,$offset) = @_; + $CPAN::Frontend->mydie("read with offset not implemented") if defined $offset; + if (exists $self->{GZ}) { + my $gz = $self->{GZ}; + my $byteread = $gz->gzread($$ref,$length);# 30eaf79e8b446ef52464b5422da328a8 + return $byteread; + } else { + my $fh = $self->{FH}; + return read($fh,$$ref,$length); + } +} + + +sub DESTROY { + my($self) = @_; + if (exists $self->{GZ}) { + my $gz = $self->{GZ}; + $gz->gzclose() if defined $gz; # hard to say if it is allowed + # to be undef ever. AK, 2000-09 + } else { + my $fh = $self->{FH}; + $fh->close if defined $fh; + } + undef $self; +} + +sub untar { + my($self) = @_; + my $file = $self->{FILE}; + my($prefer) = 0; + + my $exttar = $self->{TARPRG} || ""; + $exttar = "" if $exttar =~ /^\s+$/; # user refuses to use it + my $extgzip = $self->{UNGZIPPRG} || ""; + $extgzip = "" if $extgzip =~ /^\s+$/; # user refuses to use it + + if (0) { # makes changing order easier + } elsif ($BUGHUNTING) { + $prefer=2; + } elsif ($CPAN::Config->{prefer_external_tar}) { + $prefer = 1; + } elsif ( + $CPAN::META->has_usable("Archive::Tar") + && + _zlib_ok ) { + my $prefer_external_tar = $CPAN::Config->{prefer_external_tar}; + unless (defined $prefer_external_tar) { + if ($^O =~ /(MSWin32|solaris)/) { + $prefer_external_tar = 0; + } else { + $prefer_external_tar = 1; + } + } + $prefer = $prefer_external_tar ? 1 : 2; + } elsif ($exttar && $extgzip) { + # no modules and not bz2 + $prefer = 1; + # but solaris binary tar is a problem + if ($^O eq 'solaris' && qx($exttar --version 2>/dev/null) !~ /gnu/i) { + $CPAN::Frontend->mywarn(<< 'END_WARN'); + +WARNING: Many CPAN distributions were archived with GNU tar and some of +them may be incompatible with Solaris tar. We respectfully suggest you +configure CPAN to use a GNU tar instead ("o conf init tar") or install +a recent Archive::Tar instead; + +END_WARN + } + } else { + my $foundtar = $exttar ? "'$exttar'" : "nothing"; + my $foundzip = $extgzip ? "'$extgzip'" : $foundtar ? "nothing" : "also nothing"; + my $foundAT; + if ($CPAN::META->has_usable("Archive::Tar")) { + $foundAT = sprintf "'%s'", "Archive::Tar::"->VERSION; + } else { + $foundAT = "nothing"; + } + my $foundCZ; + if (_zlib_ok) { + $foundCZ = sprintf "'%s'", "Compress::Zlib::"->VERSION; + } elsif ($foundAT) { + $foundCZ = "nothing"; + } else { + $foundCZ = "also nothing"; + } + $CPAN::Frontend->mydie(qq{ + +CPAN.pm needs either the external programs tar and gzip -or- both +modules Archive::Tar and Compress::Zlib installed. + +For tar I found $foundtar, for gzip $foundzip. + +For Archive::Tar I found $foundAT, for Compress::Zlib $foundCZ; + +Can't continue cutting file '$file'. +}); + } + my $tar_verb = "v"; + if (defined $CPAN::Config->{tar_verbosity}) { + $tar_verb = $CPAN::Config->{tar_verbosity} eq "none" ? "" : + $CPAN::Config->{tar_verbosity}; + } + if ($prefer==1) { # 1 => external gzip+tar + my($system); + my $is_compressed = $self->gtest(); + my $tarcommand = CPAN::HandleConfig->safe_quote($exttar); + if ($is_compressed) { + my $command = CPAN::HandleConfig->safe_quote($extgzip); + $system = qq{$command -d -c }. + qq{< "$file" | $tarcommand x${tar_verb}f -}; + } else { + $system = qq{$tarcommand x${tar_verb}f "$file"}; + } + if (system($system) != 0) { + # people find the most curious tar binaries that cannot handle + # pipes + if ($is_compressed) { + (my $ungzf = $file) =~ s/\.gz(?!\n)\Z//; + $ungzf = basename $ungzf; + my $ct = CPAN::Tarzip->new($file); + if ($ct->gunzip($ungzf)) { + $CPAN::Frontend->myprint(qq{Uncompressed $file successfully\n}); + } else { + $CPAN::Frontend->mydie(qq{Couldn\'t uncompress $file\n}); + } + $file = $ungzf; + } + $system = qq{$tarcommand x${tar_verb}f "$file"}; + $CPAN::Frontend->myprint(qq{Using Tar:$system:\n}); + my $ret = system($system); + if ($ret==0) { + $CPAN::Frontend->myprint(qq{Untarred $file successfully\n}); + } else { + if ($? == -1) { + $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: '%s'\n}, + $file, $!); + } elsif ($? & 127) { + $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: child died with signal %d, %s coredump\n}, + $file, ($? & 127), ($? & 128) ? 'with' : 'without'); + } else { + $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: child exited with value %d\n}, + $file, $? >> 8); + } + } + return 1; + } else { + return 1; + } + } elsif ($prefer==2) { # 2 => modules + unless ($CPAN::META->has_usable("Archive::Tar")) { + $CPAN::Frontend->mydie("Archive::Tar not installed, please install it to continue"); + } + # Make sure AT does not use uid/gid/permissions in the archive + # This leaves it to the user's umask instead + local $Archive::Tar::CHMOD = 1; + local $Archive::Tar::SAME_PERMISSIONS = 0; + # Make sure AT leaves current user as owner + local $Archive::Tar::CHOWN = 0; + my $tar = Archive::Tar->new($file,1); + my $af; # archive file + my @af; + if ($BUGHUNTING) { + # RCS 1.337 had this code, it turned out unacceptable slow but + # it revealed a bug in Archive::Tar. Code is only here to hunt + # the bug again. It should never be enabled in published code. + # GDGraph3d-0.53 was an interesting case according to Larry + # Virden. + warn(">>>Bughunting code enabled<<< " x 20); + for $af ($tar->list_files) { + if ($af =~ m!^(/|\.\./)!) { + $CPAN::Frontend->mydie("ALERT: Archive contains ". + "illegal member [$af]"); + } + $CPAN::Frontend->myprint("$af\n"); + $tar->extract($af); # slow but effective for finding the bug + return if $CPAN::Signal; + } + } else { + for $af ($tar->list_files) { + if ($af =~ m!^(/|\.\./)!) { + $CPAN::Frontend->mydie("ALERT: Archive contains ". + "illegal member [$af]"); + } + if ($tar_verb eq "v" || $tar_verb eq "vv") { + $CPAN::Frontend->myprint("$af\n"); + } + push @af, $af; + return if $CPAN::Signal; + } + $tar->extract(@af) or + $CPAN::Frontend->mydie("Could not untar with Archive::Tar."); + } + + Mac::BuildTools::convert_files([$tar->list_files], 1) + if ($^O eq 'MacOS'); + + return 1; + } +} + +sub unzip { + my($self) = @_; + my $file = $self->{FILE}; + if ($CPAN::META->has_inst("Archive::Zip")) { + # blueprint of the code from Archive::Zip::Tree::extractTree(); + my $zip = Archive::Zip->new(); + my $status; + $status = $zip->read($file); + $CPAN::Frontend->mydie("Read of file[$file] failed\n") + if $status != Archive::Zip::AZ_OK(); + $CPAN::META->debug("Successfully read file[$file]") if $CPAN::DEBUG; + my @members = $zip->members(); + for my $member ( @members ) { + my $af = $member->fileName(); + if ($af =~ m!^(/|\.\./)!) { + $CPAN::Frontend->mydie("ALERT: Archive contains ". + "illegal member [$af]"); + } + $status = $member->extractToFileNamed( $af ); + $CPAN::META->debug("af[$af]status[$status]") if $CPAN::DEBUG; + $CPAN::Frontend->mydie("Extracting of file[$af] from zipfile[$file] failed\n") if + $status != Archive::Zip::AZ_OK(); + return if $CPAN::Signal; + } + return 1; + } elsif ( my $unzip = $CPAN::Config->{unzip} ) { + my @system = ($unzip, $file); + return system(@system) == 0; + } + else { + $CPAN::Frontend->mydie(<<"END"); + +Can't unzip '$file': + +You have not configured an 'unzip' program and do not have Archive::Zip +installed. Please either install Archive::Zip or else configure 'unzip' +by running the command 'o conf init unzip' from the CPAN shell prompt. + +END + } +} + +1; + +__END__ + +=head1 NAME + +CPAN::Tarzip - internal handling of tar archives for CPAN.pm + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/URL.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/URL.pm new file mode 100644 index 0000000000..52b42eec88 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/URL.pm @@ -0,0 +1,31 @@ +# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- +# vim: ts=4 sts=4 sw=4: +package CPAN::URL; +use overload '""' => "as_string", fallback => 1; +# accessors: TEXT(the url string), FROM(DEF=>defaultlist,USER=>urllist), +# planned are things like age or quality + +use vars qw( + $VERSION +); +$VERSION = "5.5"; + +sub new { + my($class,%args) = @_; + bless { + %args + }, $class; +} +sub as_string { + my($self) = @_; + $self->text; +} +sub text { + my($self,$set) = @_; + if (defined $set) { + $self->{TEXT} = $set; + } + $self->{TEXT}; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/CPAN/Version.pm b/tools/msys/usr/share/perl5/site_perl/CPAN/Version.pm new file mode 100644 index 0000000000..fa75221d9d --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/CPAN/Version.pm @@ -0,0 +1,177 @@ +package CPAN::Version; + +use strict; +use vars qw($VERSION); +$VERSION = "5.5003"; + +# CPAN::Version::vcmp courtesy Jost Krieger +sub vcmp { + my($self,$l,$r) = @_; + local($^W) = 0; + CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; + + # treat undef as zero + $l = 0 if $l eq 'undef'; + $r = 0 if $r eq 'undef'; + + return 0 if $l eq $r; # short circuit for quicker success + + for ($l,$r) { + s/_//g; + } + CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; + for ($l,$r) { + next unless tr/.// > 1 || /^v/; + s/^v?/v/; + 1 while s/\.0+(\d)/.$1/; # remove leading zeroes per group + } + CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; + if ($l=~/^v/ <=> $r=~/^v/) { + for ($l,$r) { + next if /^v/; + $_ = $self->float2vv($_); + } + } + CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; + my $lvstring = "v0"; + my $rvstring = "v0"; + if ($] >= 5.006 + && $l =~ /^v/ + && $r =~ /^v/) { + $lvstring = $self->vstring($l); + $rvstring = $self->vstring($r); + CPAN->debug(sprintf "lv[%vd] rv[%vd]", $lvstring, $rvstring) if $CPAN::DEBUG; + } + + return ( + ($l ne "undef") <=> ($r ne "undef") + || + $lvstring cmp $rvstring + || + $l <=> $r + || + $l cmp $r + ); +} + +sub vgt { + my($self,$l,$r) = @_; + $self->vcmp($l,$r) > 0; +} + +sub vlt { + my($self,$l,$r) = @_; + $self->vcmp($l,$r) < 0; +} + +sub vge { + my($self,$l,$r) = @_; + $self->vcmp($l,$r) >= 0; +} + +sub vle { + my($self,$l,$r) = @_; + $self->vcmp($l,$r) <= 0; +} + +sub vstring { + my($self,$n) = @_; + $n =~ s/^v// or die "CPAN::Version::vstring() called with invalid arg [$n]"; + pack "U*", split /\./, $n; +} + +# vv => visible vstring +sub float2vv { + my($self,$n) = @_; + my($rev) = int($n); + $rev ||= 0; + my($mantissa) = $n =~ /\.(\d{1,12})/; # limit to 12 digits to limit + # architecture influence + $mantissa ||= 0; + $mantissa .= "0" while length($mantissa)%3; + my $ret = "v" . $rev; + while ($mantissa) { + $mantissa =~ s/(\d{1,3})// or + die "Panic: length>0 but not a digit? mantissa[$mantissa]"; + $ret .= ".".int($1); + } + # warn "n[$n]ret[$ret]"; + $ret =~ s/(\.0)+/.0/; # v1.0.0 => v1.0 + $ret; +} + +sub readable { + my($self,$n) = @_; + $n =~ /^([\w\-\+\.]+)/; + + return $1 if defined $1 && length($1)>0; + # if the first user reaches version v43, he will be treated as "+". + # We'll have to decide about a new rule here then, depending on what + # will be the prevailing versioning behavior then. + + if ($] < 5.006) { # or whenever v-strings were introduced + # we get them wrong anyway, whatever we do, because 5.005 will + # have already interpreted 0.2.4 to be "0.24". So even if he + # indexer sends us something like "v0.2.4" we compare wrongly. + + # And if they say v1.2, then the old perl takes it as "v12" + + if (defined $CPAN::Frontend) { + $CPAN::Frontend->mywarn("Suspicious version string seen [$n]\n"); + } else { + warn("Suspicious version string seen [$n]\n"); + } + return $n; + } + my $better = sprintf "v%vd", $n; + CPAN->debug("n[$n] better[$better]") if $CPAN::DEBUG; + return $better; +} + +1; + +__END__ + +=head1 NAME + +CPAN::Version - utility functions to compare CPAN versions + +=head1 SYNOPSIS + + use CPAN::Version; + + CPAN::Version->vgt("1.1","1.1.1"); # 1 bc. 1.1 > 1.001001 + + CPAN::Version->vlt("1.1","1.1"); # 0 bc. 1.1 not < 1.1 + + CPAN::Version->vcmp("1.1","1.1.1"); # 1 bc. first is larger + + CPAN::Version->vcmp("1.1.1","1.1"); # -1 bc. first is smaller + + CPAN::Version->readable(v1.2.3); # "v1.2.3" + + CPAN::Version->vstring("v1.2.3"); # v1.2.3 + + CPAN::Version->float2vv(1.002003); # "v1.2.3" + +=head1 DESCRIPTION + +This module mediates between some version that perl sees in a package +and the version that is published by the CPAN indexer. + +It's only written as a helper module for both CPAN.pm and CPANPLUS.pm. + +As it stands it predates version.pm but has the same goal: make +version strings visible and comparable. + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut + +# Local Variables: +# mode: cperl +# cperl-indent-level: 4 +# End: diff --git a/tools/msys/usr/share/perl5/site_perl/MLDBM.pm b/tools/msys/usr/share/perl5/site_perl/MLDBM.pm new file mode 100644 index 0000000000..6d5bcc9449 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/MLDBM.pm @@ -0,0 +1,555 @@ +# +# MLDBM.pm +# +# store multi-level hash structure in single level tied hash (read DBM) +# +# Documentation at the __END__ +# +# Gurusamy Sarathy <gsar@umich.edu> +# Raphael Manfredi <Raphael_Manfredi@grenoble.hp.com> +# + +require 5.005; +use strict; + +#################################################################### +package MLDBM::Serializer; ## deferred + +$MLDBM::Serializer::VERSION = $MLDBM::Serializer::VERSION = '2.05'; +use Carp; + +# +# The serialization interface comprises of just three methods: +# new(), serialize() and deserialize(). Only the last two are +# _required_ to be implemented by any MLDBM serialization wrapper. +# + +sub new { bless {}, shift }; + +sub serialize { confess "deferred" }; + +sub deserialize { confess "deferred" }; + + +# +# Attributes: +# +# dumpmeth: +# the preferred dumping method. +# +# removetaint: +# untainting flag; when true, data will be untainted after +# extraction from the database. +# +# key: +# the magic string used to recognize non-natively stored data. +# +# Attribute access methods: +# +# These defaults allow readonly access. Sub-class may override +# them to allow write access if any of these attributes +# makes sense for it. +# + +sub DumpMeth { + my $s = shift; + confess "can't set dumpmeth with " . ref($s) if @_; + $s->_attrib('dumpmeth'); +} + +sub RemoveTaint { + my $s = shift; + confess "can't set untaint with " . ref($s) if @_; + $s->_attrib('removetaint'); +} + +sub Key { + my $s = shift; + confess "can't set key with " . ref($s) if @_; + $s->_attrib('key'); +} + +sub _attrib { + my ($s, $a, $v) = @_; + if (ref $s and @_ > 2) { + $s->{$a} = $v; + return $s; + } + $s->{$a}; +} + +#################################################################### +package MLDBM; + +$MLDBM::VERSION = $MLDBM::VERSION = '2.05'; + +require Tie::Hash; +@MLDBM::ISA = 'Tie::Hash'; + +use Carp; + +# +# the DB package to use (we default to SDBM since it comes with perl) +# you might want to change this default to something more efficient +# like DB_File (you can always override it in the use list) +# +$MLDBM::UseDB = "SDBM_File" unless $MLDBM::UseDB; +$MLDBM::Serializer = 'Data::Dumper' unless $MLDBM::Serializer; +$MLDBM::Key = '$MlDbM' unless $MLDBM::Key; +$MLDBM::DumpMeth = "" unless $MLDBM::DumpMeth; +$MLDBM::RemoveTaint = 0 unless $MLDBM::RemoveTaint; + +# +# A private way to load packages at runtime. +my $loadpack = sub { + my $pack = shift; + $pack =~ s|::|/|g; + $pack .= ".pm"; + eval { require $pack }; + if ($@) { + carp "MLDBM error: " . + "Please make sure $pack is a properly installed package.\n" . + "\tPerl says: \"$@\""; + return undef; + } + 1; +}; + + +# +# TIEHASH interface methods +# +sub TIEHASH { + my $c = shift; + my $s = bless {}, $c; + + # + # Create the right serializer object. + my $szr = $MLDBM::Serializer; + unless (ref $szr) { + $szr = "MLDBM::Serializer::$szr" # allow convenient short names + unless $szr =~ /^MLDBM::Serializer::/; + $loadpack->($szr) or return undef; + $szr = $szr->new($MLDBM::DumpMeth, + $MLDBM::RemoveTaint, + $MLDBM::Key); + } + $s->Serializer($szr); + + # + # Create the right TIEHASH object. + my $db = $MLDBM::UseDB; + unless (ref $db) { + $loadpack->($db) or return undef; + $db = $db->TIEHASH(@_) + or carp "MLDBM error: Second level tie failed, \"$!\"" + and return undef; + } + $s->UseDB($db); + + return $s; +} + +sub FETCH { + my ($s, $k) = @_; + my $ret = $s->{DB}->FETCH($k); + $s->{SR}->deserialize($ret); +} + +sub STORE { + my ($s, $k, $v) = @_; + $v = $s->{SR}->serialize($v); + $s->{DB}->STORE($k, $v); +} + +sub DELETE { my $s = shift; $s->{DB}->DELETE(@_); } +sub FIRSTKEY { my $s = shift; $s->{DB}->FIRSTKEY(@_); } +sub NEXTKEY { my $s = shift; $s->{DB}->NEXTKEY(@_); } +sub EXISTS { my $s = shift; $s->{DB}->EXISTS(@_); } +sub CLEAR { my $s = shift; $s->{DB}->CLEAR(@_); } + +sub new { &TIEHASH } + +# +# delegate messages to the underlying DBM +# +sub AUTOLOAD { + return if $MLDBM::AUTOLOAD =~ /::DESTROY$/; + my $s = shift; + if (ref $s) { # twas a method call + my $dbname = ref($s->{DB}); + # permit inheritance + $MLDBM::AUTOLOAD =~ s/^.*::([^:]+)$/$dbname\:\:$1/; + $s->{DB}->$MLDBM::AUTOLOAD(@_); + } +} + +# +# delegate messages to the underlying Serializer +# +sub DumpMeth { my $s = shift; $s->{SR}->DumpMeth(@_); } +sub RemoveTaint { my $s = shift; $s->{SR}->RemoveTaint(@_); } +sub Key { my $s = shift; $s->{SR}->Key(@_); } + +# +# get/set the DB object +# +sub UseDB { my $s = shift; @_ ? ($s->{DB} = shift) : $s->{DB}; } + +# +# get/set the Serializer object +# +sub Serializer { my $s = shift; @_ ? ($s->{SR} = shift) : $s->{SR}; } + +# +# stuff to do at 'use' time +# +sub import { + my ($pack, $dbpack, $szr, $dumpmeth, $removetaint, $key) = @_; + $MLDBM::UseDB = $dbpack if defined $dbpack and $dbpack; + $MLDBM::Serializer = $szr if defined $szr and $szr; + # undocumented, may change! + $MLDBM::DumpMeth = $dumpmeth if defined $dumpmeth; + $MLDBM::RemoveTaint = $removetaint if defined $removetaint; + $MLDBM::Key = $key if defined $key and $key; +} + +# helper subroutine for tests to compare to arbitrary data structures +# for equivalency +sub _compare { + use vars qw(%compared); + local %compared; + return _cmp(@_); +} + +sub _cmp { + my($a, $b) = @_; + + # catch circular loops + return(1) if $compared{$a.'&*&*&*&*&*'.$b}++; +# print "$a $b\n"; +# print &Data::Dumper::Dumper($a, $b); + + if(ref($a) and ref($a) eq ref($b)) { + if(eval { @$a }) { +# print "HERE ".@$a." ".@$b."\n"; + @$a == @$b or return 0; +# print @$a, ' ', @$b, "\n"; +# print "HERE2\n"; + + for(0..@$a-1) { + &_cmp($a->[$_], $b->[$_]) or return 0; + } + } elsif(eval { %$a }) { + keys %$a == keys %$b or return 0; + for (keys %$a) { + &_cmp($a->{$_}, $b->{$_}) or return 0; + } + } elsif(eval { $$a }) { + &_cmp($$a, $$b) or return 0; + } else { + die("data $a $b not handled"); + } + return 1; + } elsif(! ref($a) and ! ref($b)) { + return ($a eq $b); + } else { + return 0; + } + +} + +1; + +__END__ + +=head1 NAME + +MLDBM - store multi-level Perl hash structure in single level tied hash + +=head1 SYNOPSIS + + use MLDBM; # this gets the default, SDBM + #use MLDBM qw(DB_File FreezeThaw); # use FreezeThaw for serializing + #use MLDBM qw(DB_File Storable); # use Storable for serializing + + $dbm = tie %o, 'MLDBM' [..other DBM args..] or die $!; + +=head1 DESCRIPTION + +This module can serve as a transparent interface to any TIEHASH package +that is required to store arbitrary perl data, including nested references. +Thus, this module can be used for storing references and other arbitrary data +within DBM databases. + +It works by serializing the references in the hash into a single string. In the +underlying TIEHASH package (usually a DBM database), it is this string that +gets stored. When the value is fetched again, the string is deserialized to +reconstruct the data structure into memory. + +For historical and practical reasons, it requires the B<Data::Dumper> package, +available at any CPAN site. B<Data::Dumper> gives you really nice-looking dumps of +your data structures, in case you wish to look at them on the screen, and +it was the only serializing engine before version 2.00. However, as of version +2.00, you can use any of B<Data::Dumper>, B<FreezeThaw> or B<Storable> to +perform the underlying serialization, as hinted at by the L<SYNOPSIS> overview +above. Using B<Storable> is usually much faster than the other methods. + +See the L<BUGS> section for important limitations. + +=head2 Changing the Defaults + +B<MLDBM> relies on an underlying TIEHASH implementation (usually a +DBM package), and an underlying serialization package. The respective +defaults are B<SDBM_File> and B<Data::Dumper>. Both of these defaults +can be changed. Changing the B<SDBM_File> default is strongly recommended. +See L<WARNINGS> below. + +Three serialization wrappers are currently supported: B<Data::Dumper>, +B<Storable>, and B<FreezeThaw>. Additional serializers can be +supported by writing a wrapper that implements the interface required by +B<MLDBM::Serializer>. See the supported wrappers and the B<MLDBM::Serializer> +source for details. + +In the following, I<$OBJ> stands for the tied object, as in: + + $obj = tie %o, .... + $obj = tied %o; + +=over 4 + +=item $MLDBM::UseDB I<or> I<$OBJ>->UseDB(I<[TIEDOBJECT]>) + +The global C<$MLDBM::UseDB> can be set to default to something other than +C<SDBM_File>, in case you have a more efficient DBM, or if you want to use +this with some other TIEHASH implementation. Alternatively, you can specify +the name of the package at C<use> time, as the first "parameter". +Nested module names can be specified as "Foo::Bar". + +The corresponding method call returns the underlying TIEHASH object when +called without arguments. It can be called with any object that +implements Perl's TIEHASH interface, to set that value. + +=item $MLDBM::Serializer I<or> I<$OBJ>->Serializer(I<[SZROBJECT]>) + +The global C<$MLDBM::Serializer> can be set to the name of the serializing +package to be used. Currently can be set to one of C<Data::Dumper>, +C<Storable>, or C<FreezeThaw>. Defaults to C<Data::Dumper>. Alternatively, +you can specify the name of the serializer package at C<use> time, as the +second "parameter". + +The corresponding method call returns the underlying MLDBM serializer object +when called without arguments. It can be called with an object that +implements the MLDBM serializer interface, to set that value. + +=back + +=head2 Controlling Serializer Properties + +These methods are meant to supply an interface to the properties of the +underlying serializer used. Do B<not> call or set them without +understanding the consequences in full. The defaults are usually sensible. + +Not all of these necessarily apply to all the supplied serializers, so we +specify when to apply them. Failure to respect this will usually lead to +an exception. + +=over 4 + +=item $MLDBM::DumpMeth I<or> I<$OBJ>->DumpMeth(I<[METHNAME]>) + +If the serializer provides alternative serialization methods, this +can be used to set them. + +With B<Data::Dumper> (which offers a pure Perl and an XS verion +of its serializing routine), this is set to C<Dumpxs> by default if that +is supported in your installation. Otherwise, defaults to the slower +C<Dump> method. + +With B<Storable>, a value of C<portable> requests that serialization be +architecture neutral, i.e. the deserialization can later occur on another +platform. Of course, this only makes sense if your database files are +themselves architecture neutral. By default, native format is used for +greater serializing speed in B<Storable>. Both B<Data::Dumper> and +B<FreezeThaw> are always architecture neutral. + +B<FreezeThaw> does not honor this attribute. + +=item $MLDBM::Key I<or> I<$OBJ>->Key(I<[KEYSTRING]>) + +If the serializer only deals with part of the data (perhaps because +the TIEHASH object can natively store some types of data), it may need +a unique key string to recognize the data it handles. This can be used +to set that string. Best left alone. + +Defaults to the magic string used to recognize MLDBM data. It is a six +character wide, unique string. This is best left alone, unless you know +what you are doing. + +B<Storable> and B<FreezeThaw> do not honor this attribute. + +=item $MLDBM::RemoveTaint I<or> I<$OBJ>->RemoveTaint(I<[BOOL]>) + +If the serializer can optionally untaint any retrieved data subject to +taint checks in Perl, this can be used to request that feature. Data +that comes from external sources (like disk-files) must always be +viewed with caution, so use this only when you are sure that that is +not an issue. + +B<Data::Dumper> uses C<eval()> to deserialize and is therefore subject to +taint checks. Can be set to a true value to make the B<Data::Dumper> +serializer untaint the data retrieved. It is not enabled by default. +Use with care. + +B<Storable> and B<FreezeThaw> do not honor this attribute. + +=back + +=head1 EXAMPLES + +Here is a simple example. Note that does not depend upon the underlying +serializing package--most real life examples should not, usually. + + use MLDBM; # this gets SDBM and Data::Dumper + #use MLDBM qw(SDBM_File Storable); # SDBM and Storable + use Fcntl; # to get 'em constants + + $dbm = tie %o, 'MLDBM', 'testmldbm', O_CREAT|O_RDWR, 0640 or die $!; + + $c = [\ 'c']; + $b = {}; + $a = [1, $b, $c]; + $b->{a} = $a; + $b->{b} = $a->[1]; + $b->{c} = $a->[2]; + @o{qw(a b c)} = ($a, $b, $c); + + # + # to see what was stored + # + use Data::Dumper; + print Data::Dumper->Dump([@o{qw(a b c)}], [qw(a b c)]); + + # + # to modify data in a substructure + # + $tmp = $o{a}; + $tmp->[0] = 'foo'; + $o{a} = $tmp; + + # + # can access the underlying DBM methods transparently + # + #print $dbm->fd, "\n"; # DB_File method + +Here is another small example using Storable, in a portable format: + + use MLDBM qw(DB_File Storable); # DB_File and Storable + + tie %o, 'MLDBM', 'testmldbm', O_CREAT|O_RDWR, 0640 or die $!; + + (tied %o)->DumpMeth('portable'); # Ask for portable binary + $o{'ENV'} = \%ENV; # Stores the whole environment + + +=head1 BUGS + +=over 4 + +=item 1. + +Adding or altering substructures to a hash value is not entirely transparent +in current perl. If you want to store a reference or modify an existing +reference value in the DBM, it must first be retrieved and stored in a +temporary variable for further modifications. In particular, something like +this will NOT work properly: + + $mldb{key}{subkey}[3] = 'stuff'; # won't work + +Instead, that must be written as: + + $tmp = $mldb{key}; # retrieve value + $tmp->{subkey}[3] = 'stuff'; + $mldb{key} = $tmp; # store value + +This limitation exists because the perl TIEHASH interface currently has no +support for multidimensional ties. + +=item 2. + +The B<Data::Dumper> serializer uses eval(). A lot. Try the B<Storable> +serializer, which is generally the most efficient. + +=back + +=head1 WARNINGS + +=over 4 + +=item 1. + +Many DBM implementations have arbitrary limits on the size of records +that can be stored. For example, SDBM and many ODBM or NDBM +implementations have a default limit of 1024 bytes for the size of a +record. MLDBM can easily exceed these limits when storing large data +structures, leading to mysterious failures. Although SDBM_File is +used by MLDBM by default, it is not a good choice if you're storing +large data structures. Berkeley DB and GDBM both do not have these +limits, so I recommend using either of those instead. + +=item 2. + +MLDBM does well with data structures that are not too deep and not +too wide. You also need to be careful about how many C<FETCH>es your +code actually ends up doing. Meaning, you should get the most mileage +out of a C<FETCH> by holding on to the highest level value for as long +as you need it. Remember that every toplevel access of the tied hash, +for example C<$mldb{foo}>, translates to a MLDBM C<FETCH()> call. + +Too often, people end up writing something like this: + + tie %h, 'MLDBM', ...; + for my $k (keys %{$h{something}}) { + print $h{something}{$k}[0]{foo}{bar}; # FETCH _every_ time! + } + +when it should be written this for efficiency: + + tie %h, 'MLDBM', ...; + my $root = $h{something}; # FETCH _once_ + for my $k (keys %$root) { + print $k->[0]{foo}{bar}; + } + + +=back + +=head1 AUTHORS + +Gurusamy Sarathy <F<gsar@umich.edu>>. + +Support for multiple serializing packages by +Raphael Manfredi <F<Raphael_Manfredi@grenoble.hp.com>>. + +Test suite fixes for perl 5.8.0 done by Josh Chamas. + +Copyright (c) 1995-98 Gurusamy Sarathy. All rights reserved. + +Copyright (c) 1998 Raphael Manfredi. + +Copyright (c) 2002 Josh Chamas, Chamas Enterprises Inc. + +Copyright (c) 2010-2013 Alexandr Ciornii (alexchorny@gmail.com). + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=head1 VERSION + +Version 2.05 + +=head1 SEE ALSO + +perl(1), perltie(1), perlfunc(1), L<Data::Dumper>, L<FreezeThaw>, L<Storable>, L<DBM::Deep>, L<MLDBM::Serializer::JSON>. + +=cut diff --git a/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/Data/Dumper.pm b/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/Data/Dumper.pm new file mode 100644 index 0000000000..54db4ca03a --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/Data/Dumper.pm @@ -0,0 +1,65 @@ +#################################################################### +package MLDBM::Serializer::Data::Dumper; +BEGIN { @MLDBM::Serializer::Data::Dumper::ISA = qw(MLDBM::Serializer) } + +use Data::Dumper '2.08'; # Backward compatibility +use Carp; + +# +# Create a Data::Dumper serializer object. +# +sub new { + my $self = shift->SUPER::new(); + my $meth = shift || ""; + $meth = (defined(&Data::Dumper::Dumpxs) ? 'Dumpxs' : 'Dump') + unless $meth =~ /^Dump(xs)?$/; + $self->DumpMeth($meth); + $self->RemoveTaint(shift); + $self->Key(shift); + $self; +} + +# +# Serialize $val if it is a reference, or if it does begin with our magic +# key string, since then at retrieval time we expect a Data::Dumper string. +# Otherwise, return the scalar value. +# +sub serialize { + my $self = shift; + my ($val) = @_; + return undef unless defined $val; + return $val unless ref($val) or $val =~ m|^\Q$self->{'key'}|o; + my $dumpmeth = $self->{'dumpmeth'}; + local $Data::Dumper::Indent = 0; + local $Data::Dumper::Purity = 1; + local $Data::Dumper::Terse = 1; + return $self->{'key'} . Data::Dumper->$dumpmeth([$val], ['M']); +} + +# +# If the value is undefined or does not begin with our magic key string, +# return it as-is. Otherwise, we need to recover the underlying data structure. +# +sub deserialize { + my $self = shift; + my ($val) = @_; + return undef unless defined $val; + return $val unless $val =~ s|^\Q$self->{'key'}||o; + my $M = ""; + ($val) = $val =~ /^(.*)$/s if $self->{'removetaint'}; + # Disambiguate hashref (perl may treat it as a block) + my $N = eval($val =~ /^\{/ ? '+'.$val : $val); + return $M ? $M : $N unless $@; + carp "MLDBM error: $@\twhile evaluating:\n $val"; +} + +sub DumpMeth { my $s = shift; $s->_attrib('dumpmeth', @_); } +sub RemoveTaint { my $s = shift; $s->_attrib('removetaint', @_); } +sub Key { my $s = shift; $s->_attrib('key', @_); } + +# avoid used only once warnings +{ + local $Data::Dumper::Terse; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/FreezeThaw.pm b/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/FreezeThaw.pm new file mode 100644 index 0000000000..34caf30014 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/FreezeThaw.pm @@ -0,0 +1,16 @@ +#################################################################### +package MLDBM::Serializer::FreezeThaw; +BEGIN { @MLDBM::Serializer::FreezeThaw::ISA = qw(MLDBM::Serializer) } + +use FreezeThaw; + +sub serialize { + return FreezeThaw::freeze($_[1]); +} + +sub deserialize { + my ($obj) = FreezeThaw::thaw($_[1]); + return $obj; +} + +1; diff --git a/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/Storable.pm b/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/Storable.pm new file mode 100644 index 0000000000..62d33463e2 --- /dev/null +++ b/tools/msys/usr/share/perl5/site_perl/MLDBM/Serializer/Storable.pm @@ -0,0 +1,41 @@ +#################################################################### +package MLDBM::Serializer::Storable; +BEGIN { @MLDBM::Serializer::Storable::ISA = qw(MLDBM::Serializer) } + +use Storable; + +sub new { + my $self = shift->SUPER::new(); + $self->DumpMeth(shift); + # Storable doesn't honor other attributes + $self; +} + +# +# Serialize a reference to supplied value +# +sub serialize { + my $self = shift; + my $dumpmeth = $self->{'_dumpsub_'}; + &$dumpmeth(\$_[0]); +} + +# +# Deserialize and de-reference +# +sub deserialize { + my $obj = Storable::thaw($_[1]); # Does not care whether portable + defined($obj) ? $$obj : undef; +} + +# +# Change dump method when portability is requested +# +sub DumpMeth { + my $self = shift; + $self->{'_dumpsub_'} = + ($_[0] && $_[0] eq 'portable' ? \&Storable::nfreeze : \&Storable::freeze); + $self->_attrib('dumpmeth', @_); +} + +1; -- GitLab