This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
apply Net::Ping patch that makes the fork()-based approach
[perl5.git] / lib / CPAN.pm
index 502eaf3..0abfe1d 100644 (file)
@@ -1,12 +1,11 @@
 # -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*-
 package CPAN;
-$VERSION = '1.58_93';
-
-# $Id: CPAN.pm,v 1.376 2000/11/15 07:14:58 k Exp $
+$VERSION = '1.64';
+# $Id: CPAN.pm,v 1.397 2003/02/06 09:44:40 k Exp $
 
 # only used during development:
 $Revision = "";
-# $Revision = "[".substr(q$Revision: 1.376 $, 10)."]";
+# $Revision = "[".substr(q$Revision: 1.397 $, 10)."]";
 
 use Carp ();
 use Config ();
@@ -23,9 +22,12 @@ use Safe ();
 use Text::ParseWords ();
 use Text::Wrap;
 use File::Spec;
+use Sys::Hostname;
 no lib "."; # we need to run chdir all over and we would get at wrong
             # libraries there
 
+require Mac::BuildTools if $^O eq 'MacOS';
+
 END { $End++; &cleanup; }
 
 %CPAN::DEBUG = qw[
@@ -110,6 +112,20 @@ sub shell {
            $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");
+                last;
+            }
+            my($fh) = FileHandle->new;
+            open $fh, "<$histfile" or last;
+            local $/ = "\n";
+            while (<$fh>) {
+                chomp;
+                $term->AddHistory($_);
+            }
+            close $fh;
+        }}
        # $term->OUT is autoflushed anyway
        my $odef = select STDERR;
        $| = 1;
@@ -230,6 +246,10 @@ package CPAN::FTP;
 use vars qw($Ua $Thesite $Themethod);
 @CPAN::FTP::ISA = qw(CPAN::Debug);
 
+package CPAN::LWP::UserAgent;
+use vars qw(@ISA $USER $PASSWD $SETUPDONE);
+# we delay requiring LWP::UserAgent and setting up inheritence until we need it
+
 package CPAN::Complete;
 @CPAN::Complete::ISA = qw(CPAN::Debug);
 @CPAN::Complete::COMMANDS = sort qw(
@@ -239,10 +259,10 @@ package CPAN::Complete;
 ) unless @CPAN::Complete::COMMANDS;
 
 package CPAN::Index;
-use vars qw($last_time $date_of_03);
+use vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03);
 @CPAN::Index::ISA = qw(CPAN::Debug);
-$last_time ||= 0;
-$date_of_03 ||= 0;
+$LAST_TIME ||= 0;
+$DATE_OF_03 ||= 0;
 # use constant PROTOCOL => "2.0"; # outcommented to avoid warning on upgrade from 1.57
 sub PROTOCOL { 2.0 }
 
@@ -451,23 +471,37 @@ sub all_objects {
 #-> sub CPAN::checklock ;
 sub checklock {
     my($self) = @_;
-    my $lockfile = MM->catfile($CPAN::Config->{cpan_home},".lock");
+    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: $!");
-       my $other = <$fh>;
+       my $otherpid  = <$fh>;
+       my $otherhost = <$fh>;
        $fh->close;
-       if (defined $other && $other) {
-           chomp $other;
-           return if $$==$other; # should never happen
+       if (defined $otherpid && $otherpid) {
+           chomp $otherpid;
+        }
+       if (defined $otherhost && $otherhost) {
+           chomp $otherhost;
+       }
+       my $thishost  = hostname();
+       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 (defined $otherpid && $otherpid) {
+           return if $$ == $otherpid; # should never happen
            $CPAN::Frontend->mywarn(
                                    qq{
-There seems to be running another CPAN process ($other). Contacting...
+There seems to be running another CPAN process (pid $otherpid).  Contacting...
 });
-           if (kill 0, $other) {
+           if (kill 0, $otherpid) {
                $CPAN::Frontend->mydie(qq{Other job is running.
 You may want to kill it and delete the lockfile, maybe. On UNIX try:
-    kill $other
+    kill $otherpid
     rm $lockfile
 });
            } elsif (-w $lockfile) {
@@ -487,9 +521,9 @@ You may want to kill it and delete the lockfile, maybe. On UNIX try:
                           );
            }
        } else {
-            $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile $lockfile ".
+            $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile $lockfile\n".
                                            "reports other process with ID ".
-                                           "$other. Cannot proceed.\n"));
+                                           "$otherpid. Cannot proceed.\n"));
         }
     }
     my $dotcpan = $CPAN::Config->{cpan_home};
@@ -530,7 +564,7 @@ Please make sure the directory exists and is writable.
     unless ($fh = FileHandle->new(">$lockfile")) {
        if ($! =~ /Permission/) {
            my $incc = $INC{'CPAN/Config.pm'};
-           my $myincc = MM->catfile($ENV{HOME},'.cpan','CPAN','MyConfig.pm');
+           my $myincc = File::Spec->catfile($ENV{HOME},'.cpan','CPAN','MyConfig.pm');
            $CPAN::Frontend->myprint(qq{
 
 Your configuration suggests that CPAN.pm should use a working
@@ -553,6 +587,7 @@ or
        $CPAN::Frontend->mydie("Could not open >$lockfile: $!");
     }
     $fh->print($$, "\n");
+    $fh->print(hostname(), "\n");
     $self->{LOCK} = $lockfile;
     $fh->close;
     $SIG{TERM} = sub {
@@ -709,10 +744,10 @@ sub has_inst {
 
 }) unless $Have_warned->{"Net::FTP"}++;
        sleep 3;
-    } elsif ($mod eq "MD5"){
+    } elsif ($mod eq "Digest::MD5"){
        $CPAN::Frontend->myprint(qq{
-  CPAN: MD5 security checks disabled because MD5 not installed.
-  Please consider installing the MD5 module.
+  CPAN: MD5 security checks disabled because Digest::MD5 not installed.
+  Please consider installing the Digest::MD5 module.
 
 });
        sleep 2;
@@ -744,27 +779,66 @@ sub cleanup {
   my($message) = @_;
   my $i = 0;
   my $ineval = 0;
-  if (
-      0 &&           # disabled, try reload cpan with it
-      $] > 5.004_60  # thereabouts
-     ) {
-    $ineval = $^S;
-  } else {
-    my($subroutine);
-    while ((undef,undef,undef,$subroutine) = caller(++$i)) {
+  my($subroutine);
+  while ((undef,undef,undef,$subroutine) = caller(++$i)) {
       $ineval = 1, last if
          $subroutine eq '(eval)';
-    }
   }
   return if $ineval && !$End;
-  return unless defined $META->{LOCK}; # unsafe meta access, ok
-  return unless -f $META->{LOCK}; # unsafe meta access, ok
-  unlink $META->{LOCK}; # unsafe meta access, ok
+  return unless defined $META->{LOCK};
+  return unless -f $META->{LOCK};
+  $META->savehist;
+  unlink $META->{LOCK};
   # require Carp;
   # Carp::cluck("DEBUGGING");
   $CPAN::Frontend->mywarn("Lockfile removed.\n");
 }
 
+#-> 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;
+    unless ($CPAN::term->can("GetHistory")) {
+        $CPAN::Frontend->mywarn("Terminal does not support GetHistory.\n");
+        return;
+    }
+    my @h = $CPAN::term->GetHistory;
+    splice @h, 0, @h-$histsize if @h>$histsize;
+    my($fh) = FileHandle->new;
+    open $fh, ">$histfile" or mydie("Couldn't open >$histfile: $!");
+    local $\ = local $, = "\n";
+    print $fh @h;
+    close $fh;
+}
+
+sub is_tested {
+    my($self,$what) = @_;
+    $self->{is_tested}{$what} = 1;
+}
+
+sub is_installed {
+    my($self,$what) = @_;
+    delete $self->{is_tested}{$what};
+}
+
+sub set_perl5lib {
+    my($self) = @_;
+    $self->{is_tested} ||= {};
+    return unless %{$self->{is_tested}};
+    my $env = $ENV{PERL5LIB};
+    $env = $ENV{PERLLIB} unless defined $env;
+    my @env;
+    push @env, $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");
+    $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env;
+}
+
 package CPAN::CacheMgr;
 
 #-> sub CPAN::CacheMgr::as_string ;
@@ -818,9 +892,9 @@ sub entries {
     for ($dh->read) {
        next if $_ eq "." || $_ eq "..";
        if (-f $_) {
-           push @entries, MM->catfile($dir,$_);
+           push @entries, File::Spec->catfile($dir,$_);
        } elsif (-d _) {
-           push @entries, MM->catdir($dir,$_);
+           push @entries, File::Spec->catdir($dir,$_);
        } else {
            $CPAN::Frontend->mywarn("Warning: weird direntry in $dir: $_\n");
        }
@@ -1090,6 +1164,36 @@ sub init {
     1;
 }
 
+# This is a piece of repeated code that is abstracted here for
+# maintainability.  RMB
+#
+sub _configpmtest {
+    my($configpmdir, $configpmtest) = @_; 
+    if (-w $configpmtest) {
+        return $configpmtest;
+    } elsif (-w $configpmdir) {
+        #_#_# following code dumped core on me with 5.003_11, a.k.
+        my $configpm_bak = "$configpmtest.bak";
+        unlink $configpm_bak if -f $configpm_bak;
+        if( -f $configpmtest ) {       
+            if( rename $configpmtest, $configpm_bak ) {  
+                $CPAN::Frontend->mywarn(<<END)
+Old configuration file $configpmtest
+    moved to $configpm_bak
+END
+           }
+       }       
+       my $fh = FileHandle->new;
+       if ($fh->open(">$configpmtest")) {
+           $fh->print("1;\n");
+           return $configpmtest;
+       } else {
+           # Should never happen
+           Carp::confess("Cannot open >$configpmtest");
+       }
+    } else { return } 
+}
+
 #-> sub CPAN::Config::load ;
 sub load {
     my($self) = shift;
@@ -1098,7 +1202,7 @@ sub load {
     eval {require CPAN::Config;};       # We eval because of some
                                         # MakeMaker problems
     unless ($dot_cpan++){
-      unshift @INC, MM->catdir($ENV{HOME},".cpan");
+      unshift @INC, File::Spec->catdir($ENV{HOME},".cpan");
       eval {require CPAN::MyConfig;};   # where you can override
                                         # system wide settings
       shift @INC;
@@ -1117,42 +1221,17 @@ sub load {
        $redo++;
     } else {
        my($path_to_cpan) = File::Basename::dirname($INC{"CPAN.pm"});
-       my($configpmdir) = MM->catdir($path_to_cpan,"CPAN");
-       my($configpmtest) = MM->catfile($configpmdir,"Config.pm");
+       my($configpmdir) = File::Spec->catdir($path_to_cpan,"CPAN");
+       my($configpmtest) = File::Spec->catfile($configpmdir,"Config.pm");
        if (-d $configpmdir or File::Path::mkpath($configpmdir)) {
-           if (-w $configpmtest) {
-               $configpm = $configpmtest;
-           } elsif (-w $configpmdir) {
-               #_#_# following code dumped core on me with 5.003_11, a.k.
-               unlink "$configpmtest.bak" if -f "$configpmtest.bak";
-               rename $configpmtest, "$configpmtest.bak" if -f $configpmtest;
-               my $fh = FileHandle->new;
-               if ($fh->open(">$configpmtest")) {
-                   $fh->print("1;\n");
-                   $configpm = $configpmtest;
-               } else {
-                   # Should never happen
-                   Carp::confess("Cannot open >$configpmtest");
-               }
-           }
+           $configpm = _configpmtest($configpmdir,$configpmtest); 
        }
        unless ($configpm) {
-           $configpmdir = MM->catdir($ENV{HOME},".cpan","CPAN");
+           $configpmdir = File::Spec->catdir($ENV{HOME},".cpan","CPAN");
            File::Path::mkpath($configpmdir);
-           $configpmtest = MM->catfile($configpmdir,"MyConfig.pm");
-           if (-w $configpmtest) {
-               $configpm = $configpmtest;
-           } elsif (-w $configpmdir) {
-               #_#_# following code dumped core on me with 5.003_11, a.k.
-               my $fh = FileHandle->new;
-               if ($fh->open(">$configpmtest")) {
-                   $fh->print("1;\n");
-                   $configpm = $configpmtest;
-               } else {
-                   # Should never happen
-                   Carp::confess("Cannot open >$configpmtest");
-               }
-           } else {
+           $configpmtest = File::Spec->catfile($configpmdir,"MyConfig.pm");
+           $configpm = _configpmtest($configpmdir,$configpmtest); 
+           unless ($configpm) {
                Carp::confess(qq{WARNING: CPAN.pm is unable to }.
                              qq{create a configuration file.});
            }
@@ -1250,19 +1329,17 @@ sub h {
     } else {
        $CPAN::Frontend->myprint(q{
 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
+ command  argument          description
+ a,b,d,m  WORD or /REGEXP/  about authors, bundles, distributions, modules
+ i        WORD or /REGEXP/  about anything of above
+ r        NONE              reinstall recommendations
+ ls       AUTHOR            about files in the author's directory
 
 Download, Test, Make, Install...
  get                        download
  make                       make (implies get)
- test      modules,         make test (implies make)
- install   dists, bundles   make install (implies test)
+ 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
@@ -1282,7 +1359,7 @@ sub a {
   my($self,@arg) = @_;
   # authors are always UPPERCASE
   for (@arg) {
-    $_ = uc $_;
+    $_ = uc $_ unless /=/;
   }
   $CPAN::Frontend->myprint($self->format_result('Author',@arg));
 }
@@ -1290,10 +1367,15 @@ sub a {
 #-> sub CPAN::Shell::ls ;
 sub ls      {
     my($self,@arg) = @_;
+    my @accept;
     for (@arg) {
-        $_ = uc $_;
+        unless (/^[A-Z\-]+$/i) {
+            $CPAN::Frontend->mywarn("ls command rejects argument $_: not an author\n");
+            next;
+        }
+        push @accept, uc $_;
     }
-    for my $a (@arg){
+    for my $a (@accept){
         my $author = $self->expand('Author',$a) or die "No author found for $a";
         $author->ls;
     }
@@ -1306,13 +1388,13 @@ sub local_bundles {
     foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) {
         my @bbase = "Bundle";
         while (my $bbase = shift @bbase) {
-            $bdir = MM->catdir($incdir,split /::/, $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 =~ /^\./; # 
-                    if (-d MM->catdir($bdir,$entry)){
+                    next if $entry =~ /^\./;
+                    if (-d File::Spec->catdir($bdir,$entry)){
                         push @bbase, "$bbase\::$entry";
                     } else {
                         next unless $entry =~ s/\.pm(?!\n)\Z//;
@@ -1458,7 +1540,7 @@ Known options:
 sub paintdots_onreload {
     my($ref) = shift;
     sub {
-       if ( $_[0] =~ /[Ss]ubroutine (\w+) redefined/ ) {
+       if ( $_[0] =~ /[Ss]ubroutine ([\w:]+) redefined/ ) {
            my($subr) = $1;
            ++$$ref;
            local($|) = 1;
@@ -1476,14 +1558,17 @@ sub reload {
     $command ||= "";
     $self->debug("self[$self]command[$command]arg[@arg]") if $CPAN::DEBUG;
     if ($command =~ /cpan/i) {
-       CPAN->debug("reloading the whole CPAN.pm") if $CPAN::DEBUG;
-       my $fh = FileHandle->new($INC{'CPAN.pm'});
-       local($/);
-       my $redef = 0;
-       local($SIG{__WARN__}) = paintdots_onreload(\$redef);
-       eval <$fh>;
-       warn $@ if $@;
-       $CPAN::Frontend->myprint("\n$redef subroutines redefined\n");
+        for my $f (qw(CPAN.pm CPAN/FirstTime.pm)) {
+            next unless $INC{$f};
+            CPAN->debug("reloading the whole $f") if $CPAN::DEBUG;
+            my $fh = FileHandle->new($INC{$f});
+            local($/);
+            my $redef = 0;
+            local($SIG{__WARN__}) = paintdots_onreload(\$redef);
+            eval <$fh>;
+            warn $@ if $@;
+            $CPAN::Frontend->myprint("\n$redef subroutines redefined\n");
+        }
     } elsif ($command =~ /index/) {
       CPAN::Index->force_reload;
     } else {
@@ -1669,7 +1754,7 @@ sub autobundle {
     my($self) = shift;
     CPAN::Config->load unless $CPAN::Config_loaded++;
     my(@bundle) = $self->_u_r_common("a",@_);
-    my($todir) = MM->catdir($CPAN::Config->{'cpan_home'},"Bundle");
+    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");
@@ -1680,10 +1765,10 @@ sub autobundle {
     $m++;
     my($c) = 0;
     my($me) = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, $c;
-    my($to) = MM->catfile($todir,"$me.pm");
+    my($to) = File::Spec->catfile($todir,"$me.pm");
     while (-f $to) {
        $me = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, ++$c;
-       $to = MM->catfile($todir,"$me.pm");
+       $to = File::Spec->catfile($todir,"$me.pm");
     }
     my($fh) = FileHandle->new(">$to") or Carp::croak "Can't open >$to: $!";
     $fh->print(
@@ -1877,6 +1962,8 @@ sub print_ornamented {
            print color($ornament), sprintf($sprintf,$line), color("reset"), $nl;
        }
     } else {
+        # chomp $what;
+        # $what .= "\n"; # newlines unless $PRINT_ORNAMENTING
        print $what;
     }
 }
@@ -1964,12 +2051,12 @@ sub rematein {
        }
        if (ref $obj) {
             $obj->color_cmd_tmps(0,1);
-            CPAN::Queue->new($s);
+            CPAN::Queue->new($obj->id);
             push @qcopy, $obj;
        } elsif ($CPAN::META->exists('CPAN::Author',$s)) {
            $obj = $CPAN::META->instance('CPAN::Author',$s);
-            if ($meth eq "dump") {
-                $obj->dump;
+            if ($meth =~ /^(dump|ls)$/) {
+                $obj->$meth();
             } else {
                 $CPAN::Frontend->myprint(
                                          join "",
@@ -2055,6 +2142,60 @@ sub look   { shift->rematein('look',@_); }
 #-> sub CPAN::Shell::cvs_import ;
 sub cvs_import   { shift->rematein('cvs_import',@_); }
 
+package CPAN::LWP::UserAgent;
+
+sub config {
+    return if $SETUPDONE;
+    if ($CPAN::META->has_usable('LWP::UserAgent')) {
+        require LWP::UserAgent;
+        @ISA = qw(Exporter LWP::UserAgent);
+        $SETUPDONE++;
+    } else {
+        $CPAN::Frontend->mywarn("LWP::UserAgent not available\n");
+    }
+}
+
+sub get_basic_credentials {
+    my($self, $realm, $uri, $proxy) = @_;
+    return unless $proxy;
+    if ($USER && $PASSWD) {
+    } elsif (defined $CPAN::Config->{proxy_user} &&
+        defined $CPAN::Config->{proxy_pass}) {
+        $USER = $CPAN::Config->{proxy_user};
+        $PASSWD = $CPAN::Config->{proxy_pass};
+    } else {
+        require ExtUtils::MakeMaker;
+        ExtUtils::MakeMaker->import(qw(prompt));
+        $USER = prompt("Proxy authentication needed!
+ (Note: to permanently configure username and password run
+   o conf proxy_user your_username
+   o conf proxy_pass your_password
+ )\nUsername:");
+        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");
+        }
+        $PASSWD = prompt("Password:");
+        if ($CPAN::META->has_inst("Term::ReadKey")) {
+            Term::ReadKey::ReadMode("restore");
+        }
+        $CPAN::Frontend->myprint("\n\n");
+    }
+    return($USER,$PASSWD);
+}
+
+sub mirror {
+    my($self,$url,$aslocal) = @_;
+    my $result = $self->SUPER::mirror($url,$aslocal);
+    if ($result->code == 407) {
+        undef $USER;
+        undef $PASSWD;
+        $result = $self->SUPER::mirror($url,$aslocal);
+    }
+    $result;
+}
+
 package CPAN::FTP;
 
 #-> sub CPAN::FTP::ftp_get ;
@@ -2164,14 +2305,34 @@ sub localize {
     # Inheritance is not easier to manage than a few if/else branches
     if ($CPAN::META->has_usable('LWP::UserAgent')) {
        unless ($Ua) {
-           $Ua = LWP::UserAgent->new;
-           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};
+            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};
+
+
+# >>>>> On Wed, 13 Dec 2000 09:21:34 -0500, "Robison, Jonathon (J.M.)" <jrobiso2@visteon.com> said:
+# 
+#  > I note that although CPAN.pm can use proxies, it doesn't seem equipped to
+#  > use ones that require basic autorization.
+#  
+#  > Example of when I use it manually in my own stuff:
+#  
+#  > $ua->proxy(['http','ftp'], http://my.proxy.server:83');
+#  > $req->proxy_authorization_basic("username","password");
+#  > $res = $ua->request($req);
+# 
+
+                $Ua->no_proxy($var)
+                    if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy};
+            }
        }
     }
     $ENV{ftp_proxy} = $CPAN::Config->{ftp_proxy} if $CPAN::Config->{ftp_proxy};
@@ -2183,6 +2344,9 @@ sub localize {
     # where we did get a file from
     my(@reordered,$last);
     $CPAN::Config->{urllist} ||= [];
+    unless (ref $CPAN::Config->{urllist} eq 'ARRAY') {
+        warn "Malformed urllist; ignoring.  Configuration file corrupt?\n";
+    }
     $last = $#{$CPAN::Config->{urllist}};
     if ($force & 2) { # local cpans probably out of date, don't reorder
        @reordered = (0..$last);
@@ -2271,6 +2435,7 @@ sub hosteasy {
                                                     # meant
                                                     # file://localhost
                $l =~ s|^/||s unless -f $l;         # e.g. /P:
+               $self->debug("without URI::URL we try local file $l") if $CPAN::DEBUG;
            }
            if ( -f $l && -r _) {
                $Thesite = $i;
@@ -2291,8 +2456,11 @@ sub hosteasy {
   $url
 ");
          unless ($Ua) {
-           require LWP::UserAgent;
-           $Ua = LWP::UserAgent->new;
+              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) {
@@ -2314,12 +2482,17 @@ sub hosteasy {
              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.
          }
        } else {
-         $self->debug("LWP not installed") if $CPAN::DEBUG;
+            $CPAN::Frontend->myprint("LWP not available\n");
        }
         return if $CPAN::Signal;
        if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) {
@@ -2418,7 +2591,7 @@ Trying with "$funkyftp$src_switch" to get
     $url
 ]);
          my($system) =
-             "$chdir$funkyftp$src_switch '$url' $devnull$stdout_redir";
+             "$chdir$funkyftp$src_switch \"$url\" $devnull$stdout_redir";
          $self->debug("system[$system]") if $CPAN::DEBUG;
          my($wstatus);
          if (($wstatus = system($system)) == 0
@@ -2451,7 +2624,7 @@ Trying with "$funkyftp$src_switch" to get
 Trying with "$funkyftp$src_switch" to get
   $url.gz
 ]);
-           my($system) = "$funkyftp$src_switch '$url.gz' $devnull > $asl_gz";
+           my($system) = "$funkyftp$src_switch \"$url.gz\" $devnull > $asl_gz";
            $self->debug("system[$system]") if $CPAN::DEBUG;
            my($wstatus);
            if (($wstatus = system($system)) == 0
@@ -2517,7 +2690,7 @@ sub hosthardest {
             @dialog,
             "lcd $aslocal_dir",
             "cd /",
-            map("cd $_", split "/", $dir), # RFC 1738
+            map("cd $_", split /\//, $dir), # RFC 1738
             "bin",
             "get $getfile $targetfile",
             "quit"
@@ -2669,7 +2842,7 @@ package CPAN::FTP::netrc;
 
 sub new {
     my($class) = @_;
-    my $file = MM->catfile($ENV{HOME},".netrc");
+    my $file = File::Spec->catfile($ENV{HOME},".netrc");
 
     my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
        $atime,$mtime,$ctime,$blksize,$blocks)
@@ -2759,7 +2932,7 @@ sub cpl {
     my @return;
     if ($pos == 0) {
        @return = grep /^$word/, @CPAN::Complete::COMMANDS;
-    } elsif ( $line !~ /^[\!abcdhimorutl]/ ) {
+    } elsif ( $line !~ /^[\!abcdghimorutl]/ ) {
        @return = ();
     } elsif ($line =~ /^(a|ls)\s/) {
        @return = cplx('CPAN::Author',uc($word));
@@ -2769,8 +2942,11 @@ sub cpl {
     } elsif ($line =~ /^d\s/) {
        @return = cplx('CPAN::Distribution',$word);
     } elsif ($line =~ m/^(
-                          [mru]|make|clean|dump|test|install|readme|look|cvs_import
+                          [mru]|make|clean|dump|get|test|install|readme|look|cvs_import
                          )\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);
@@ -2843,7 +3019,7 @@ package CPAN::Index;
 #-> sub CPAN::Index::force_reload ;
 sub force_reload {
     my($class) = @_;
-    $CPAN::Index::last_time = 0;
+    $CPAN::Index::LAST_TIME = 0;
     $class->reload(1);
 }
 
@@ -2868,9 +3044,9 @@ sub reload {
     }
     if ( $CPAN::META->{PROTOCOL} < PROTOCOL  ) {
         # warn "Setting last_time to 0";
-        $last_time = 0; # No warning necessary
+        $LAST_TIME = 0; # No warning necessary
     }
-    return if $last_time + $CPAN::Config->{index_expire}*86400 > $time
+    return if $LAST_TIME + $CPAN::Config->{index_expire}*86400 > $time
        and ! $force;
     if (0) {
         # IFF we are developing, it helps to wipe out the memory
@@ -2880,7 +3056,7 @@ sub reload {
     }
     {
         my($debug,$t2);
-        local $last_time = $time;
+        local $LAST_TIME = $time;
         local $CPAN::META->{PROTOCOL} = PROTOCOL;
 
         my $needshort = $^O eq "dos";
@@ -2920,7 +3096,7 @@ sub reload {
         $time = $t2;
         CPAN->debug($debug) if $CPAN::DEBUG;
     }
-    $last_time = $time;
+    $LAST_TIME = $time;
     $CPAN::META->{PROTOCOL} = PROTOCOL;
 }
 
@@ -2931,8 +3107,8 @@ sub reload_x {
     CPAN::Config->load; # we should guarantee loading wherever we rely
                         # on Config XXX
     $localname ||= $wanted;
-    my $abs_wanted = MM->catfile($CPAN::Config->{'keep_source_where'},
-                                  $localname);
+    my $abs_wanted = File::Spec->catfile($CPAN::Config->{'keep_source_where'},
+                                        $localname);
     if (
        -f $abs_wanted &&
        -M $abs_wanted < $CPAN::Config->{'index_expire'} &&
@@ -2992,12 +3168,12 @@ sub rd_modpacks {
        push @lines, @ls;
     }
     # read header
-    my $line_count;
+    my($line_count,$last_updated);
     while (@lines) {
        my $shift = shift(@lines);
-       $shift =~ /^Line-Count:\s+(\d+)/;
-       $line_count = $1 if $1;
        last if $shift =~ /^\s*$/;
+       $shift =~ /^Line-Count:\s+(\d+)/ and $line_count = $1;
+        $shift =~ /^Last-Updated:\s+(.+)/ and $last_updated = $1;
     }
     if (not defined $line_count) {
 
@@ -3017,6 +3193,41 @@ CPAN mirror. I'll continue but problems seem likely to happen.\a\n},
 $index_target, $line_count, scalar(@lines);
 
     }
+    if (not defined $last_updated) {
+
+       warn 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
+};
+
+       sleep 5;
+    } else {
+
+       $CPAN::Frontend
+            ->myprint(sprintf qq{  Database was generated on %s\n},
+                      $last_updated);
+        $DATE_OF_02 = $last_updated;
+
+        if ($CPAN::META->has_inst(HTTP::Date)) {
+            require HTTP::Date;
+            my($age) = (time - HTTP::Date::str2time($last_updated))/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);
+
+            }
+        } else {
+            $CPAN::Frontend->myprint("  HTTP::Date not available\n");
+        }
+    }
+
+
     # A necessity since we have metadata_cache: delete what isn't
     # there anymore
     my $secondtime = $CPAN::META->exists("CPAN::Module","CPAN");
@@ -3138,8 +3349,8 @@ sub rd_modlist {
     while (@eval) {
        my $shift = shift(@eval);
        if ($shift =~ /^Date:\s+(.*)/){
-           return if $date_of_03 eq $1;
-           ($date_of_03) = $1;
+           return if $DATE_OF_03 eq $1;
+           ($DATE_OF_03) = $1;
        }
        last if $shift =~ /^\s*$/;
     }
@@ -3169,12 +3380,13 @@ sub write_metadata_cache {
                      CPAN::Distribution)) {
        $cache->{$k} = $CPAN::META->{readonly}{$k}; # unsafe meta access, ok
     }
-    my $metadata_file = MM->catfile($CPAN::Config->{cpan_home},"Metadata");
-    $cache->{last_time} = $last_time;
+    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("Going to write $metadata_file\n");
     eval { Storable::nstore($cache, $metadata_file) };
-    $CPAN::Frontend->mywarn($@) if $@;
+    $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ??
 }
 
 #-> sub CPAN::Index::read_metadata_cache ;
@@ -3182,20 +3394,20 @@ sub read_metadata_cache {
     my($self) = @_;
     return unless $CPAN::Config->{'cache_metadata'};
     return unless $CPAN::META->has_usable("Storable");
-    my $metadata_file = MM->catfile($CPAN::Config->{cpan_home},"Metadata");
+    my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata");
     return unless -r $metadata_file and -f $metadata_file;
     $CPAN::Frontend->myprint("Going to read $metadata_file\n");
     my $cache;
     eval { $cache = Storable::retrieve($metadata_file) };
-    $CPAN::Frontend->mywarn($@) if $@;
+    $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ??
     if (!$cache || ref $cache ne 'HASH'){
-        $last_time = 0;
+        $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",
+                                            "with protocol v%s, requiring v%s\n",
                                             $cache->{PROTOCOL},
                                             PROTOCOL)
                                    );
@@ -3203,7 +3415,7 @@ sub read_metadata_cache {
         }
     } else {
         $CPAN::Frontend->mywarn("Ignoring Metadata cache written ".
-                                "with protocol v1.0");
+                                "with protocol v1.0\n");
         return;
     }
     my $clcnt = 0;
@@ -3230,14 +3442,18 @@ sub read_metadata_cache {
     $CPAN::META->{PROTOCOL} ||=
         $cache->{PROTOCOL}; # reading does not up or downgrade, but it
                             # does initialize to some protocol
-    $last_time = $cache->{last_time};
+    $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;
 }
 
 package CPAN::InfoObj;
 
 # Accessors
 sub cpan_userid { shift->{RO}{CPAN_USERID} }
-sub id { shift->{ID} }
+sub id { shift->{ID}; }
 
 #-> sub CPAN::InfoObj::new ;
 sub new {
@@ -3345,13 +3561,25 @@ sub dump {
 
 package CPAN::Author;
 
+#-> 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 "%-15s %s (%s)\n", $class, $self->{ID}, $self->fullname;
+    push @m, sprintf(qq{%-15s %s ("%s" <%s>)\n},
+                     $class,
+                     $self->{ID},
+                     $self->fullname,
+                     $self->email);
     join "", @m;
 }
 
@@ -3364,35 +3592,55 @@ sub fullname {
 #-> sub CPAN::Author::email ;
 sub email    { shift->{RO}{EMAIL}; }
 
+#-> sub CPAN::Author::ls ;
 sub ls {
     my $self = shift;
     my $id = $self->id;
 
     # adapted from CPAN::Distribution::verifyMD5 ;
-    my(@chksumfile);
-    @chksumfile = $self->id =~ /(.)(.)(.*)/;
-    $chksumfile[1] = join "", @chksumfile[0,1];
-    $chksumfile[2] = join "", @chksumfile[1,2];
-    push @chksumfile, "CHECKSUMS";
-    print join "", map {
-        sprintf("%8d %10s %s\n", @$_)
-    } sort { $a->[2] cmp $b->[2] } $self->dir_listing(\@chksumfile);
+    my(@csf); # chksumfile
+    @csf = $self->id =~ /(.)(.)(.*)/;
+    $csf[1] = join "", @csf[0,1];
+    $csf[2] = join "", @csf[1,2];
+    my(@dl);
+    @dl = $self->dir_listing([$csf[0],"CHECKSUMS"], 0);
+    unless (grep {$_->[2] eq $csf[1]} @dl) {
+        $CPAN::Frontend->myprint("No files in the directory of $id\n");
+        return;
+    }
+    @dl = $self->dir_listing([@csf[0,1],"CHECKSUMS"], 0);
+    unless (grep {$_->[2] eq $csf[2]} @dl) {
+        $CPAN::Frontend->myprint("No files in the directory of $id\n");
+        return;
+    }
+    @dl = $self->dir_listing([@csf,"CHECKSUMS"], 1);
+    $CPAN::Frontend->myprint(join "", map {
+        sprintf("%8d %10s %s/%s\n", $_->[0], $_->[1], $id, $_->[2])
+    } sort { $a->[2] cmp $b->[2] } @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 $lc_want =
-       MM->catfile($CPAN::Config->{keep_source_where},
-                    "authors", "id", @$chksumfile);
+       File::Spec->catfile($CPAN::Config->{keep_source_where},
+                           "authors", "id", @$chksumfile);
     local($") = "/";
+    # connect "force" argument with "index_expire".
+    my $force = 0;
+    if (my @stat = stat $lc_want) {
+        $force = $stat[9] + $CPAN::Config->{index_expire}*86400 <= time;
+    }
     my $lc_file = CPAN::FTP->localize("authors/id/@$chksumfile",
-                                      $lc_want,1);
+                                      $lc_want,$force);
     unless ($lc_file) {
         $CPAN::Frontend->myprint("Trying $lc_want.gz\n");
        $chksumfile->[-1] .= ".gz";
        $lc_file = CPAN::FTP->localize("authors/id/@$chksumfile",
-                                      "$lc_want.gz",1);
+                                       "$lc_want.gz",1);
        if ($lc_file) {
            $lc_file =~ s{\.gz(?!\n)\Z}{}; #};
            CPAN::Tarzip->gunzip("$lc_file.gz",$lc_file);
@@ -3421,12 +3669,16 @@ sub dir_listing {
     my(@result,$f);
     for $f (sort keys %$cksum) {
         if (exists $cksum->{$f}{isdir}) {
-            my(@dir) = @$chksumfile;
-            pop @dir;
-            push @dir, $f, "CHECKSUMS";
-            push @result, map {
-                [$_->[0], $_->[1], "$f/$_->[2]"]
-            } $self->dir_listing(\@dir);
+            if ($recursive) {
+                my(@dir) = @$chksumfile;
+                pop @dir;
+                push @dir, $f, "CHECKSUMS";
+                push @result, map {
+                    [$_->[0], $_->[1], "$f/$_->[2]"]
+                } $self->dir_listing(\@dir,1);
+            } else {
+                push @result, [ 0, "-", $f ];
+            }
         } else {
             push @result, [
                            ($cksum->{$f}{"size"}||0),
@@ -3448,11 +3700,18 @@ sub undelay {
     delete $self->{later};
 }
 
+# CPAN::Distribution::normalize
 sub normalize {
     my($self,$s) = @_;
-    if ($s =~ tr|/|| == 1) {
+    $s = $self->id unless defined $s;
+    if (
+        $s =~ tr|/|| == 1
+        or
+        $s !~ m|[A-Z]/[A-Z-]{2}/[A-Z-]{2,}/|
+       ) {
+        return $s if $s =~ m:^N/A|^Contact Author: ;
         $s =~ s|^(.)(.)([^/]*/)(.+)$|$1/$1$2/$1$2$3$4| or
-            $CPAN::Frontend->mywarn("Strange distribution name [$s]");
+            $CPAN::Frontend->mywarn("Strange distribution name [$s]\n");
         CPAN->debug("s[$s]") if $CPAN::DEBUG;
     }
     $s;
@@ -3510,6 +3769,17 @@ sub containsmods {
   keys %{$self->{CONTAINSMODS}};
 }
 
+#-> sub CPAN::Distribution::uptodate ;
+sub uptodate {
+    my($self) = @_;
+    my $c;
+    foreach $c ($self->containsmods) {
+        my $obj = CPAN::Shell->expandany($c);
+        return 0 unless $obj->uptodate;
+    }
+    return 1;
+}
+
 #-> sub CPAN::Distribution::called_for ;
 sub called_for {
     my($self,$id) = @_;
@@ -3517,6 +3787,22 @@ sub called_for {
     return $self->{CALLED_FOR};
 }
 
+#-> sub CPAN::Distribution::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 {
+        my $cwd = CPAN::anycwd();
+        $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] }.
+                               qq{to todir[$todir]: $!});
+    }
+}
+
 #-> sub CPAN::Distribution::get ;
 sub get {
     my($self) = @_;
@@ -3526,44 +3812,66 @@ sub get {
            "Is already unwrapped into directory $self->{'build_dir'}";
        $CPAN::Frontend->myprint(join "", map {"  $_\n"} @e) and return if @e;
     }
+    my $sub_wd = CPAN::anycwd(); # for cleaning up as good as possible
+
+    #
+    # Get the file on local disk
+    #
+
     my($local_file);
     my($local_wanted) =
-        MM->catfile(
-                    $CPAN::Config->{keep_source_where},
-                    "authors",
-                    "id",
-                    split("/",$self->id)
-                   );
+        File::Spec->catfile(
+                           $CPAN::Config->{keep_source_where},
+                           "authors",
+                           "id",
+                           split(/\//,$self->id)
+                          );
 
     $self->debug("Doing localize") if $CPAN::DEBUG;
-    my $CWD = CPAN::anycwd();
-    $local_file =
-       CPAN::FTP->localize("authors/id/$self->{ID}", $local_wanted)
-           or $CPAN::Frontend->mydie("Giving up on '$local_wanted'\n");
-    return if $CPAN::Signal;
+    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_file[$local_file]") if $CPAN::DEBUG;
     $self->{localfile} = $local_file;
-    $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); # unsafe meta access, ok
-    my $builddir = $CPAN::META->{cachemgr}->dir; # unsafe meta access, ok
-    $self->debug("doing chdir $builddir") if $CPAN::DEBUG;
-    chdir $builddir or Carp::croak("Couldn't chdir $builddir: $!");
-    my $packagedir;
+    return if $CPAN::Signal;
 
-    $self->debug("local_file[$local_file]") if $CPAN::DEBUG;
-    if ($CPAN::META->has_inst("MD5")) {
-       $self->debug("MD5 is installed, verifying");
+    #
+    # Check integrity
+    #
+    if ($CPAN::META->has_inst("Digest::MD5")) {
+       $self->debug("Digest::MD5 is installed, verifying");
        $self->verifyMD5;
     } else {
-       $self->debug("MD5 is NOT installed");
+       $self->debug("Digest::MD5 is NOT installed");
     }
+    return if $CPAN::Signal;
+
+    #
+    # Create a clean room and go there
+    #
+    $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");
     mkdir "tmp", 0755 or Carp::croak "Couldn't mkdir tmp: $!";
-    chdir "tmp" or $CPAN::Frontend->mydie(qq{Could not chdir to "tmp": $!});;
-    $self->debug("Changed directory to tmp") if $CPAN::DEBUG;
-    return if $CPAN::Signal;
-    if (! $local_file) {
-       Carp::croak "bad download, can't do anything :-(\n";
-    } elsif ($local_file =~ /(\.tar\.(gz|Z)|\.tgz)(?!\n)\Z/i){
+    if ($CPAN::Signal){
+        $self->safe_chdir($sub_wd);
+        return;
+    }
+    $self->safe_chdir("tmp");
+
+    #
+    # Unpack the goods
+    #
+    if ($local_file =~ /(\.tar\.(gz|Z)|\.tgz)(?!\n)\Z/i){
         $self->{was_uncompressed}++ unless CPAN::Tarzip->gtest($local_file);
        $self->untar_me($local_file);
     } elsif ( $local_file =~ /\.zip(?!\n)\Z/i ) {
@@ -3573,87 +3881,108 @@ sub get {
        $self->pm2dir_me($local_file);
     } else {
        $self->{archived} = "NO";
+        $self->safe_chdir($sub_wd);
+        return;
     }
-    my $updir = File::Spec->updir;
-    unless (chdir $updir) {
-        my $cwd = CPAN::anycwd();
-        $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] to updir[$updir]: $!});
-    }
-    if ($self->{archived} ne 'NO') {
-        my $cwd = File::Spec->catdir(File::Spec->curdir, "tmp");
-        chdir $cwd or $CPAN::Frontend->mydie(qq{Could not chdir to "$cwd": $!});
-      # 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??
-      $dh->close;
-      my ($distdir,$packagedir);
-      if (@readdir == 1 && -d $readdir[0]) {
+
+    # 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??
+    $dh->close;
+    my ($distdir,$packagedir);
+    if (@readdir == 1 && -d $readdir[0]) {
         $distdir = $readdir[0];
-        $packagedir = MM->catdir($builddir,$distdir);
+        $packagedir = File::Spec->catdir($builddir,$distdir);
+        $self->debug("packagedir[$packagedir]builddir[$builddir]distdir[$distdir]")
+            if $CPAN::DEBUG;
         -d $packagedir and $CPAN::Frontend->myprint("Removing previously used ".
                                                     "$packagedir\n");
         File::Path::rmtree($packagedir);
         rename($distdir,$packagedir) or
             Carp::confess("Couldn't rename $distdir to $packagedir: $!");
-      } else {
-          my $userid = $self->cpan_userid;
-          unless ($userid) {
-              CPAN->debug("no userid? self[$self]");
-              $userid = "anon";
-          }
-          my $pragmatic_dir = $userid . '000';
-          $pragmatic_dir =~ s/\W_//g;
-          $pragmatic_dir++ while -d "../$pragmatic_dir";
-          $packagedir = MM->catdir($builddir,$pragmatic_dir);
-          File::Path::mkpath($packagedir);
-          my($f);
-          for $f (@readdir) { # is already without "." and ".."
-              my $to = MM->catdir($packagedir,$f);
-              rename($f,$to) or Carp::confess("Couldn't rename $f to $to: $!");
-          }
-      }
-      $self->{'build_dir'} = $packagedir;
-      chdir $updir;
-        unless (chdir $updir) {
-            my $cwd = CPAN::anycwd();
-            $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] to updir[$updir]: $!});
+        $self->debug(sprintf("renamed distdir[%s] to packagedir[%s] -e[%s]-d[%s]",
+                             $distdir,
+                             $packagedir,
+                             -e $packagedir,
+                             -d $packagedir,
+                            )) if $CPAN::DEBUG;
+    } else {
+        my $userid = $self->cpan_userid;
+        unless ($userid) {
+            CPAN->debug("no userid? self[$self]");
+            $userid = "anon";
+        }
+        my $pragmatic_dir = $userid . '000';
+        $pragmatic_dir =~ s/\W_//g;
+        $pragmatic_dir++ while -d "../$pragmatic_dir";
+        $packagedir = File::Spec->catdir($builddir,$pragmatic_dir);
+        $self->debug("packagedir[$packagedir]") if $CPAN::DEBUG;
+        File::Path::mkpath($packagedir);
+        my($f);
+        for $f (@readdir) { # is already without "." and ".."
+            my $to = File::Spec->catdir($packagedir,$f);
+            rename($f,$to) or Carp::confess("Couldn't rename $f to $to: $!");
         }
+    }
+    if ($CPAN::Signal){
+        $self->safe_chdir($sub_wd);
+        return;
+    }
 
-      $self->debug("Changed directory to .. (self[$self]=[".
-                   $self->as_string."])") if $CPAN::DEBUG;
-      File::Path::rmtree("tmp");
-      if ($CPAN::Config->{keep_source_where} =~ /^no/i ){
-        $CPAN::Frontend->myprint("Going to unlink $local_file\n");
-        unlink $local_file or Carp::carp "Couldn't unlink $local_file";
-      }
-      my($makefilepl) = MM->catfile($packagedir,"Makefile.PL");
-      unless (-f $makefilepl) {
-        my($configure) = MM->catfile($packagedir,"Configure");
+    $self->{'build_dir'} = $packagedir;
+    $self->safe_chdir(File::Spec->updir);
+    File::Path::rmtree("tmp");
+
+    my($mpl) = File::Spec->catfile($packagedir,"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.
+        sleep 1;
+        my $mpldh = DirHandle->new($packagedir)
+            or Carp::croak("Couldn't opendir $packagedir: $!");
+        $mpl_exists = grep /^Makefile\.PL$/, $mpldh->read;
+        $mpldh->close;
+    }
+    unless ($mpl_exists) {
+        $self->debug(sprintf("makefilepl[%s]anycwd[%s]",
+                             $mpl,
+                             CPAN::anycwd(),
+                            )) if $CPAN::DEBUG;
+        my($configure) = File::Spec->catfile($packagedir,"Configure");
         if (-f $configure) {
-          # do we have anything to do?
-          $self->{'configure'} = $configure;
-        } elsif (-f MM->catfile($packagedir,"Makefile")) {
-          $CPAN::Frontend->myprint(qq{
+            # do we have anything to do?
+            $self->{'configure'} = $configure;
+        } elsif (-f File::Spec->catfile($packagedir,"Makefile")) {
+            $CPAN::Frontend->myprint(qq{
 Package comes with a Makefile and without a Makefile.PL.
 We\'ll try to build it with that Makefile then.
 });
-          $self->{writemakefile} = "YES";
-          sleep 2;
+            $self->{writemakefile} = "YES";
+            sleep 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);
-          $CPAN::Frontend->myprint(qq{Package comes without Makefile.PL.
-  Writing one on our own (calling it $cf)\n});
-          $self->{had_no_makefile_pl}++;
-          my $fh = FileHandle->new(">$makefilepl")
-              or Carp::croak("Could not open >$makefilepl");
-          $fh->print(
+            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);
+            $CPAN::Frontend->myprint(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}++;
+            sleep 3;
+
+            # Writing our own Makefile.PL
+
+            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{
@@ -3662,11 +3991,10 @@ use ExtUtils::MakeMaker;
 WriteMakefile(NAME => q[$cf]);
 
 });
-          $fh->close;
+            $fh->close;
         }
-      }
     }
-    chdir $CWD or die "Could not chdir to $CWD: $!";
+
     return $self;
 }
 
@@ -3720,7 +4048,7 @@ sub look {
     my($self) = @_;
 
     if ($^O eq 'MacOS') {
-      $self->ExtUtils::MM_MacOS::look;
+      $self->Mac::BuildTools::look;
       return;
     }
 
@@ -3736,14 +4064,22 @@ Please define it with "o conf shell <your shell>"
        return;
     }
     my $dist = $self->id;
-    my $dir  = $self->dir or $self->get;
-    $dir = $self->dir;
+    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();
-    chdir($dir) or $CPAN::Frontend->mydie(qq{Could not chdir to "$dir": $!});
+    $self->safe_chdir($dir);
     $CPAN::Frontend->myprint(qq{Working directory is $dir\n});
     system($CPAN::Config->{'shell'}) == 0
        or $CPAN::Frontend->mydie("Subprocess shell error");
-    chdir($pwd) or $CPAN::Frontend->mydie(qq{Could not chdir to "$pwd": $!});
+    $self->safe_chdir($pwd);
 }
 
 # CPAN::Distribution::cvs_import ;
@@ -3758,7 +4094,7 @@ sub cvs_import {
 
     my $userid = $self->cpan_userid;
 
-    my $cvs_dir = (split '/', $dir)[-1];
+    my $cvs_dir = (split /\//, $dir)[-1];
     $cvs_dir =~ s/-\d+[^-]+(?!\n)\Z//;
     my $cvs_root = 
       $CPAN::Config->{cvsroot} || $ENV{CVSROOT};
@@ -3791,19 +4127,19 @@ sub readme {
     $self->debug("sans[$sans] suffix[$suffix]\n") if $CPAN::DEBUG;
     my($local_file);
     my($local_wanted) =
-        MM->catfile(
-                       $CPAN::Config->{keep_source_where},
-                       "authors",
-                       "id",
-                       split("/","$sans.readme"),
-                      );
+        File::Spec->catfile(
+                            $CPAN::Config->{keep_source_where},
+                            "authors",
+                            "id",
+                            split(/\//,"$sans.readme"),
+                           );
     $self->debug("Doing localize") if $CPAN::DEBUG;
     $local_file = CPAN::FTP->localize("authors/id/$sans.readme",
                                      $local_wanted)
        or $CPAN::Frontend->mydie(qq{No $sans.readme found});;
 
     if ($^O eq 'MacOS') {
-        ExtUtils::MM_MacOS::launch_file($local_file);
+        Mac::BuildTools::launch_file($local_file);
         return;
     }
 
@@ -3833,12 +4169,12 @@ sub verifyMD5 {
        $CPAN::Frontend->myprint(join "", map {"  $_\n"} @e) and return if @e;
     }
     my($lc_want,$lc_file,@local,$basename);
-    @local = split("/",$self->id);
+    @local = split(/\//,$self->id);
     pop @local;
     push @local, "CHECKSUMS";
     $lc_want =
-       MM->catfile($CPAN::Config->{keep_source_where},
-                     "authors", "id", @local);
+       File::Spec->catfile($CPAN::Config->{keep_source_where},
+                           "authors", "id", @local);
     local($") = "/";
     if (
        -s $lc_want
@@ -3899,7 +4235,7 @@ sub MD5_check_file {
        unless ($eq) {
          # had to inline it, when I tied it, the tiedness got lost on
          # the call to eq_MD5. (Jan 1998)
-         my $md5 = MD5->new;
+         my $md5 = Digest::MD5->new;
          my($data,$ref);
          $ref = \$data;
          while ($fh->READ($ref, 4096) > 0){
@@ -3958,7 +4294,7 @@ going awry right now.
 #-> sub CPAN::Distribution::eq_MD5 ;
 sub eq_MD5 {
     my($self,$fh,$expectMD5) = @_;
-    my $md5 = MD5->new;
+    my $md5 = Digest::MD5->new;
     my($data);
     while (read($fh, $data, 4096)){
       $md5->add($data);
@@ -4027,17 +4363,17 @@ sub isa_perl {
 #-> sub CPAN::Distribution::perl ;
 sub perl {
     my($self) = @_;
-    my($perl) = MM->file_name_is_absolute($^X) ? $^X : "";
+    my($perl) = File::Spec->file_name_is_absolute($^X) ? $^X : "";
     my $pwd  = CPAN::anycwd();
-    my $candidate = MM->catfile($pwd,$^X);
+    my $candidate = File::Spec->catfile($pwd,$^X);
     $perl ||= $candidate if MM->maybe_command($candidate);
     unless ($perl) {
        my ($component,$perl_name);
       DIST_PERLNAME: foreach $perl_name ($^X, 'perl', 'perl5', "perl$]") {
-           PATH_COMPONENT: foreach $component (MM->path(),
+           PATH_COMPONENT: foreach $component (File::Spec->path(),
                                                $Config::Config{'binexp'}) {
                  next unless defined($component) && $component;
-                 my($abs) = MM->catfile($component,$perl_name);
+                 my($abs) = File::Spec->catfile($component,$perl_name);
                  if (MM->maybe_command($abs)) {
                      $perl = $abs;
                      last DIST_PERLNAME;
@@ -4105,7 +4441,7 @@ or
     $self->debug("Changed directory to $builddir") if $CPAN::DEBUG;
 
     if ($^O eq 'MacOS') {
-        ExtUtils::MM_MacOS::make($self);
+        Mac::BuildTools::make($self);
         return;
     }
 
@@ -4166,7 +4502,7 @@ or
        } else {
          $self->{writemakefile} =
              qq{NO Makefile.PL refused to write a Makefile.};
-         # It's probably worth to record the reason, so let's retry
+         # It's probably worth it to record the reason, so let's retry
          # local $/;
          # my $fh = IO::File->new("$system |"); # STDERR? STDIN?
          # $self->{writemakefile} .= <$fh>;
@@ -4351,13 +4687,16 @@ sub test {
        if $CPAN::DEBUG;
 
     if ($^O eq 'MacOS') {
-        ExtUtils::MM_MacOS::make_test($self);
+        Mac::BuildTools::make_test($self);
         return;
     }
 
+    local $ENV{PERL5LIB} = $ENV{PERL5LIB} || "";
+    $CPAN::META->set_perl5lib;
     my $system = join " ", $CPAN::Config->{'make'}, "test";
     if (system($system) == 0) {
         $CPAN::Frontend->myprint("  $system -- OK\n");
+        $CPAN::META->is_tested($self->{'build_dir'});
         $self->{make_test} = "YES";
     } else {
         $self->{make_test} = "NO";
@@ -4382,7 +4721,7 @@ sub clean {
     $self->debug("Changed directory to $self->{'build_dir'}") if $CPAN::DEBUG;
 
     if ($^O eq 'MacOS') {
-        ExtUtils::MM_MacOS::make_clean($self);
+        Mac::BuildTools::make_clean($self);
         return;
     }
 
@@ -4457,7 +4796,7 @@ sub install {
        if $CPAN::DEBUG;
 
     if ($^O eq 'MacOS') {
-        ExtUtils::MM_MacOS::make_install($self);
+        Mac::BuildTools::make_install($self);
         return;
     }
 
@@ -4473,6 +4812,7 @@ sub install {
     $pipe->close;
     if ($?==0) {
         $CPAN::Frontend->myprint("  $system -- OK\n");
+        $CPAN::META->is_installed($self->{'build_dir'});
         return $self->{'install'} = "YES";
     } else {
         $self->{'install'} = "NO";
@@ -4492,6 +4832,14 @@ sub dir {
 
 package CPAN::Bundle;
 
+sub look {
+    my $self = shift;
+    $CPAN::Frontend->myprint(
+                             qq{ look() commmand on bundles not}.
+                             qq{ implemented (What should it do?)}
+                            );
+}
+
 sub undelay {
     my $self = shift;
     delete $self->{later};
@@ -4541,59 +4889,65 @@ sub as_string {
 
 #-> sub CPAN::Bundle::contains ;
 sub contains {
-  my($self) = @_;
-  my($parsefile) = $self->inst_file;
-  my($id) = $self->id;
-  $self->debug("parsefile[$parsefile]id[$id]") if $CPAN::DEBUG;
-  unless ($parsefile) {
-    # Try to get at it in the cpan directory
-    $self->debug("no parsefile") if $CPAN::DEBUG;
-    Carp::confess "I don't know a $id" unless $self->cpan_file;
-    my $dist = $CPAN::META->instance('CPAN::Distribution',
-                                    $self->cpan_file);
-    $dist->get;
-    $self->debug($dist->as_string) if $CPAN::DEBUG;
-    my($todir) = $CPAN::Config->{'cpan_home'};
-    my(@me,$from,$to,$me);
-    @me = split /::/, $self->id;
-    $me[-1] .= ".pm";
-    $me = MM->catfile(@me);
-    $from = $self->find_bundle_file($dist->{'build_dir'},$me);
-    $to = MM->catfile($todir,$me);
-    File::Path::mkpath(File::Basename::dirname($to));
-    File::Copy::copy($from, $to)
-       or Carp::confess("Couldn't copy $from to $to: $!");
-    $parsefile = $to;
-  }
-  my @result;
-  my $fh = FileHandle->new;
-  local $/ = "\n";
-  open($fh,$parsefile) or die "Could not open '$parsefile': $!";
-  my $in_cont = 0;
-  $self->debug("parsefile[$parsefile]") if $CPAN::DEBUG;
-  while (<$fh>) {
-    $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];
-  }
-  close $fh;
-  delete $self->{STATUS};
-  $self->{CONTAINS} = \@result;
-  $self->debug("CONTAINS[@result]") if $CPAN::DEBUG;
-  unless (@result) {
-    $CPAN::Frontend->mywarn(qq{
-The bundle file "$parsefile" may be a broken
+    my($self) = @_;
+    my($inst_file) = $self->inst_file || "";
+    my($id) = $self->id;
+    $self->debug("inst_file[$inst_file]id[$id]") if $CPAN::DEBUG;
+    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->mydie("Bundle $id not found on disk and not on CPAN.
+  Maybe stale symlink? Maybe removed during session? Giving up.\n");
+        }
+        my $dist = $CPAN::META->instance('CPAN::Distribution',
+                                         $self->cpan_file);
+        $dist->get;
+        $self->debug($dist->as_string) 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);
+        $from = $self->find_bundle_file($dist->{'build_dir'},$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+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];
+    }
+    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;
+    }
+    @result;
 }
 
 #-> sub CPAN::Bundle::find_bundle_file
@@ -4601,9 +4955,9 @@ 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 = MM->catfile($where,$what);
+###    my $bu = File::Spec->catfile($where,$what);
 ###    return $bu if -f $bu;
-    my $manifest = MM->catfile($where,"MANIFEST");
+    my $manifest = File::Spec->catfile($where,"MANIFEST");
     unless (-f $manifest) {
        require ExtUtils::Manifest;
        my $cwd = CPAN::anycwd();
@@ -4617,7 +4971,7 @@ sub find_bundle_file {
     my $what2 = $what;
     if ($^O eq 'MacOS') {
       $what =~ s/^://;
-      $what2 =~ tr|:|/|;
+      $what =~ tr|:|/|;
       $what2 =~ s/:Bundle://;
       $what2 =~ tr|:|/|;
     } else {
@@ -4629,7 +4983,7 @@ sub find_bundle_file {
        my($file) = /(\S+)/;
        if ($file =~ m|\Q$what\E$|) {
            $bu = $file;
-           # return MM->catfile($where,$bu); # bad
+           # return File::Spec->catfile($where,$bu); # bad
            last;
        }
        # retry if she managed to
@@ -4637,25 +4991,41 @@ sub find_bundle_file {
        $bu = $file if $file =~ m|\Q$what2\E$|;
     }
     $bu =~ tr|/|:| if $^O eq 'MacOS';
-    return MM->catfile($where, $bu) if $bu;
+    return File::Spec->catfile($where, $bu) if $bu;
     Carp::croak("Couldn't find a Bundle file in $where");
 }
 
-# needs to work slightly different from Module::inst_file because of
-# cpan_home/Bundle/ directory.
+# 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) = @_;
-    return $self->{INST_FILE} if
-        exists $self->{INST_FILE} && $self->{INST_FILE};
     my($inst_file);
     my(@me);
     @me = split /::/, $self->id;
     $me[-1] .= ".pm";
-    $inst_file = MM->catfile($CPAN::Config->{'cpan_home'}, @me);
-    return $self->{INST_FILE} = $inst_file if -f $inst_file;
-    $self->SUPER::inst_file;
+    my($incdir,$bestv);
+    foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) {
+        my $bfile = File::Spec->catfile($incdir, @me);
+        CPAN->debug("bfile[$bfile]") if $CPAN::DEBUG;
+        next unless -f $bfile;
+        my $foundv = MM->parse_version($bfile);
+        if (!$bestv || CPAN::Version->vgt($foundv,$bestv)) {
+            $self->{INST_FILE} = $bfile;
+            $self->{INST_VERSION} = $bestv = $foundv;
+        }
+    }
+    $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 ;
@@ -4766,6 +5136,18 @@ sub 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 mut 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) = @_;
@@ -4917,8 +5299,11 @@ sub as_string {
             # warn "dist[$dist]";
             # mff=manifest file; mfh=manifest handle
             my($mff,$mfh);
-            if ($dist->{build_dir} and
-                -f ($mff = MM->catfile($dist->{build_dir}, "MANIFEST")) and
+            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;
@@ -4939,7 +5324,7 @@ sub as_string {
                 }
                 $lfl =~ s/\s.*//; # remove comments
                 $lfl =~ s/\s+//g; # chomp would maybe be too system-specific
-                my $lfl_abs = MM->catfile($dist->{build_dir},$lfl);
+                my $lfl_abs = File::Spec->catfile($dist->{build_dir},$lfl);
                 # warn "lfl_abs[$lfl_abs]";
                 if (-f $lfl_abs) {
                     $self->{MANPAGE} = $self->manpage_headline($lfl_abs);
@@ -4976,8 +5361,8 @@ sub manpage_headline {
     my $inpod = 0;
     local $/ = "\n";
     while (<$fh>) {
-      $inpod = m/^=(?!head1\s+NAME)/ ? 0 :
-         m/^=head1\s+NAME/ ? 1 : $inpod;
+      $inpod = m/^=(?!head1\s+NAME\s*$)/ ? 0 :
+         m/^=head1\s+NAME\s*$/ ? 1 : $inpod;
       next unless $inpod;
       next if /^=/;
       next if /^\s+$/;
@@ -4991,7 +5376,8 @@ sub manpage_headline {
 }
 
 #-> sub CPAN::Module::cpan_file ;
-sub 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 (defined $self->{RO}{CPAN_FILE}) {
@@ -5133,7 +5519,7 @@ sub inst_file {
     @packpath = split /::/, $self->{ID};
     $packpath[-1] .= ".pm";
     foreach $dir (@INC) {
-       my $pmfile = MM->catfile($dir,@packpath);
+       my $pmfile = File::Spec->catfile($dir,@packpath);
        if (-f $pmfile){
            return $pmfile;
        }
@@ -5149,7 +5535,7 @@ sub xs_file {
     push @packpath, $packpath[-1];
     $packpath[-1] .= "." . $Config::Config{'dlext'};
     foreach $dir (@INC) {
-       my $xsfile = MM->catfile($dir,'auto',@packpath);
+       my $xsfile = File::Spec->catfile($dir,'auto',@packpath);
        if (-f $xsfile){
            return $xsfile;
        }
@@ -5183,7 +5569,7 @@ sub inst_version {
     # compare it           use utility for compare
     # print it             do nothing
 
-    # Alt2 maintain it as what is is
+    # Alt2 maintain it as what it is
     # read index files     convert
     # compare it           use utility because there's still a ">" vs "gt" issue
     # print it             use CPAN::Version for print
@@ -5288,7 +5674,7 @@ sub TIEHANDLE {
     $ret = bless {GZ => $gz}, $class;
   } else {
     my $pipe = "$CPAN::Config->{gzip} --decompress --stdout $file |";
-    my $fh = FileHandle->new($pipe) or die "Could pipe[$pipe]: $!";
+    my $fh = FileHandle->new($pipe) or die "Could not pipe[$pipe]: $!";
     binmode $fh;
     $ret = bless {FH => $fh}, $class;
   }
@@ -5432,7 +5818,7 @@ is available. Can\'t continue.
         $tar->extract(@af);
     }
 
-    ExtUtils::MM_MacOS::convert_files([$tar->list_files], 1)
+    Mac::BuildTools::convert_files([$tar->list_files], 1)
         if ($^O eq 'MacOS');
 
     return 1;
@@ -5543,7 +5929,7 @@ sub readable {
 
     # And if they say v1.2, then the old perl takes it as "v12"
 
-    $CPAN::Frontend->mywarn("Suspicious version string seen [$n]");
+    $CPAN::Frontend->mywarn("Suspicious version string seen [$n]\n");
     return $n;
   }
   my $better = sprintf "v%vd", $n;
@@ -5573,6 +5959,16 @@ Batch mode:
 
   autobundle, clean, install, make, recompile, test
 
+=head1 STATUS
+
+This module will eventually be replaced by CPANPLUS. CPANPLUS is kind
+of a modern rewrite from ground up with greater extensibility and more
+features but no full compatibility. If you're new to CPAN.pm, you
+probably should investigate if CPANPLUS is the better choice for you.
+If you're already used to CPAN.pm you're welcome to continue using it,
+if you accept that its development is mostly (though not completely)
+stalled.
+
 =head1 DESCRIPTION
 
 The CPAN module is designed to automate the make and install of perl
@@ -5598,10 +5994,10 @@ mechanism.
 For extended searching capabilities there's a plugin for CPAN available,
 L<C<CPAN::WAIT>|CPAN::WAIT>. C<CPAN::WAIT> is a full-text search engine
 that indexes all documents available in CPAN authors directories. If
-C<CPAN::WAIT> is installed on your system, the interactive shell
-of CPAN.pm will enable the C<wq>, C<wr>, C<wd>, C<wl>, and C<wh>
-commands which send queries to the WAIT server that has been configured
-for your installation.
+C<CPAN::WAIT> is installed on your system, the interactive shell of
+CPAN.pm will enable the C<wq>, C<wr>, C<wd>, C<wl>, and C<wh> commands
+which send queries to the WAIT server that has been configured for your
+installation.
 
 All other methods provided are accessible in a programmer style and in an
 interactive shell style.
@@ -5697,6 +6093,13 @@ 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 ls author
+
+C<ls> lists all distribution files in and below an author's CPAN
+directory. Only those files that contain modules are listed and if
+there is more than one for any given module, only the most recent one
+is listed.
+
 =item Signals
 
 CPAN.pm installs signal handlers for SIGINT and SIGTERM. While you are
@@ -5767,10 +6170,11 @@ separated):
 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 underbar into the
-visible version number), so the really hottest and newest distribution
-file 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
+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
 
@@ -5809,6 +6213,12 @@ list of CPAN::Module objects according to the C<@things> arguments
 given. In scalar context it only returns 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 fro distributions.
+
 =item Programming Examples
 
 This enables the programmer to do operations that combine
@@ -5818,7 +6228,7 @@ functionalities that are available in the shell.
     perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)'
 
     # install my favorite programs if necessary:
-    for $mod (qw(Net::FTP MD5 Data::Dumper)){
+    for $mod (qw(Net::FTP Digest::MD5 Data::Dumper)){
         my $obj = CPAN::Shell->expand('Module',$mod);
         $obj->install;
     }
@@ -5871,7 +6281,299 @@ tricks:
 
 =back
 
-=head2 Methods in the four Classes
+=head2 Methods in the other Classes
+
+The programming interface for the classes CPAN::Module,
+CPAN::Distribution, CPAN::Bundle, and CPAN::Author is still considered
+beta and partially even alpha. In the following paragraphs only those
+methods are documented that have proven useful over a longer time and
+thus are unlikely to change.
+
+=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 normally would have failed. 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.
+
+=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 uptodate.
+
+=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::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.
+Only works for distributions listed in the 02packages.details.txt.gz
+file. This typically means that only the 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 normally would have failed. 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.
+
+=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> will be issued in
+any case and if this fails, the install will be canceled. The
+cancellation can be avoided by letting C<force> run the C<install> for
+you.
+
+=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> and C<make> there.
+
+=item CPAN::Distribution::prereq_pm()
+
+Returns the hash reference that has been announced by a distribution
+as the PREREQ_PM hash in the Makefile.PL. Note: works only 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::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
+uptodate. Relies on containsmods.
+
+=item CPAN::Index::force_reload()
+
+Forces a reload of all indices.
+
+=item CPAN::Index::reload()
+
+Reloads all indices if they have been read 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
+
+=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::force($method,@args)
+
+Forces CPAN to perform a task that normally would have failed. 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.
+
+=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 like perl itself stops searching @INC when it finds a
+module.
+
+=item CPAN::Module::inst_version()
+
+Returns the version number of the 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 is not installed.
+
+=item CPAN::Module::readme()
+
+Runs a C<readme> on the distribution associated with this module.
+
+=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
 
@@ -5929,7 +6631,7 @@ If you have a local mirror of CPAN and can access all files with
 "file:" URLs, then you only need a perl better 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 an URL that is not C<ftp:>.
+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
@@ -6009,6 +6711,8 @@ defined:
   dontload_hash      anonymous hash: modules in the keys will not be
                      loaded by the CPAN::has_inst() routine
   gzip              location of external program gzip
+  histfile           file to maintain history between sessions
+  histsize           maximum number of lines to keep in histfile
   inactivity_timeout breaks interactive Makefile.PLs after this
                      many seconds inactivity. Set to 0 to never break.
   inhibit_startup_message
@@ -6022,6 +6726,8 @@ defined:
   prerequisites_policy
                      what to do if you are missing module prerequisites
                      ('follow' automatically, 'ask' me, or 'ignore')
+  proxy_user         username for accessing an authenticating proxy
+  proxy_pass         password for accessing an authenticating proxy
   scan_cache        controls scanning of cache ('atstart' or 'never')
   tar                location of external program tar
   term_is_latin      if true internal UTF-8 is translated to ISO-8859-1
@@ -6109,11 +6815,11 @@ development will go towards strong authentication.
 
 Most functions in package CPAN are exported per default. The reason
 for this is that the primary use is intended for the cpan shell or for
-oneliners.
+one-liners.
 
 =head1 POPULATE AN INSTALLATION WITH LOTS OF MODULES
 
-Populating a freshly installed perl with your favorite modules is pretty
+Populating a freshly installed perl with my 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
@@ -6150,7 +6856,7 @@ that you can configure ncftp so that it works for your firewall.
 
 Firewalls can be categorized into three basic types.
 
-=over
+=over 4
 
 =item http firewall
 
@@ -6158,14 +6864,14 @@ This is where the firewall machine runs a web server and to access the
 outside world you must do it via the web server. If you set environment
 variables like http_proxy or ftp_proxy to a values beginning with http://
 or in your web browser you have to set proxy information then you know
-you are running a http firewall.
+you are running an http firewall.
 
 To access servers outside these types of firewalls with perl (even for
 ftp) you will need to use LWP.
 
 =item ftp firewall
 
-This where the firewall machine runs a ftp server. This kind of
+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"
@@ -6175,7 +6881,7 @@ will need to use Net::FTP.
 
 =item One way visibility
 
-I say one way visibility as these firewalls try to make themselve look
+I say one way visibility as these firewalls try to make themselves look
 invisible to the users inside the firewall. An FTP data connection is
 normally created by sending the remote server your IP address and then
 listening for the connection. But the remote server will not be able to
@@ -6184,7 +6890,7 @@ FTP connections need to be done in a passive mode.
 
 There are two that I can think off.
 
-=over
+=over 4
 
 =item SOCKS
 
@@ -6197,7 +6903,17 @@ the firewall as if it is not there.
 
 This is the firewall implemented in the Linux kernel, it allows you to
 hide a complete network behind one IP address. With this firewall no
-special compiling is need as you can access hosts directly.
+special compiling is needed as you can access hosts directly.
+
+For accessing ftp servers behind such firewalls you may need to set
+the environment variable C<FTP_PASSIVE> to a true value, e.g.
+
+    env FTP_PASSIVE=1 perl -MCPAN -eshell
+
+or
+
+    perl -MCPAN -e '$ENV{FTP_PASSIVE} = 1; shell'
+
 
 =back
 
@@ -6219,14 +6935,16 @@ like
 
     o conf ncftp "/usr/bin/ncftp -f /home/scott/ncftplogin.cfg"
 
-Your milage may vary...
+Your mileage may vary...
 
 =head1 FAQ
 
-=over
+=over 4
 
-=item 1) I installed a new version of module X but CPAN keeps saying,
-      I have the old version installed
+=item 1)
+
+I installed a new version of module X but CPAN keeps saying,
+I have the old version installed
 
 Most probably you B<do> have the old version installed. This can
 happen if a module installs itself into a different directory in the
@@ -6238,14 +6956,18 @@ 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?
+=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?
+=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
@@ -6259,8 +6981,10 @@ Install the bundle file you produced in the first step with something like
 
 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
+=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
 
@@ -6270,8 +6994,9 @@ You may want to configure something like
 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?
+=item 5)
+
+I am not root, how can I install a module in a personal directory?
 
 You will most probably like something like this:
 
@@ -6294,14 +7019,17 @@ or setting the PERL5LIB environment variable.
 Another thing you should bear in mind is that the UNINST parameter
 should never be set if you are not root.
 
-=item 6) How to get a package, unwrap it, and make a change before
-      building it?
+=item 6)
+
+How to get a package, unwrap it, and make a change before building it?
 
   look Sybase::Sybperl
 
-=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?
+=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
@@ -6318,21 +7046,27 @@ definition file manually. It is planned to improve the metadata
 situation for dependencies on CPAN in general, but this will still
 take some time.
 
-=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?
+=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 error msg about line 1 to 4,
-      setting meta input/output via the /etc/inputrc file.
+=item 9)
+
+When I run CPAN's shell, I get error msg about line 1 to 4,
+setting meta input/output via the /etc/inputrc file.
 
 Some versions of readline are picky about capitalization in the
 /etc/inputrc file and specifically RedHat 6.2 comes with a
 /etc/inputrc that contains the word C<on> in lowercase. Change the
 occurrences of C<on> to C<On> and the bug should disappear.
 
-=item 10) Some authors have strange characters in their names.
+=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
@@ -6350,7 +7084,7 @@ becomes stable with regard to charset issues.
 
 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/, modules/ and scripts/. CPAN is 
+but they are not. PAUSE is authors/, modules/ and scripts/. CPAN is
 PAUSE plus the clpa/, doc/, misc/, ports/, and src/.
 
 Future development should be directed towards a better integration of
@@ -6365,6 +7099,11 @@ traditional method of building a Perl module package from a shell.
 
 Andreas Koenig E<lt>andreas.koenig@anima.deE<gt>
 
+=head1 TRANSLATIONS
+
+Kawai,Takanori provides a Japanese translation of this manpage at
+http://member.nifty.ne.jp/hippo2000/perltips/CPAN.htm
+
 =head1 SEE ALSO
 
 perl(1), CPAN::Nox(3)