diff options
author | markm <markm@FreeBSD.org> | 2000-06-25 11:04:01 +0000 |
---|---|---|
committer | markm <markm@FreeBSD.org> | 2000-06-25 11:04:01 +0000 |
commit | 2618fad5bbb2d0182eb31ed805c41b543c513940 (patch) | |
tree | 52ba93338b13aefd02a0055304a9eccfa0e049f5 /contrib/perl5/lib/CPAN.pm | |
parent | 77644ee620b6a79cf8c538abaf7cd301a875528d (diff) | |
download | FreeBSD-src-2618fad5bbb2d0182eb31ed805c41b543c513940.zip FreeBSD-src-2618fad5bbb2d0182eb31ed805c41b543c513940.tar.gz |
Vendor import of Perl 5.006
Diffstat (limited to 'contrib/perl5/lib/CPAN.pm')
-rw-r--r-- | contrib/perl5/lib/CPAN.pm | 727 |
1 files changed, 487 insertions, 240 deletions
diff --git a/contrib/perl5/lib/CPAN.pm b/contrib/perl5/lib/CPAN.pm index 2276943..84dfd31 100644 --- a/contrib/perl5/lib/CPAN.pm +++ b/contrib/perl5/lib/CPAN.pm @@ -6,13 +6,13 @@ use vars qw{$Try_autoload $Frontend $Defaultsite }; #}; -$VERSION = '1.48'; +$VERSION = '1.52'; -# $Id: CPAN.pm,v 1.260 1999/03/06 19:31:02 k Exp $ +# $Id: CPAN.pm,v 1.276 2000/01/08 15:29:46 k Exp $ # only used during development: $Revision = ""; -# $Revision = "[".substr(q$Revision: 1.260 $, 10)."]"; +# $Revision = "[".substr(q$Revision: 1.276 $, 10)."]"; use Carp (); use Config (); @@ -61,7 +61,7 @@ use strict qw(vars); @CPAN::ISA = qw(CPAN::Debug Exporter); @EXPORT = qw( - autobundle bundle expand force get + autobundle bundle expand force get cvs_import install make readme recompile shell test clean ); @@ -90,7 +90,7 @@ sub AUTOLOAD { #-> sub CPAN::shell ; sub shell { my($self) = @_; - $Suppress_readline ||= ! -t STDIN; + $Suppress_readline = ! -t STDIN unless defined $Suppress_readline; CPAN::Config->load unless $CPAN::Config_loaded++; my $prompt = "cpan> "; @@ -113,6 +113,12 @@ sub shell { $readline::rl_completion_function = $readline::rl_completion_function = 'CPAN::Complete::cpl'; } + # $term->OUT is autoflushed anyway + my $odef = select STDERR; + $| = 1; + select STDOUT; + $| = 1; + select $odef; } no strict; @@ -120,7 +126,8 @@ sub shell { my $getcwd; $getcwd = $CPAN::Config->{'getcwd'} || 'cwd'; my $cwd = CPAN->$getcwd(); - my $try_detect_readline = $term->ReadLine eq "Term::ReadLine::Stub"; + my $try_detect_readline; + $try_detect_readline = $term->ReadLine eq "Term::ReadLine::Stub" if $term; my $rl_avail = $Suppress_readline ? "suppressed" : ($term->ReadLine ne "Term::ReadLine::Stub") ? "enabled" : "available (try ``install Bundle::CPAN'')"; @@ -190,7 +197,8 @@ ReadLine support $rl_avail my $redef; local($SIG{__WARN__}) = CPAN::Shell::dotdot_onreload(\$redef); require Term::ReadLine; - $CPAN::Frontend->myprint("\n$redef subroutines in Term::ReadLine redefined\n"); + $CPAN::Frontend->myprint("\n$redef subroutines in ". + "Term::ReadLine redefined\n"); goto &shell; } } @@ -285,7 +293,7 @@ sub try_dot_al { $pkg =~ s|::|/|g; if (defined($name=$INC{"$pkg.pm"})) { - $name =~ s|^(.*)$pkg\.pm$|$1auto/$pkg/$func.al|; + $name =~ s|^(.*)$pkg\.pm\z|$1auto/$pkg/$func.al|s; $name = undef unless (-r $name); } unless (defined $name) @@ -301,7 +309,7 @@ sub try_dot_al { *$autoload = sub {}; $ok = 1; } else { - if ($name =~ s{(\w{12,})\.al$}{substr($1,0,11).".al"}e){ + if ($name =~ s{(\w{12,})\.al\z}{substr($1,0,11).".al"}e){ eval {local $SIG{__DIE__};require $name}; } if ($@){ @@ -539,7 +547,40 @@ You may want to kill it and delete the lockfile, maybe. On UNIX try: } } } - File::Path::mkpath($CPAN::Config->{cpan_home}); + 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 $diemess = qq{ +Your configuration suggests "$dotcpan" as your +CPAN.pm working directory. I could not create this directory due +to this error: $firsterror\n}; + $diemess .= qq{ +As "$dotcpan" is a symlink to "$symlinkcpan", +I tried to create that, but I failed with this error: $seconderror +} if $seconderror; + $diemess .= qq{ +Please make sure the directory exists and is writable. +}; + $CPAN::Frontend->mydie($diemess); + } + } my $fh; unless ($fh = FileHandle->new(">$lockfile")) { if ($! =~ /Permission/) { @@ -580,6 +621,27 @@ or print "Caught SIGINT\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 optinion 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 + $SIG{'__DIE__'} = \&cleanup; $self->debug("Signal handler set.") if $CPAN::DEBUG; } @@ -784,7 +846,7 @@ sub disk_usage { if ($^O eq 'MacOS') { require Mac::Files; my $cat = Mac::Files::FSpGetCatInfo($_); - $Du += $cat->ioFlLgLen() + $cat->ioFlRLgLen(); + $Du += $cat->ioFlLgLen() + $cat->ioFlRLgLen() if $cat; } else { $Du += (-s _); } @@ -1103,7 +1165,8 @@ Known options: commit commit session changes to disk init go through a dialog to set all parameters -You may edit key values in the follow fashion: +You may edit key values in the follow fashion (the "o" is a literal +letter o): o conf build_cache 15 @@ -1149,29 +1212,29 @@ sub h { $CPAN::Frontend->myprint("Detailed help not yet implemented\n"); } else { $CPAN::Frontend->myprint(q{ -command arguments description -a string authors -b or display bundles -d /regex/ info distributions -m or about modules -i none anything of above - -r as reinstall recommendations -u above uninstalled distributions -See manpage for autobundle, recompile, force, look, etc. - -make make -test modules, make test (implies make) -install dists, bundles, make install (implies test) -clean "r" or "u" make clean -readme display the README file - -reload index|cpan load most recent indices/CPAN.pm -h or ? display this menu -o various set and query options -! perl-code eval a perl command -q quit the shell subroutine -}); +Display Information + a authors + b string display bundles + d or info distributions + m /regex/ about modules + i or anything of above + r none reinstall recommendations + u uninstalled distributions + +Download, Test, Make, Install... + get download + make make (implies get) + test modules, make test (implies make) + install dists, bundles make install (implies test) + clean make clean + look open subshell in these dists' directories + readme display these dists' README files + +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 force cmd unconditionally do cmd}); } } @@ -1190,7 +1253,7 @@ sub b { my($entry); for $entry ($dh->read) { next if -d MM->catdir($bdir,$entry); - next unless $entry =~ s/\.pm$//; + next unless $entry =~ s/\.pm\z//; $CPAN::META->instance('CPAN::Bundle',"Bundle::$entry"); } } @@ -1293,10 +1356,13 @@ sub o { } } } else { - $CPAN::Frontend->myprint("Valid options for debug are ". - join(", ",sort(keys %CPAN::DEBUG), 'all'). - qq{ or a number. Completion works on the options. }. - qq{Case is ignored.\n\n}); + 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:\n"); @@ -1358,7 +1424,7 @@ index re-reads the index files\n}); sub _binary_extensions { my($self) = shift @_; my(@result,$module,%seen,%need,$headerdone); - my $isaperl = q{perl5[._-]\\d{3}(_[0-4][0-9])?\\.tar[._-]gz$}; + my $isaperl = q{perl5[._-]\\d{3}(_[0-4][0-9])?\\.tar[._-]gz\z}; for $module ($self->expand('Module','/./')) { my $file = $module->cpan_file; next if $file eq "N/A"; @@ -1562,21 +1628,34 @@ sub expand { my $class = "CPAN::$type"; my $obj; if (defined $regex) { - for $obj ( sort {$a->id cmp $b->id} $CPAN::META->all_objects($class)) { - push @m, $obj - if - $obj->id =~ /$regex/i - or + for $obj ( + sort + {$a->id cmp $b->id} + $CPAN::META->all_objects($class) + ) { + unless ($obj->id){ + # BUG, we got an empty object somewhere + CPAN->debug(sprintf( + "Empty id on obj[%s]%%[%s]", + $obj, + join(":", %$obj) + )) if $CPAN::DEBUG; + next; + } + push @m, $obj + if $obj->id =~ /$regex/i + or ( ( - $] < 5.00303 ### provide sort of compatibility with 5.003 + $] < 5.00303 ### provide sort of + ### compatibility with 5.003 || $obj->can('name') ) && $obj->name =~ /$regex/i ); - } + } } else { my($xarg) = $arg; if ( $type eq 'Bundle' ) { @@ -1670,6 +1749,15 @@ sub mydie { die "\n"; } +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 sub rematein { @@ -1680,6 +1768,7 @@ sub rematein { $pragma = $meth; $meth = shift @some; } + setup_output(); CPAN->debug("pragma[$pragma]meth[$meth] some[@some]") if $CPAN::DEBUG; my($s,@s); foreach $s (@some) { @@ -1756,6 +1845,8 @@ sub install { shift->rematein('install',@_); } sub clean { shift->rematein('clean',@_); } #-> sub CPAN::Shell::look ; sub look { shift->rematein('look',@_); } +#-> sub CPAN::Shell::cvs_import ; +sub cvs_import { shift->rematein('cvs_import',@_); } package CPAN::FTP; @@ -1932,6 +2023,9 @@ sub localize { my $ret = $self->$method(\@host_seq,$file,$aslocal); if ($ret) { $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; return $ret; } else { @@ -1985,7 +2079,7 @@ sub hosteasy { $l =~ s|^file:||; # assume they # meant # file://localhost - $l =~ s|^/|| unless -f $l; # e.g. /P: + $l =~ s|^/||s unless -f $l; # e.g. /P: } if ( -f $l && -r _) { $Thesite = $i; @@ -2012,8 +2106,11 @@ sub hosteasy { my $res = $Ua->mirror($url, $aslocal); if ($res->is_success) { $Thesite = $i; + my $now = time; + utime $now, $now, $aslocal; # download time is more + # important than upload time return $aslocal; - } elsif ($url !~ /\.gz$/) { + } elsif ($url !~ /\.gz\z/) { my $gzurl = "$url.gz"; $CPAN::Frontend->myprint("Fetching with LWP: $gzurl @@ -2050,7 +2147,7 @@ sub hosteasy { $Thesite = $i; return $aslocal; } - if ($aslocal !~ /\.gz$/) { + if ($aslocal !~ /\.gz\z/) { my $gz = "$aslocal.gz"; $CPAN::Frontend->myprint("Fetching with Net::FTP $url.gz @@ -2086,8 +2183,8 @@ sub hosthard { HOSTHARD: for $i (@$host_seq) { my $url = $CPAN::Config->{urllist}[$i] || $CPAN::Defaultsite; unless ($self->is_reachable($url)) { - $CPAN::Frontend->myprint("Skipping $url (not reachable)\n"); - next; + $CPAN::Frontend->myprint("Skipping $url (not reachable)\n"); + next; } $url .= "/" unless substr($url,-1) eq "/"; $url .= $file; @@ -2097,90 +2194,107 @@ sub hosthard { # if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { # to if ($url =~ m|^([^:]+)://(.*?)/(.*)/(.*)|) { - # proto not yet used - ($proto,$host,$dir,$getfile) = ($1,$2,$3,$4); + # 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; # who said, we could ftp anything except ftp? } + $self->debug("localizing funkyftpwise[$url]") if $CPAN::DEBUG; my($f,$funkyftp); for $f ('lynx','ncftpget','ncftp') { - next unless exists $CPAN::Config->{$f}; - $funkyftp = $CPAN::Config->{$f}; - next unless defined $funkyftp; - next if $funkyftp =~ /^\s*$/; - my($want_compressed); - my $aslocal_uncompressed; - ($aslocal_uncompressed = $aslocal) =~ s/\.gz//; - my($source_switch) = ""; - $source_switch = " -source" if $funkyftp =~ /\blynx$/; - $source_switch = " -c" if $funkyftp =~ /\bncftp$/; - $CPAN::Frontend->myprint( - qq[ + next unless exists $CPAN::Config->{$f}; + $funkyftp = $CPAN::Config->{$f}; + next unless defined $funkyftp; + next if $funkyftp =~ /^\s*$/; + my($want_compressed); + my $aslocal_uncompressed; + ($aslocal_uncompressed = $aslocal) =~ s/\.gz//; + my($source_switch) = ""; + if ($f eq "lynx"){ + $source_switch = " -source"; + } elsif ($f eq "ncftp"){ + $source_switch = " -c"; + } + my($chdir) = ""; + my($stdout_redir) = " > $aslocal_uncompressed"; + if ($f eq "ncftpget"){ + $chdir = "cd $aslocal_dir && "; + $stdout_redir = ""; + } + $CPAN::Frontend->myprint( + qq[ Trying with "$funkyftp$source_switch" to get $url ]); - my($system) = "$funkyftp$source_switch '$url' $devnull > ". - "$aslocal_uncompressed"; + my($system) = + "$chdir$funkyftp$source_switch '$url' $devnull$stdout_redir"; + $self->debug("system[$system]") if $CPAN::DEBUG; + my($wstatus); + if (($wstatus = system($system)) == 0 + && + ($f eq "lynx" ? + -s $aslocal_uncompressed # lynx returns 0 on my + # system even if it fails + : 1 + ) + ) { + if (-s $aslocal) { + # Looks good + } elsif ($aslocal_uncompressed ne $aslocal) { + # test gzip integrity + if ( + CPAN::Tarzip->gtest($aslocal_uncompressed) + ) { + rename $aslocal_uncompressed, $aslocal; + } else { + CPAN::Tarzip->gzip($aslocal_uncompressed, + "$aslocal_uncompressed.gz"); + } + } + $Thesite = $i; + return $aslocal; + } elsif ($url !~ /\.gz\z/) { + unlink $aslocal_uncompressed if + -f $aslocal_uncompressed && -s _ == 0; + my $gz = "$aslocal.gz"; + my $gzurl = "$url.gz"; + $CPAN::Frontend->myprint( + qq[ +Trying with "$funkyftp$source_switch" to get + $url.gz +]); + my($system) = "$funkyftp$source_switch '$url.gz' $devnull > ". + "$aslocal_uncompressed.gz"; $self->debug("system[$system]") if $CPAN::DEBUG; my($wstatus); if (($wstatus = system($system)) == 0 && - -s $aslocal_uncompressed # lynx returns 0 on my - # system even if it fails + -s "$aslocal_uncompressed.gz" ) { - if ($aslocal_uncompressed ne $aslocal) { - # test gzip integrity - if ( - CPAN::Tarzip->gtest($aslocal_uncompressed) - ) { - rename $aslocal_uncompressed, $aslocal; - } else { - CPAN::Tarzip->gzip($aslocal_uncompressed, - "$aslocal_uncompressed.gz"); - } - } - $Thesite = $i; - return $aslocal; - } elsif ($url !~ /\.gz$/) { - unlink $aslocal_uncompressed if - -f $aslocal_uncompressed && -s _ == 0; - my $gz = "$aslocal.gz"; - my $gzurl = "$url.gz"; - $CPAN::Frontend->myprint( - qq[ -Trying with "$funkyftp$source_switch" to get - $url.gz -]); - my($system) = "$funkyftp$source_switch '$url.gz' $devnull > ". - "$aslocal_uncompressed.gz"; - $self->debug("system[$system]") if $CPAN::DEBUG; - my($wstatus); - if (($wstatus = system($system)) == 0 - && - -s "$aslocal_uncompressed.gz" - ) { - # test gzip integrity - if (CPAN::Tarzip->gtest("$aslocal_uncompressed.gz")) { - CPAN::Tarzip->gunzip("$aslocal_uncompressed.gz", - $aslocal); - } else { - rename $aslocal_uncompressed, $aslocal; - } - $Thesite = $i; - return $aslocal; + # test gzip integrity + if (CPAN::Tarzip->gtest("$aslocal_uncompressed.gz")) { + CPAN::Tarzip->gunzip("$aslocal_uncompressed.gz", + $aslocal); } else { - unlink "$aslocal_uncompressed.gz" if - -f "$aslocal_uncompressed.gz"; + rename $aslocal_uncompressed, $aslocal; } + $Thesite = $i; + return $aslocal; } else { - my $estatus = $wstatus >> 8; - my $size = -f $aslocal ? ", left\n$aslocal with size ".-s _ : ""; - $CPAN::Frontend->myprint(qq{ + unlink "$aslocal_uncompressed.gz" if + -f "$aslocal_uncompressed.gz"; + } + } 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{ System call "$system" returned status $estatus (wstat $wstatus)$size }); - } + } } } } @@ -2208,12 +2322,12 @@ sub hosthardest { next; } my($host,$dir,$getfile) = ($1,$2,$3); - my($netrcfile,$fh); 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); @@ -2226,7 +2340,7 @@ sub hosthardest { "get $getfile $targetfile", "quit" ); - if (! $netrc->netrc) { + 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]", @@ -2463,10 +2577,10 @@ sub cpl { /^$word/, sort qw( ! a b d h i m o q r u autobundle clean - make test install force reload look + make test install force reload look cvs_import ) ); - } elsif ( $line !~ /^[\!abdhimorutl]/ ) { + } elsif ( $line !~ /^[\!abcdhimorutl]/ ) { @return = (); } elsif ($line =~ /^a\s/) { @return = cplx('CPAN::Author',$word); @@ -2474,7 +2588,7 @@ sub cpl { @return = cplx('CPAN::Bundle',$word); } elsif ($line =~ /^d\s/) { @return = cplx('CPAN::Distribution',$word); - } elsif ($line =~ /^([mru]|make|clean|test|install|readme|look)\s/ ) { + } elsif ($line =~ /^([mru]|make|clean|test|install|readme|look|cvs_import)\s/ ) { @return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word)); } elsif ($line =~ /^i\s/) { @return = cpl_any($word); @@ -2552,10 +2666,15 @@ sub reload { # 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; + $_ = 0.001 unless $_ && $_ > 0.001; } return if $last_time + $CPAN::Config->{index_expire}*86400 > $time and ! $force; + ## 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; my($debug,$t2); $last_time = $time; @@ -2675,7 +2794,7 @@ sub rd_modpacks { my($mod,$version,$dist) = split; ### $version =~ s/^\+//; - # if it is a bundle, instatiate a bundle object + # if it is a bundle, instantiate a bundle object my($bundle,$id,$userid); if ($mod eq 'CPAN' && @@ -2688,6 +2807,7 @@ sub rd_modpacks { if ($version > $CPAN::VERSION){ $CPAN::Frontend->myprint(qq{ There\'s a new CPAN.pm version (v$version) available! + [Current version is v$CPAN::VERSION] You might want to try install Bundle::CPAN reload cpan @@ -2731,12 +2851,20 @@ sub rd_modpacks { } # instantiate a distribution object - unless ($CPAN::META->exists('CPAN::Distribution',$dist)) { - $CPAN::META->instance( - 'CPAN::Distribution' => $dist - )->set( - 'CPAN_USERID' => $userid - ); + 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 + ); } return if $CPAN::Signal; @@ -2829,9 +2957,15 @@ sub as_string { $extra .= ")"; } if (ref($self->{$_}) eq "ARRAY") { # language interface? XXX - push @m, sprintf " %-12s %s%s\n", $_, "@{$self->{$_}}", $extra; + push @m, sprintf " %-12s %s%s\n", $_, "@{$self->{$_}}", $extra; + } elsif (ref($self->{$_}) eq "HASH") { + push @m, sprintf( + " %-12s %s%s\n", + $_, + join(" ",keys %{$self->{$_}}), + $extra); } else { - push @m, sprintf " %-12s %s%s\n", $_, $self->{$_}, $extra; + push @m, sprintf " %-12s %s%s\n", $_, $self->{$_}, $extra; } } join "", @m, "\n"; @@ -2843,6 +2977,12 @@ sub author { $CPAN::META->instance(CPAN::Author,$self->{CPAN_USERID})->fullname; } +sub dump { + my($self) = @_; + require Data::Dumper; + Data::Dumper::Dumper($self); +} + package CPAN::Author; #-> sub CPAN::Author::as_glimpse ; @@ -2870,6 +3010,25 @@ sub email { shift->{'EMAIL'} } package CPAN::Distribution; +#-> sub CPAN::Distribution::as_string ; +sub as_string { + my $self = shift; + $self->containsmods; + $self->SUPER::as_string(@_); +} + +#-> sub CPAN::Distribution::containsmods ; +sub containsmods { + my $self = shift; + return if exists $self->{CONTAINSMODS}; + for my $mod ($CPAN::META->all_objects("CPAN::Module")) { + my $mod_file = $mod->{CPAN_FILE} or next; + my $dist_id = $self->{ID} or next; + my $mod_id = $mod->{ID} or next; + $self->{CONTAINSMODS}{$mod_id} = undef if $mod_file eq $dist_id; + } +} + #-> sub CPAN::Distribution::called_for ; sub called_for { my($self,$id) = @_; @@ -2919,11 +3078,11 @@ sub get { $self->debug("Changed directory to tmp") if $CPAN::DEBUG; if (! $local_file) { Carp::croak "bad download, can't do anything :-(\n"; - } elsif ($local_file =~ /(\.tar\.(gz|Z)|\.tgz)$/i){ + } elsif ($local_file =~ /(\.tar\.(gz|Z)|\.tgz)\z/i){ $self->untar_me($local_file); - } elsif ( $local_file =~ /\.zip$/i ) { + } elsif ( $local_file =~ /\.zip\z/i ) { $self->unzip_me($local_file); - } elsif ( $local_file =~ /\.pm\.(gz|Z)$/) { + } elsif ( $local_file =~ /\.pm\.(gz|Z)\z/) { $self->pm2dir_me($local_file); } else { $self->{archived} = "NO"; @@ -2934,7 +3093,7 @@ sub get { # 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 $_ !~ /^\.\.?$/, $dh->read; ### MAC?? + my @readdir = grep $_ !~ /^\.\.?\z/s, $dh->read; ### MAC?? $dh->close; my ($distdir,$packagedir); if (@readdir == 1 && -d $readdir[0]) { @@ -3024,7 +3183,7 @@ sub pm2dir_me { my($self,$local_file) = @_; $self->{archived} = "pm"; my $to = File::Basename::basename($local_file); - $to =~ s/\.(gz|Z)$//; + $to =~ s/\.(gz|Z)\z//; if (CPAN::Tarzip->gunzip($local_file,$to)) { $self->{unwrapped} = "YES"; } else { @@ -3075,6 +3234,44 @@ Please define it with "o conf shell <your shell>" chdir($pwd); } +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+[^-]+\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; + my @cmd = ('cvs', '-d', $cvs_root, 'import', '-m', $cvs_log, + "$cvs_dir", $userid, "v$version"); + + my $getcwd; + $getcwd = $CPAN::Config->{'getcwd'} || 'cwd'; + my $pwd = CPAN->$getcwd(); + chdir($dir); + + $CPAN::Frontend->myprint(qq{Working directory is $dir\n}); + + $CPAN::Frontend->myprint(qq{@cmd\n}); + system(@cmd) == 0 or + $CPAN::Frontend->mydie("cvs import failed"); + chdir($pwd); +} + #-> sub CPAN::Distribution::readme ; sub readme { my($self) = @_; @@ -3146,7 +3343,7 @@ sub verifyMD5 { $lc_file = CPAN::FTP->localize("authors/id/@local", "$lc_want.gz",1); if ($lc_file) { - $lc_file =~ s/\.gz$//; + $lc_file =~ s/\.gz\z//; CPAN::Tarzip->gunzip("$lc_file.gz",$lc_file); } else { return; @@ -3193,7 +3390,7 @@ sub MD5_check_file { my $md5 = MD5->new; my($data,$ref); $ref = \$data; - while ($fh->READ($ref, 4096)){ + while ($fh->READ($ref, 4096) > 0){ $md5->add($data); } my $hexdigest = $md5->hexdigest; @@ -3257,7 +3454,7 @@ sub force { $self->{'force_update'}++; for my $att (qw( MD5_STATUS archived build_dir localfile make install unwrapped - writemakefile have_sponsored + writemakefile )) { delete $self->{$att}; } @@ -3271,8 +3468,8 @@ sub isa_perl { ([._-]) (\d{3}(_[0-4][0-9])?) \.tar[._-]gz - $ - }x; + \z + }xs; "$1.$3"; } @@ -3338,8 +3535,8 @@ or "had problems unarchiving. Please build manually"; exists $self->{writemakefile} && - $self->{writemakefile} eq "NO" and push @e, - "Had some problem writing Makefile"; + $self->{writemakefile} =~ m/ ^ NO\s* ( .* ) /sx and push @e, + $1 || "Had some problem writing Makefile"; defined $self->{'make'} and push @e, "Has already been processed within this session"; @@ -3396,18 +3593,27 @@ or kill 9, $pid; waitpid $pid, 0; $CPAN::Frontend->myprint($@); - $self->{writemakefile} = "NO - $@"; + $self->{writemakefile} = "NO $@"; $@ = ""; return; } } else { $ret = system($system); if ($ret != 0) { - $self->{writemakefile} = "NO"; + $self->{writemakefile} = "NO Makefile.PL returned status $ret"; return; } } - $self->{writemakefile} = "YES"; + if (-f "Makefile") { + $self->{writemakefile} = "YES"; + } else { + $self->{writemakefile} = + qq{NO Makefile.PL refused to write a Makefile.}; + # It's probably worth to record the reason, so let's retry + # local $/; + # my $fh = IO::File->new("$system |"); # STDERR? STDIN? + # $self->{writemakefile} .= <$fh>; + } } return if $CPAN::Signal; if (my @prereq = $self->needs_prereq){ @@ -3441,7 +3647,7 @@ of modules we are processing right now?", "yes"); $CPAN::Frontend->myprint(" $system -- OK\n"); $self->{'make'} = "YES"; } else { - $self->{writemakefile} = "YES"; + $self->{writemakefile} ||= "YES"; $self->{'make'} = "NO"; $CPAN::Frontend->myprint(" $system -- NOT OK\n"); } @@ -3474,7 +3680,7 @@ sub needs_prereq { next if $mo->uptodate; # it's not needed, so don't push it. We cannot omit this step, because # if 'force' is in effect, nobody else will check. - if ($self->{'have_sponsored'}{$p}++){ + if ($self->{have_sponsored}{$p}++){ # We have already sponsored it and for some reason it's still # not available. So we do nothing. Or what should we do? # if we push it again, we have a potential infinite loop @@ -3657,13 +3863,14 @@ sub contains { my $fh = FileHandle->new; local $/ = "\n"; open($fh,$parsefile) or die "Could not open '$parsefile': $!"; - my $inpod = 0; + my $in_cont = 0; $self->debug("parsefile[$parsefile]") if $CPAN::DEBUG; while (<$fh>) { - $inpod = m/^=(?!head1\s+CONTENTS)/ ? 0 : - m/^=head1\s+CONTENTS/ ? 1 : $inpod; - next unless $inpod; + $in_cont = m/^=(?!head1\s+CONTENTS)/ ? 0 : + m/^=head1\s+CONTENTS/ ? 1 : $in_cont; + next unless $in_cont; next if /^=/; + s/\#.*//; next if /^\s+$/; chomp; push @result, (split " ", $_, 2)[0]; @@ -3709,7 +3916,7 @@ sub find_bundle_file { $what2 =~ s/:Bundle://; $what2 =~ tr|:|/|; } else { - $what2 =~ s|Bundle/||; + $what2 =~ s|Bundle[/\\]||; } my $bu; while (<$fh>) { @@ -3775,13 +3982,19 @@ explicitly a file $s. # recap with less noise if ( $meth eq "install") { if (%fail) { - $CPAN::Frontend->myprint(qq{\nBundle summary: }. - qq{The following items seem to }. - qq{have had installation problems:\n}); + require Text::Wrap; + my $raw = sprintf(qq{Bundle summary: +The following items in bundle %s had installation problems:}, + $self->id + ); + $CPAN::Frontend->myprint(Text::Wrap::fill("","",$raw)); + $CPAN::Frontend->myprint("\n"); + my $paragraph = ""; for $s ($self->contains) { - $CPAN::Frontend->myprint( "$s " ) if $fail{$s}; + $paragraph .= "$s " if $fail{$s}; } - $CPAN::Frontend->myprint(qq{\n}); + $CPAN::Frontend->myprint(Text::Wrap::fill(" "," ",$paragraph)); + $CPAN::Frontend->myprint("\n"); } else { $self->{'install'} = 'YES'; } @@ -3911,7 +4124,7 @@ sub as_string { sub manpage_headline { my($self,$local_file) = @_; my(@local_file) = $local_file; - $local_file =~ s/\.pm$/.pod/; + $local_file =~ s/\.pm\z/.pod/; push @local_file, $local_file; my(@result,$locf); for $locf (@local_file) { @@ -4011,6 +4224,8 @@ sub rematein { 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 ; @@ -4091,7 +4306,7 @@ sub inst_version { local($^W) = 0 if $] < 5.00303 && $ExtUtils::MakeMaker::VERSION < 5.38; # warn "HERE"; my $have = MM->parse_version($parsefile) || "undef"; - $have =~ s/\s+//g; + $have =~ s/\s*//g; # stringify to float around floating point issues $have; } @@ -4174,7 +4389,7 @@ sub READLINE { my $gz = $self->{GZ}; my($line,$bytesread); $bytesread = $gz->gzreadline($line); - return undef if $bytesread == 0; + return undef if $bytesread <= 0; return $line; } else { my $fh = $self->{FH}; @@ -4202,7 +4417,7 @@ sub DESTROY { $gz->gzclose(); } else { my $fh = $self->{FH}; - $fh->close; + $fh->close if defined $fh; } undef $self; } @@ -4213,29 +4428,30 @@ sub untar { if (MM->maybe_command($CPAN::Config->{'gzip'}) && MM->maybe_command($CPAN::Config->{'tar'})) { - if ($^O =~ /win/i) { # irgggh - # people find the most curious tar binaries that cannot handle - # pipes - my $system = "$CPAN::Config->{'gzip'} --decompress $file"; - if (system($system)==0) { - $CPAN::Frontend->myprint(qq{Uncompressed $file successfully\n}); - } else { - $CPAN::Frontend->mydie( - qq{Couldn\'t uncompress $file\n} - ); - } - $file =~ s/\.gz$//; - $system = "$CPAN::Config->{tar} xvf $file"; - if (system($system)==0) { - $CPAN::Frontend->myprint(qq{Untarred $file successfully\n}); - } else { - $CPAN::Frontend->mydie(qq{Couldn\'t untar $file\n}); - } - return 1; + my $system = "$CPAN::Config->{'gzip'} --decompress --stdout " . + "< $file | $CPAN::Config->{tar} xvf -"; + if (system($system) != 0) { + # people find the most curious tar binaries that cannot handle + # pipes + my $system = "$CPAN::Config->{'gzip'} --decompress $file"; + if (system($system)==0) { + $CPAN::Frontend->myprint(qq{Uncompressed $file successfully\n}); + } else { + $CPAN::Frontend->mydie( + qq{Couldn\'t uncompress $file\n} + ); + } + $file =~ s/\.gz\z//; + $system = "$CPAN::Config->{tar} xvf $file"; + $CPAN::Frontend->myprint(qq{Using Tar:$system:\n}); + if (system($system)==0) { + $CPAN::Frontend->myprint(qq{Untarred $file successfully\n}); + } else { + $CPAN::Frontend->mydie(qq{Couldn\'t untar $file\n}); + } + return 1; } else { - my $system = "$CPAN::Config->{'gzip'} --decompress --stdout " . - "< $file | $CPAN::Config->{tar} xvf -"; - return system($system) == 0; + return 1; } } elsif ($CPAN::META->has_inst("Archive::Tar") && @@ -4291,8 +4507,8 @@ Modules are fetched from one or more of the mirrored CPAN directory. The CPAN module also supports the concept of named and versioned -'bundles' of modules. Bundles simplify the handling of sets of -related modules. See BUNDLES below. +I<bundles> of modules. Bundles simplify the handling of sets of +related modules. See Bundles below. The package contains a session manager and a cache manager. There is no status retained between sessions. The session manager keeps track @@ -4343,29 +4559,14 @@ objects. The parser recognizes a regular expression only if you enclose it between two slashes. The principle is that the number of found objects influences how an -item is displayed. If the search finds one item, the result is displayed -as object-E<gt>as_string, but if we find more than one, we display -each as object-E<gt>as_glimpse. E.g. - - cpan> a ANDK - Author id = ANDK - EMAIL a.koenig@franz.ww.TU-Berlin.DE - FULLNAME Andreas König - - - cpan> a /andk/ - Author id = ANDK - EMAIL a.koenig@franz.ww.TU-Berlin.DE - FULLNAME Andreas König - - - cpan> a /and.*rt/ - Author ANDYD (Andy Dougherty) - Author MERLYN (Randal L. Schwartz) +item is displayed. If the search finds one item, the result is +displayed with the rather verbose method C<as_string>, but if we find +more than one, we display each object with the terse method +<as_glimpse>. =item make, test, install, clean modules or distributions -These commands take any number of arguments and investigates what is +These commands take any number of arguments and investigate what is necessary to perform the action. 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 @@ -4407,12 +4608,11 @@ A C<clean> command results in a being executed within the distribution file's working directory. -=item readme, look module or distribution +=item get, readme, look module or distribution -These two commands take only one argument, be it a module or a -distribution file. C<readme> unconditionally runs, displaying the -README of the associated distribution file. C<Look> gets and -untars (if not yet done) the distribution file, changes to the +C<get> downloads a distribution file without further action. 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. =item Signals @@ -4513,8 +4713,8 @@ functions in the calling package (C<install(...)>). 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. Each of the commands that produce -listings of modules (C<r>, C<autobundle>, C<u>) returns a list of the -IDs of all modules within the list. +listings of modules (C<r>, C<autobundle>, C<u>) also return a list of +the IDs of all modules within the list. =over 2 @@ -4549,6 +4749,41 @@ functionalities that are available in the shell. print "No VERSION in ", $mod->id, "\n"; } +Or if you want to write a cronjob to watch The CPAN, you could list +all modules that need updating: + + perl -e 'use CPAN; CPAN::Shell->r;' + +If you don't want to get any output if all modules are up to date, you +can parse the output of above command for the regular expression +//modules are up to date// and decide to mail the output only if it +doesn't match. Ick? + +If you prefer to do it more in a programmer style in one single +process, maybe something like this suites you better: + + # 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 you too much output every day, you maybe only want to +watch 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 four Classes @@ -4666,7 +4901,7 @@ 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. +with this floppy. See also below the paragraph about CD-ROM support. =head1 CONFIGURATION @@ -4689,7 +4924,6 @@ defined: many seconds inactivity. Set to 0 to never break. inhibit_startup_message if true, does not print the startup message - keep_source keep the source in a local directory? keep_source_where directory in which to keep the source (if we do) make location of external make program make_arg arguments that should always be passed to 'make' @@ -4713,29 +4947,40 @@ shell with the command set defined within the C<o conf> command: =over 2 -=item o conf E<lt>scalar optionE<gt> +=item C<o conf E<lt>scalar optionE<gt>> prints the current value of the I<scalar option> -=item o conf E<lt>scalar optionE<gt> E<lt>valueE<gt> +=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 o conf E<lt>list optionE<gt> +=item C<o conf E<lt>list optionE<gt>> prints the current value of the I<list option> in MakeMaker's neatvalue format. -=item o conf E<lt>list optionE<gt> [shift|pop] +=item C<o conf E<lt>list optionE<gt> [shift|pop]> shifts or pops the array in the I<list option> variable -=item o conf E<lt>list optionE<gt> [unshift|push|splice] E<lt>listE<gt> +=item C<o conf E<lt>list optionE<gt> [unshift|push|splice] E<lt>listE<gt>> works like the corresponding perl commands. =back +=head2 Note on urllist parameter's format + +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 file URLs, please try the correct format. Either: + + file://localhost/whatever/ftp/pub/CPAN/ + +or + + file:///home/ftp/pub/CPAN/ + =head2 urllist parameter has CD-ROM support The C<urllist> parameter of the configuration table contains a list of @@ -4780,28 +5025,30 @@ To populate a freshly installed perl with my favorite modules is pretty easiest by maintaining 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 that re installed for the currently running perl +file for all modules that are installed for the currently running perl interpreter. It's recommended to run this command only once 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 then go out. +then answer a few questions and then go out for a coffee. -Maintaining a bundle definition file means to keep track of two things: -dependencies and interactivity. CPAN.pm (currently) does not take into -account dependencies between distributions, so a bundle definition file -should specify distributions that depend on others B<after> the others. -On the other hand, it's a bit annoying that many distributions need some -interactive configuring. So what I try to accomplish in my private bundle -file is to have the packages that need to be configured early in the file -and the gentle ones later, so I can go out after a few minutes and leave -CPAN.pm unattained. +Maintaining a bundle definition file means to keep 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 a bit +annoying that many distributions need some interactive configuring. So +what I try to accomplish in my private bundle file is to have the +packages that need to be configured early in the file and the gentle +ones later, so I can go out after a few minutes and leave CPAN.pm +unattained. =head1 WORKING WITH CPAN.pm BEHIND FIREWALLS -Thanks to Graham Barr for contributing the firewall following howto. +Thanks to Graham Barr for contributing the following paragraphs about +the interaction between perl, and various firewall configurations. Firewalls can be categorized into three basic types. @@ -4820,10 +5067,10 @@ ftp) you will need to use LWP. =item ftp firewall -This where the firewall machine runs a ftp server. This kind of firewall will -only let you access ftp serves outside the firewall. This is usually done by -connecting to the firewall with ftp, then entering a username like -"user@outside.host.com" +This where the firewall machine runs a 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 will need to use Net::FTP. @@ -4860,7 +5107,7 @@ special compiling is need as you can access hosts directly. =head1 BUGS -We should give coverage for _all_ of the CPAN and not just the PAUSE +We should give coverage for B<all> of the CPAN and not just the PAUSE part, right? In this discussion CPAN and PAUSE have become equal -- but they are not. PAUSE is authors/ and modules/. CPAN is PAUSE plus the clpa/, doc/, misc/, ports/, src/, scripts/. @@ -4875,7 +5122,7 @@ traditional method of building a Perl module package from a shell. =head1 AUTHOR -Andreas König E<lt>a.koenig@kulturbox.deE<gt> +Andreas Koenig E<lt>andreas.koenig@anima.deE<gt> =head1 SEE ALSO |