[BUG:PATCH] dumpvar.pl parses some references incorrectly
[p5sagit/p5-mst-13.2.git] / lib / CPAN.pm
index 2a5ef29..c8b7b28 100644 (file)
@@ -1,11 +1,11 @@
 package CPAN;
 use vars qw{$META $Signal $Cwd $End $Suppress_readline};
 
-$VERSION = '1.19';
+$VERSION = '1.2401';
 
-# $Id: CPAN.pm,v 1.121 1997/02/03 09:08:23 k Exp $
+# $Id: CPAN.pm,v 1.139 1997/03/31 22:43:23 k Exp $
 
-# my $version = substr q$Revision: 1.121 $, 10; # only used during development
+# my $version = substr q$Revision: 1.139 $, 10; # only used during development
 
 use Carp ();
 use Config ();
@@ -22,7 +22,9 @@ use Safe ();
 use Text::ParseWords ();
 use Text::Wrap;
 
-$Cwd = Cwd::cwd();
+my $getcwd;
+$getcwd  = $CPAN::Config->{'getcwd'} || 'cwd';
+$Cwd = Cwd->$getcwd();
 
 END { $End++; &cleanup; }
 
@@ -56,8 +58,6 @@ use strict qw(vars);
 $META ||= new CPAN;                 # In case we reeval ourselves we
                                     # need a ||
 
-CPAN::Config->load unless defined $CPAN::No_Config_is_ok;
-
 @EXPORT = qw( 
             autobundle bundle expand force get
             install make readme recompile shell test clean
@@ -129,7 +129,7 @@ sub checklock {
                        qq{    kill $other\n}.
                            qq{    rm $lockfile\n};
            } elsif (-w $lockfile) {
-               my($ans)=
+               my($ans) =
                    ExtUtils::MakeMaker::prompt
                        (qq{Other job not responding. Shall I overwrite }.
                         qq{the lockfile? (Y/N)},"y");
@@ -182,8 +182,7 @@ or
        $Signal = 1;
     };
     $SIG{'__DIE__'} = \&cleanup;
-    print STDERR "Signal handler set.\n"
-       unless $CPAN::Config->{'inhibit_startup_message'};
+    $self->debug("Signal handler set.") if $CPAN::DEBUG;
 }
 
 #-> sub CPAN::DESTROY ;
@@ -195,7 +194,7 @@ sub DESTROY {
 sub exists {
     my($mgr,$class,$id) = @_;
     CPAN::Index->reload;
-    Carp::croak "exists called without class argument" unless $class;
+    ### Carp::croak "exists called without class argument" unless $class;
     $id ||= "";
     exists $META->{$class}{$id};
 }
@@ -234,7 +233,7 @@ sub hasMD5 {
        eval {require MD5;};
        if ($@) {
            print "MD5 security checks disabled because MD5 not installed.
-  Please consider installing MD5\n";
+  Please consider installing the MD5 module\n";
            $self->{'hasMD5'} = 0;
        } else {
            $self->{'hasMD5'}++;
@@ -262,8 +261,9 @@ sub hasWAIT {
 #-> sub CPAN::instance ;
 sub instance {
     my($mgr,$class,$id) = @_;
+    ### CPAN::Index->reload; ### not faster: unless time - $CPAN::Index::last_time > 60;
     CPAN::Index->reload;
-    Carp::croak "instance called without class argument" unless $class;
+    ### Carp::croak "instance called without class argument" unless $class;
     $id ||= "";
     $META->{$class}{$id} ||= $class->new(ID => $id );
 }
@@ -297,7 +297,7 @@ sub shell {
     local($^W) = 1;
     unless ($Suppress_readline) {
        require Term::ReadLine;
-       import Term::ReadLine;
+#      import Term::ReadLine;
        $term = new Term::ReadLine 'CPAN Monitor';
        $readline::rl_completion_function =
            $readline::rl_completion_function = 'CPAN::Complete::complete';
@@ -305,11 +305,13 @@ sub shell {
 
     no strict;
     $META->checklock();
-    my $cwd = Cwd::cwd();
-    # How should we determine if we have more than stub ReadLine enabled?
+    my $getcwd;
+    $getcwd = $CPAN::Config->{'getcwd'} || 'cwd';
+    my $cwd = Cwd->$getcwd();
     my $rl_avail = $Suppress_readline ? "suppressed" :
-       defined &Term::ReadLine::Perl::readline ? "enabled" :
-           "available (get Term::ReadKey and Term::ReadLine::Perl)";
+       ($term->ReadLine ne "Term::ReadLine::Stub") ? "enabled" :
+           "available (get Term::ReadKey and Term::ReadLine::Perl ".
+           "or get Term::ReadLine::Gnu)";
 
     print qq{
 cpan shell -- CPAN exploration and modules installation (v$CPAN::VERSION)
@@ -319,10 +321,10 @@ Readline support $rl_avail
     while () {
        if ($Suppress_readline) {
            print $prompt;
-           last unless defined ($_ = <>);
+           last unless defined ($_ = <> );
            chomp;
        } else {
-#           if ($CPAN::DEBUG) {
+#           if (defined($CPAN::ANDK) && $CPAN::DEBUG) { # !$CPAN::ANDK++;$CPAN::DEBUG=1024
 #               my($report,$item);
 #               $report = "";
 #               for $item (qw/ReadLine IN OUT MinLine findConsole Features/) {
@@ -330,8 +332,9 @@ Readline support $rl_avail
 #                   $report .= $term->$item() || "";
 #                   $report .= "\n";
 #               }
-#               CPAN->debug($report);
-#           }
+#               print $report;
+#              CPAN->debug($report);
+#          }
            last unless defined ($_ = $term->readline($prompt));
        }
        s/^\s//;
@@ -399,14 +402,14 @@ sub cachesize {
 # }
 
 #-> sub CPAN::CacheMgr::clean_cache ;
-sub clean_cache {
-    my $self = shift;
-    my $dir;
-    while ($self->{DU} > $self->{'MAX'} and $dir = shift @{$self->{FIFO}}) {
-       $self->force_clean_cache($dir);
-    }
-    $self->debug("leaving clean_cache with $self->{DU}") if $CPAN::DEBUG;
-}
+#=# sub clean_cache {
+#=#    my $self = shift;
+#=#    my $dir;
+#=#    while ($self->{DU} > $self->{'MAX'} and $dir = shift @{$self->{FIFO}}) {
+#=#        $self->force_clean_cache($dir);
+#=#    }
+#=#    $self->debug("leaving clean_cache with $self->{DU}") if $CPAN::DEBUG;
+#=# }
 
 #-> sub CPAN::CacheMgr::dir ;
 sub dir {
@@ -416,8 +419,11 @@ sub dir {
 #-> sub CPAN::CacheMgr::entries ;
 sub entries {
     my($self,$dir) = @_;
+    $self->debug("reading dir[$dir]") if $CPAN::DEBUG;
     $dir ||= $self->{ID};
-    my($cwd) = Cwd::cwd();
+    my $getcwd;
+    $getcwd  = $CPAN::Config->{'getcwd'} || 'cwd';
+    my($cwd) = Cwd->$getcwd();
     chdir $dir or Carp::croak("Can't chdir to $dir: $!");
     my $dh = DirHandle->new(".") or Carp::croak("Couldn't opendir $dir: $!");
     my(@entries);
@@ -432,22 +438,22 @@ sub entries {
        }
     }
     chdir $cwd or Carp::croak("Can't chdir to $cwd: $!");
-    sort {-M $b <=> -M $a} @entries;
+    sort { -M $b <=> -M $a} @entries;
 }
 
 #-> sub CPAN::CacheMgr::disk_usage ;
 sub disk_usage {
     my($self,$dir) = @_;
-    if (! defined $dir or $dir eq "") {
-       $self->debug("Cannot determine disk usage for some reason") if $CPAN::DEBUG;
-       return;
-    }
-    return if defined $self->{SIZE}{$dir};
+#    if (! defined $dir or $dir eq "") {
+#      $self->debug("Cannot determine disk usage for some reason") if $CPAN::DEBUG;
+#      return;
+#    }
+    return if $self->{SIZE}{$dir};
     local($Du) = 0;
     find(
         sub {
             return if -l $_;
-            $Du += -s;
+            $Du += -s _;
         },
         $dir
        );
@@ -456,14 +462,10 @@ sub disk_usage {
     $self->debug("measured $dir is $Du") if $CPAN::DEBUG;
     $self->{DU} += $Du/1024/1024;
     if ($self->{DU} > $self->{'MAX'} ) {
-       my($toremove) = $self->{FIFO}[0];
+       my($toremove) = shift @{$self->{FIFO}};
        printf "...Hold on a sec... cleaning from cache (%.1f>%.1f MB): $toremove\n",
                $self->{DU}, $self->{'MAX'};
-       $self->clean_cache;
-    } else {
-       $self->debug("NOT have to clean the cache: $self->{DU} <= $self->{'MAX'}")
-           if $CPAN::DEBUG;
-       $self->debug($self->as_string) if $CPAN::DEBUG;
+       $self->force_clean_cache($toremove);
     }
     $self->{DU};
 }
@@ -481,6 +483,9 @@ sub force_clean_cache {
 #-> sub CPAN::CacheMgr::new ;
 sub new {
     my $class = shift;
+    my $time = time;
+    my($debug,$t2);
+    $debug = "";
     my $self = {
                ID => $CPAN::Config->{'build_dir'},
                MAX => $CPAN::Config->{'build_cache'},
@@ -493,9 +498,12 @@ sub new {
     my $e;
     for $e ($self->entries) {
        next if $e eq ".." || $e eq ".";
-       $self->debug("Have to check size $e") if $CPAN::DEBUG;
        $self->disk_usage($e);
     }
+    $t2 = time;
+    $debug .= "timing of CacheMgr->new: ".($t2 - $time);
+    $time = $t2;
+    CPAN->debug($debug) if $CPAN::DEBUG;
     $self;
 }
 
@@ -621,7 +629,7 @@ EOF
 
     #$mode = 0444 | ( $mode & 0111 ? 0111 : 0 );
     #chmod $mode, $configpm;
-    $self->defaults;
+###why was that so?    $self->defaults;
     print "commit: wrote $configpm\n";
     1;
 }
@@ -649,84 +657,88 @@ sub init {
 my $dot_cpan;
 #-> sub CPAN::Config::load ;
 sub load {
-    my($self) = @_;
+    my($self) = shift;
+    my(@miss);
     eval {require CPAN::Config;};       # We eval, because of some MakeMaker problems
     unshift @INC, $CPAN::META->catdir($ENV{HOME},".cpan") unless $dot_cpan++;
     eval {require CPAN::MyConfig;};     # where you can override system wide settings
-    unless ( $self->load_succeeded ) {
-         require CPAN::FirstTime;
-         my($configpm,$fh);
-         if (defined $INC{"CPAN/Config.pm"} && -w $INC{"CPAN/Config.pm"}) {
-             $configpm = $INC{"CPAN/Config.pm"};
-         } elsif (defined $INC{"CPAN/MyConfig.pm"} && -w $INC{"CPAN/MyConfig.pm"}) {
-             $configpm = $INC{"CPAN/MyConfig.pm"};
-         } 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");
-             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");
-                     }
-                 }
-             }
-             unless ($configpm) {
-                 $configpmdir = MM->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 {
-                     Carp::confess(qq{WARNING: CPAN.pm is unable to }.
-                                   qq{create a configuration file.});
-                 }
-             }
-         }
-         CPAN->debug(qq{Calling CPAN::FirstTime::init("$configpm")})
-             if $CPAN::DEBUG;
-         print qq{
-Configuring CPAN.pm.
+    return unless @miss = $self->not_loaded;
+    require CPAN::FirstTime;
+    my($configpm,$fh,$redo);
+    $redo ||= "";
+    if (defined $INC{"CPAN/Config.pm"} && -w $INC{"CPAN/Config.pm"}) {
+       $configpm = $INC{"CPAN/Config.pm"};
+       $redo++;
+    } elsif (defined $INC{"CPAN/MyConfig.pm"} && -w $INC{"CPAN/MyConfig.pm"}) {
+       $configpm = $INC{"CPAN/MyConfig.pm"};
+       $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");
+       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");
+               }
+           }
+       }
+       unless ($configpm) {
+           $configpmdir = MM->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 {
+               Carp::confess(qq{WARNING: CPAN.pm is unable to }.
+                             qq{create a configuration file.});
+           }
+       }
+    }
+    local($") = ", ";
+    print qq{
+We have to reconfigure CPAN.pm due to following uninitialized parameters:
+
+@miss
+} if $redo ;
+    print qq{
 $configpm initialized.
 };
-         CPAN::FirstTime::init($configpm);
-    }
+    sleep 2;
+    CPAN::FirstTime::init($configpm);
 }
 
-#-> sub CPAN::Config::load_succeeded ;
-sub load_succeeded {
-    my($miss) = 0;
+#-> sub CPAN::Config::not_loaded ;
+sub not_loaded {
+    my(@miss);
     for (qw(
            cpan_home keep_source_where build_dir build_cache index_expire
            gzip tar unzip make pager makepl_arg make_arg make_install_arg
            urllist inhibit_startup_message ftp_proxy http_proxy no_proxy
           )) {
-       unless (defined $CPAN::Config->{$_}){
-           $miss++;
-           CPAN->debug("undefined configuration parameter $_") if $CPAN::DEBUG;
-       }
+       push @miss, $_ unless defined $CPAN::Config->{$_};
     }
-    return !$miss;
+    return @miss;
 }
 
 #-> sub CPAN::Config::unload ;
@@ -782,7 +794,7 @@ sub AUTOLOAD {
            CPAN::WAIT->wh;
            return;
        } else {
-           warn qq{
+           print STDERR qq{
 Commands starting with "w" require CPAN::WAIT to be installed.
 Please consider installing CPAN::WAIT to use the fulltext index.
 Type "install CPAN::WAIT" and restart CPAN.pm.
@@ -865,7 +877,7 @@ sub i {
     for $type (@type) {
        push @result, $self->expand($type,@args);
     }
-    my $result =  @result==1 ?
+    my $result =  @result == 1 ?
        $result[0]->as_string :
            join "", map {$_->as_glimpse} @result;
     $result ||= "No objects found of any type for argument @args\n";
@@ -917,11 +929,13 @@ sub o {
                    }
                    $CPAN::DEBUG = $max;
                } else {
+                   my($known) = 0;
                    for (keys %CPAN::DEBUG) {
                        next unless lc($_) eq lc($what);
                        $CPAN::DEBUG |= $CPAN::DEBUG{$_};
+                       $known = 1;
                    }
-                   print "unknown argument [$what]\n";
+                   print "unknown argument [$what]\n" unless $known;
                }
            }
        } else {
@@ -951,7 +965,10 @@ Known options:
 
 #-> sub CPAN::Shell::reload ;
 sub reload {
-    if ($_[1] =~ /cpan/i) {
+    my($self,$command,@arg) = @_;
+    $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($/);
@@ -970,8 +987,11 @@ sub reload {
        eval <$fh>;
        warn $@ if $@;
        print "\n$redef subroutines redefined\n";
-    } elsif ($_[1] =~ /index/) {
+    } elsif ($command =~ /index/) {
        CPAN::Index->force_reload;
+    } else {
+       print qq{cpan     re-evals the CPAN.pm file\n};
+       print qq{index    re-reads the index files\n};
     }
 }
 
@@ -1088,7 +1108,7 @@ sub _u_r_common {
        }
     }
     if ($what eq "r" && $version_zeroes) {
-       my $s = $version_zeroes>1 ? "s have" : " has";
+       my $s = $version_zeroes > 1 ? "s have" : " has";
        print qq{$version_zeroes installed module$s no version number to compare\n};
     }
     @result;
@@ -1193,7 +1213,7 @@ sub expand {
            push @m, $obj;
        }
     }
-    return @m;
+    return wantarray ? @m : $m[0];
 }
 
 #-> sub CPAN::Shell::format_result ;
@@ -1202,7 +1222,7 @@ sub format_result {
     my($type,@args) = @_;
     @args = '/./' unless @args;
     my(@result) = $self->expand($type,@args);
-    my $result =  @result==1 ?
+    my $result =  @result == 1 ?
        $result[0]->as_string :
            join "", map {$_->as_glimpse} @result;
     $result ||= "No objects of type $type found for argument @args\n";
@@ -1248,7 +1268,13 @@ sub rematein {
            $obj = $CPAN::META->instance('CPAN::Author',$s);
            print "Don't be silly, you can't $meth ", $obj->fullname, " ;-)\n";
        } else {
-           print "Warning: Cannot $meth $s, don't know what it is\n";
+           print qq{Warning: Cannot $meth $s, don\'t know what it is.
+Try the command
+
+    i /$s/
+
+to find objects with similar identifiers.
+};
        }
     }
 }
@@ -1313,6 +1339,7 @@ sub localize {
     $self->debug("file [$file] aslocal [$aslocal]") if $CPAN::DEBUG;
 
     return $aslocal if -f $aslocal && -r _ && ! $force;
+    rename $aslocal, "$aslocal.bak" if -f $aslocal;
 
     my($aslocal_dir) = File::Basename::dirname($aslocal);
     File::Path::mkpath($aslocal_dir);
@@ -1361,7 +1388,7 @@ sub localize {
            return $l if -f $l && -r _;
            # Maybe mirror has compressed it?
            if (-f "$l.gz") {
-               $self->debug("found compressed $l.gz");
+               $self->debug("found compressed $l.gz") if $CPAN::DEBUG;
                system("$CPAN::Config->{gzip} -dc $l.gz > $aslocal");
                return $aslocal if -f $aslocal;
            }
@@ -1413,12 +1440,13 @@ Trying with $funkyftp to get
            $want_compressed = $aslocal =~ s/\.gz//;
            my($source_switch) = "";
            $source_switch = "-source" if $funkyftp =~ /\blynx$/;
+           $source_switch = "-c" if $funkyftp =~ /\bncftp$/;
            my($system) = "$funkyftp $source_switch '$url' > $aslocal";
            my($wstatus);
            if (($wstatus = system($system)) == 0) {
                if ($want_compressed) {
                    $system = "$CPAN::Config->{'gzip'} -dt $aslocal";
-                   if (system($system)==0) {
+                   if (system($system) == 0) {
                        rename $aslocal, "$aslocal.gz";
                    } else {
                        $system = "$CPAN::Config->{'gzip'} $aslocal";
@@ -1427,7 +1455,7 @@ Trying with $funkyftp to get
                    return "$aslocal.gz";
                } else {
                    $system = "$CPAN::Config->{'gzip'} -dt $aslocal";
-                   if (system($system)==0) {
+                   if (system($system) == 0) {
                        $system = "$CPAN::Config->{'gzip'} -d $aslocal";
                        system($system);
                    } else {
@@ -1451,7 +1479,7 @@ returned status $estatus (wstat $wstatus)
                my $timestamp = 0;
                my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
                   $ctime,$blksize,$blocks) = stat($aslocal);
-               $timestamp = $mtime ||=0;
+               $timestamp = $mtime ||= 0;
 
                my($netrc) = CPAN::FTP::netrc->new;
                my($verbose) = $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG ? " -v" : "";
@@ -1568,9 +1596,80 @@ Subprocess "|$CPAN::Config->{'ftp'}$verbose -n"
        print Text::Wrap::wrap("","",$mess), "\n";
     }
     print "Cannot fetch $file\n";
+    if (-f "$aslocal.bak") {
+       rename "$aslocal.bak", $aslocal;
+       print "Trying to get away with old file:\n";
+       print $self->ls($aslocal);
+       return $aslocal;
+    }
     return;
 }
 
+# find2perl needs modularization, too, all the following is stolen
+# from there
+sub ls {
+    my($self,$name) = @_;
+    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$sizemm,
+     $atime,$mtime,$ctime,$blksize,$blocks) = lstat($name);
+
+    my($perms,%user,%group);
+    my $pname = $name;
+
+    if (defined $blocks) {
+       $blocks = int(($blocks + 1) / 2);
+    }
+    else {
+       $blocks = int(($sizemm + 1023) / 1024);
+    }
+
+    if    (-f _) { $perms = '-'; }
+    elsif (-d _) { $perms = 'd'; }
+    elsif (-c _) { $perms = 'c'; $sizemm = &sizemm; }
+    elsif (-b _) { $perms = 'b'; $sizemm = &sizemm; }
+    elsif (-p _) { $perms = 'p'; }
+    elsif (-S _) { $perms = 's'; }
+    else         { $perms = 'l'; $pname .= ' -> ' . readlink($_); }
+
+    my(@rwx) = ('---','--x','-w-','-wx','r--','r-x','rw-','rwx');
+    my(@moname) = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+    my $tmpmode = $mode;
+    my $tmp = $rwx[$tmpmode & 7];
+    $tmpmode >>= 3;
+    $tmp = $rwx[$tmpmode & 7] . $tmp;
+    $tmpmode >>= 3;
+    $tmp = $rwx[$tmpmode & 7] . $tmp;
+    substr($tmp,2,1) =~ tr/-x/Ss/ if -u _;
+    substr($tmp,5,1) =~ tr/-x/Ss/ if -g _;
+    substr($tmp,8,1) =~ tr/-x/Tt/ if -k _;
+    $perms .= $tmp;
+
+    my $user = $user{$uid} || $uid;   # too lazy to implement lookup
+    my $group = $group{$gid} || $gid;
+
+    my($sec,$min,$hour,$mday,$mon,$year) = localtime($mtime);
+    my($timeyear);
+    my($moname) = $moname[$mon];
+    if (-M _ > 365.25 / 2) {
+       $timeyear = $year + 1900;
+    }
+    else {
+       $timeyear = sprintf("%02d:%02d", $hour, $min);
+    }
+
+    sprintf "%5lu %4ld %-10s %2d %-8s %-8s %8s %s %2d %5s %s\n",
+           $ino,
+                $blocks,
+                     $perms,
+                           $nlink,
+                               $user,
+                                    $group,
+                                         $sizemm,
+                                             $moname,
+                                                $mday,
+                                                    $timeyear,
+                                                        $pname;
+}
+
 package CPAN::FTP::netrc;
 
 sub new {
@@ -1596,7 +1695,7 @@ sub new {
                my($t) = shift @tokens;
                if ($t eq "default"){
                    $hasdefault++;
-                   warn "saw a default entry before tokens[@tokens]";
+                   # warn "saw a default entry before tokens[@tokens]";
                    last NETRC;
                }
                last TOKEN if $t eq "macdef";
@@ -1697,8 +1796,8 @@ sub complete_reload {
     my(@words) = split " ", $line;
     CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG;
     my(@ok) = qw(cpan index);
-    return @ok if @words==1;
-    return grep /^\Q$word\E/, @ok if @words==2 && $word;
+    return @ok if @words == 1;
+    return grep /^\Q$word\E/, @ok if @words == 2 && $word;
 }
 
 #-> sub CPAN::Complete::complete_option ;
@@ -1708,8 +1807,8 @@ sub complete_option {
     my(@words) = split " ", $line;
     CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG;
     my(@ok) = qw(conf debug);
-    return @ok if @words==1;
-    return grep /^\Q$word\E/, @ok if @words==2 && $word;
+    return @ok if @words == 1;
+    return grep /^\Q$word\E/, @ok if @words == 2 && $word;
     if (0) {
     } elsif ($words[1] eq 'index') {
        return ();
@@ -1721,9 +1820,10 @@ sub complete_option {
 }
 
 package CPAN::Index;
-use vars qw($last_time);
+use vars qw($last_time $date_of_03);
 @CPAN::Index::ISA = qw(CPAN::Debug);
 $last_time ||= 0;
+$date_of_03 ||= 0;
 
 #-> sub CPAN::Index::force_reload ;
 sub force_reload {
@@ -1738,36 +1838,53 @@ sub reload {
     my $time = time;
 
     # XXX check if a newer one is available. (We currently read it from time to time)
+    for ($CPAN::Config->{index_expire}) {
+       $_ = 0.001 unless $_ > 0.001;
+    }
     return if $last_time + $CPAN::Config->{index_expire}*86400 > $time;
+    my($debug,$t2);
     $last_time = $time;
 
     $cl->read_authindex($cl->reload_x(
                                      "authors/01mailrc.txt.gz",
                                      "01mailrc.gz",
                                      $force));
+    $t2 = time;
+    $debug = "timing reading 01[".($t2 - $time)."]";
+    $time = $t2;
     return if $CPAN::Signal; # this is sometimes lengthy
     $cl->read_modpacks($cl->reload_x(
                                     "modules/02packages.details.txt.gz",
                                     "02packag.gz",
                                     $force));
+    $t2 = time;
+    $debug .= "02[".($t2 - $time)."]";
+    $time = $t2;
     return if $CPAN::Signal; # this is sometimes lengthy
     $cl->read_modlist($cl->reload_x(
                                    "modules/03modlist.data.gz",
                                    "03mlist.gz",
                                    $force));
+    $t2 = time;
+    $debug .= "03[".($t2 - $time)."]";
+    $time = $t2;
+    CPAN->debug($debug) if $CPAN::DEBUG;
 }
 
 #-> sub CPAN::Index::reload_x ;
 sub reload_x {
     my($cl,$wanted,$localname,$force) = @_;
     $force ||= 0;
+    CPAN::Config->load; # we should guarantee loading wherever we rely on Config XXX
     my $abs_wanted = CPAN->catfile($CPAN::Config->{'keep_source_where'},$localname);
-    if (-f $abs_wanted &&
+    if (
+       -f $abs_wanted &&
        -M $abs_wanted < $CPAN::Config->{'index_expire'} &&
-       !$force) {
-       my($s) = $CPAN::Config->{'index_expire'} != 1;
+       !$force
+       ) {
+       my $s = $CPAN::Config->{'index_expire'} == 1 ? "" : "s";
        $cl->debug(qq{$abs_wanted younger than $CPAN::Config->{'index_expire'} }.
-                  qq{day$s. I\'ll use that.\n});
+                  qq{day$s. I\'ll use that.});
        return $abs_wanted;
     } else {
        $force ||= 1;
@@ -1779,7 +1896,7 @@ sub reload_x {
 sub read_authindex {
     my($cl,$index_target) = @_;
     my $pipe = "$CPAN::Config->{gzip} --decompress --stdout $index_target";
-    warn "Going to read $index_target\n";
+    print "Going to read $index_target\n";
     my $fh = FileHandle->new("$pipe|");
     while (<$fh>) {
        chomp;
@@ -1799,26 +1916,25 @@ sub read_authindex {
 sub read_modpacks {
     my($cl,$index_target) = @_;
     my $pipe = "$CPAN::Config->{gzip} --decompress --stdout $index_target";
-    warn "Going to read $index_target\n";
+    print "Going to read $index_target\n";
     my $fh = FileHandle->new("$pipe|");
     while (<$fh>) {
-       next if 1../^\s*$/;
+       last if /^\s*$/;
+    }
+    while (<$fh>) {
        chomp;
        my($mod,$version,$dist) = split;
-       $version =~ s/^\+//;
+###    $version =~ s/^\+//;
 
        # if it as a bundle, instatiate a bundle object
-       my($bundle);
-       if ($mod =~ /^Bundle::(.*)/) {
-           $bundle = $1;
-       }
-
+       my($bundle,$id,$userid);
+       
        if ($mod eq 'CPAN') {
-           local($^W)=0;
+           local($^W)= 0;
            if ($version > $CPAN::VERSION){
                print qq{
-  Hey, you know what? There\'s a new CPAN.pm version (v$version)
-  available! I\'d suggest--provided you have time--you try
+  There\'s a new CPAN.pm version (v$version) available!
+  You might want to try
     install CPAN
     reload cpan
   without quitting the current session. It should be a seemless upgrade
@@ -1828,12 +1944,13 @@ sub read_modpacks {
                print qq{\n};
            }
            last if $CPAN::Signal;
+       } elsif ($mod =~ /^Bundle::(.*)/) {
+           $bundle = $1;
        }
 
-       my($id);
        if ($bundle){
            $id =  $CPAN::META->instance('CPAN::Bundle',$mod);
-           $id->set('CPAN_VERSION' => $version, 'CPAN_FILE' => $dist);
+###        $id->set('CPAN_VERSION' => $version, 'CPAN_FILE' => $dist);
 # This "next" makes us faster but if the job is running long, we ignore
 # rereads which is bad. So we have to be a bit slower again.
 #      } elsif ($CPAN::META->exists('CPAN::Module',$mod)) {
@@ -1841,12 +1958,19 @@ sub read_modpacks {
        } else {
            # instantiate a module object
            $id = $CPAN::META->instance('CPAN::Module',$mod);
-           $id->set('CPAN_VERSION' => $version, 'CPAN_FILE' => $dist);
+###        $id->set('CPAN_VERSION' => $version, 'CPAN_FILE' => $dist)
+###            if $id->cpan_version ne $version || $id->cpan_file ne $dist; # good speed in here
        }
 
-       # determine the author
-       my($userid) = $dist =~ /([^\/]+)/;
-       $id->set('CPAN_USERID' => $userid) if $userid =~ /\w/;
+       if ($id->cpan_file ne $dist){
+           # determine the author
+           ($userid) = $dist =~ /([^\/]+)/;
+           $id->set(
+                    'CPAN_USERID' => $userid,
+                    'CPAN_VERSION' => $version,
+                    'CPAN_FILE' => $dist
+                   );
+       }
 
        # instantiate a distribution object
        unless ($CPAN::META->exists('CPAN::Distribution',$dist)) {
@@ -1854,8 +1978,7 @@ sub read_modpacks {
                                  'CPAN::Distribution' => $dist
                                 )->set(
                                        'CPAN_USERID' => $userid
-                                      )
-                                    if $userid =~ /\w/;
+                                      );
        }
 
        return if $CPAN::Signal;
@@ -1868,15 +1991,19 @@ sub read_modpacks {
 sub read_modlist {
     my($cl,$index_target) = @_;
     my $pipe = "$CPAN::Config->{gzip} --decompress --stdout $index_target";
-    warn "Going to read $index_target\n";
+    print "Going to read $index_target\n";
     my $fh = FileHandle->new("$pipe|");
-    my $eval = "";
+    my $eval;
     while (<$fh>) {
-       next if 1../^\s*$/;
-       next if /use vars/; # will go away in 03...
-       $eval .= $_;
-       return if $CPAN::Signal;
+       if (/^Date:\s+(.*)/){
+           return if $date_of_03 eq $1;
+           ($date_of_03) = $1;
+       }
+       last if /^\s*$/;
     }
+    local($/) = undef;
+    $eval = <$fh>;
+    $fh->close;
     $eval .= q{CPAN::Modulelist->data;};
     local($^W) = 0;
     my($comp) = Safe->new("CPAN::Safe1");
@@ -2015,14 +2142,14 @@ sub get {
        $self->debug("Changed directory to tmp") if $CPAN::DEBUG;
        if ($local_file =~ /z$/i){
            $self->{archived} = "tar";
-           if (system("$CPAN::Config->{gzip} --decompress --stdout $local_file | $CPAN::Config->{tar} xvf -")==0) {
+           if (system("$CPAN::Config->{gzip} --decompress --stdout $local_file | $CPAN::Config->{tar} xvf -")== 0) {
                $self->{unwrapped} = "YES";
            } else {
                $self->{unwrapped} = "NO";
            }
        } elsif ($local_file =~ /zip$/i) {
            $self->{archived} = "zip";
-           if (system("$CPAN::Config->{unzip} $local_file")==0) {
+           if (system("$CPAN::Config->{unzip} $local_file") == 0) {
                $self->{unwrapped} = "YES";
            } else {
                $self->{unwrapped} = "NO";
@@ -2114,10 +2241,12 @@ Please define it with "o conf shell <your shell>"
     my $dist = $self->id;
     my $dir  = $self->dir or $self->get;
     $dir = $self->dir;
-    my $pwd  = Cwd::cwd();
+    my $getcwd;
+    $getcwd = $CPAN::Config->{'getcwd'} || 'cwd';
+    my $pwd  = Cwd->$getcwd();
     chdir($dir);
     print qq{Working directory is $dir.\n};
-    system($CPAN::Config->{'shell'})==0 or die "Subprocess shell error";
+    system($CPAN::Config->{'shell'}) == 0 or die "Subprocess shell error";
     chdir($pwd);
 }
 
@@ -2188,7 +2317,7 @@ sub verifyMD5 {
                                          'force>:-{'
                                         );
        my $system = "$CPAN::Config->{gzip} --decompress $local_file";
-       system($system)==0 or die "Could not uncompress $local_file";
+       system($system) == 0 or die "Could not uncompress $local_file";
        $local_file =~ s/\.gz$//;
     }
     $self->MD5_check_file($local_file,$basename);
@@ -2199,7 +2328,7 @@ sub MD5_check_file {
     my($self,$lfile,$basename) = @_;
     my($cksum);
     my $fh = new FileHandle;
-    local($/)=undef;
+    local($/) = undef;
     if (open $fh, $lfile){
        my $eval = <$fh>;
        close $fh;
@@ -2278,6 +2407,30 @@ sub force {
     delete $self->{'writemakefile'};
 }
 
+#-> sub CPAN::Distribution::perl ;
+sub perl {
+    my($self) = @_;
+    my($perl) = MM->file_name_is_absolute($^X) ? $^X : "";
+    my $getcwd = $CPAN::Config->{'getcwd'} || 'cwd';
+    my $pwd  = Cwd->$getcwd();
+    my $candidate = $CPAN::META->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(), $Config::Config{'binexp'}) {
+                 next unless defined($component) && $component;
+                 my($abs) = MM->catfile($component,$perl_name);
+                 if (MM->maybe_command($abs)) {
+                     $perl = $abs;
+                     last DIST_PERLNAME;
+                 }
+             }
+         }
+    }
+    $perl;
+}
+
 #-> sub CPAN::Distribution::make ;
 sub make {
     my($self) = @_;
@@ -2289,7 +2442,7 @@ sub make {
        $self->{archived} eq "NO" and push @e,
        "Is neither a tar nor a zip archive.";
 
-       $self->{unwrapped} eq "NO"   and push @e,
+       $self->{unwrapped} eq "NO" and push @e,
        "had problems unarchiving. Please build manually";
 
        exists $self->{writemakefile} &&
@@ -2310,62 +2463,54 @@ sub make {
     if ($self->{'configure'}) {
        $system = $self->{'configure'};
     } else {
-       my($perl) = MM->file_name_is_absolute($^X) ? $^X : "";
-       $perl ||= "$CPAN::Cwd/$^X" if -x "$CPAN::Cwd/$^X";
-       unless ($perl) {
-           my ($component,$perl_name);
-           DIST_PERLNAME: foreach $perl_name ($^X, 'perl', 'perl5', "perl$]") {
-                 DIST_COMPONENT: foreach $component (MM->path(), $Config::Config{'binexp'}) {
-                       next unless defined($component) && $component;
-                       my($abs) = MM->catfile($component,$perl_name);
-                       if (MM->maybe_command($abs)) {
-                           $perl = $abs;
-                           last DIST_PERLNAME;
-                       }
+       my($perl) = $self->perl or die "Couldn\'t find executable perl\n";
+       my $switch = "";
+# This needs a handler that can be turned on or off:
+#      $switch = "-MExtUtils::MakeMaker ".
+#          "-Mops=:default,:filesys_read,:filesys_open,require,chdir"
+#          if $] > 5.00310;
+       $system = "$perl $switch Makefile.PL $CPAN::Config->{makepl_arg}";
+    }
+    {
+       local($SIG{ALRM}) = sub { die "inactivity_timeout reached\n" };
+       my($ret,$pid);
+       $@ = "";
+       if ($CPAN::Config->{inactivity_timeout}) {
+           eval {
+               alarm $CPAN::Config->{inactivity_timeout};
+               local $SIG{CHLD} = sub { wait };
+               if (defined($pid = fork)) {
+                   if ($pid) { #parent
+                       wait;
+                   } else {    #child
+                       exec $system;
                    }
+               } else {
+                   print "Cannot fork: $!";
+                   return;
                }
-       }
-       die "Couldn\'t find executable perl\n" unless $perl;
-       $system = "$perl Makefile.PL $CPAN::Config->{makepl_arg}";
-   }
-    $SIG{ALRM} = sub { die "inactivity_timeout reached\n" };
-    my($ret,$pid);
-    $@ = "";
-    if ($CPAN::Config->{inactivity_timeout}) {
-       eval {
-           alarm $CPAN::Config->{inactivity_timeout};
-           #$SIG{CHLD} = \&REAPER;
-           if (defined($pid=fork)) {
-               if ($pid) { #parent
-                   wait;
-               } else {    #child
-                   exec $system;
-               }
-           } else {
-               print "Cannot fork: $!";
+           };
+           alarm 0;
+           if ($@){
+               kill 9, $pid;
+               waitpid $pid, 0;
+               print $@;
+               $self->{writemakefile} = "NO - $@";
+               $@ = "";
                return;
            }
+       } else {
            $ret = system($system);
-       };
-       alarm 0;
-    } else {
-       $ret = system($system);
-    }
-    if ($@){
-       kill 9, $pid;
-       waitpid $pid, 0;
-       print $@;
-       $self->{writemakefile} = "NO - $@";
-       $@ = "";
-       return;
-    } elsif ($ret != 0) {
-        $self->{writemakefile} = "NO";
-        return;
+           if ($ret != 0) {
+               $self->{writemakefile} = "NO";
+               return;
+           }
+       }
     }
     $self->{writemakefile} = "YES";
     return if $CPAN::Signal;
     $system = join " ", $CPAN::Config->{'make'}, $CPAN::Config->{make_arg};
-    if (system($system)==0) {
+    if (system($system) == 0) {
         print "  $system -- OK\n";
         $self->{'make'} = "YES";
     } else {
@@ -2396,7 +2541,7 @@ sub test {
     chdir $self->{'build_dir'} or Carp::croak("Couldn't chdir to $self->{'build_dir'}");
     $self->debug("Changed directory to $self->{'build_dir'}") if $CPAN::DEBUG;
     my $system = join " ", $CPAN::Config->{'make'}, "test";
-    if (system($system)==0) {
+    if (system($system) == 0) {
         print "  $system -- OK\n";
         $self->{'make_test'} = "YES";
     } else {
@@ -2417,7 +2562,7 @@ sub clean {
     chdir $self->{'build_dir'} or Carp::croak("Couldn't chdir to $self->{'build_dir'}");
     $self->debug("Changed directory to $self->{'build_dir'}") if $CPAN::DEBUG;
     my $system = join " ", $CPAN::Config->{'make'}, "clean";
-    if (system($system)==0) {
+    if (system($system) == 0) {
        print "  $system -- OK\n";
        $self->force;
     } else {
@@ -2442,6 +2587,11 @@ sub install {
            $self->{'make'} eq 'NO' and
                push @e, "Oops, make had returned bad status";
 
+       push @e, "make test had returned bad status, won't install without force"
+           if exists $self->{'make_test'} and
+           $self->{'make_test'} eq 'NO' and
+           ! $self->{'force_update'};
+
        exists $self->{'install'} and push @e,
        $self->{'install'} eq "YES" ?
            "Already done" : "Already tried without success";
@@ -2494,14 +2644,13 @@ sub contains {
        # Try to get at it in the cpan directory
        $self->debug("no parsefile") if $CPAN::DEBUG;
        my $dist = $CPAN::META->instance('CPAN::Distribution',$self->{'CPAN_FILE'});
-       $self->debug($dist->as_string) if $CPAN::DEBUG;
        $dist->get;
        $self->debug($dist->as_string) if $CPAN::DEBUG;
        my($todir) = $CPAN::META->catdir($CPAN::Config->{'cpan_home'},"Bundle");
        File::Path::mkpath($todir);
        my($me,$from,$to);
        ($me = $self->id) =~ s/.*://;
-       $from = $CPAN::META->catfile($dist->{'build_dir'},"$me.pm");
+       $from = $self->find_bundle_file($dist->{'build_dir'},"$me.pm");
        $to = $CPAN::META->catfile($todir,"$me.pm");
        File::Copy::copy($from, $to) or Carp::confess("Couldn't copy $from to $to: $!");
        $parsefile = $to;
@@ -2511,6 +2660,7 @@ sub contains {
     local $/ = "\n";
     open($fh,$parsefile) or die "Could not open '$parsefile': $!";
     my $inpod = 0;
+    $self->debug("parsefile[$parsefile]") if $CPAN::DEBUG;
     while (<$fh>) {
        $inpod = /^=(?!head1\s+CONTENTS)/ ? 0 : /^=head1\s+CONTENTS/ ? 1 : $inpod;
        next unless $inpod;
@@ -2521,10 +2671,38 @@ sub contains {
     }
     close $fh;
     delete $self->{STATUS};
-    $self->{CONTAINS} = [@result];
+    $self->{CONTAINS} = join ", ", @result;
+    $self->debug("CONTAINS[@result]") if $CPAN::DEBUG;
     @result;
 }
 
+#-> sub CPAN::Bundle::find_bundle_file
+sub find_bundle_file {
+    my($self,$where,$what) = @_;
+    my $bu = $CPAN::META->catfile($where,$what);
+    return $bu if -f $bu;
+    my $manifest = $CPAN::META->catfile($where,"MANIFEST");
+    unless (-f $manifest) {
+       require ExtUtils::Manifest;
+       my $getcwd = $CPAN::Config->{'getcwd'} || 'cwd';
+       my $cwd = Cwd->$getcwd();
+       chdir $where;
+       ExtUtils::Manifest::mkmanifest();
+       chdir $cwd;
+    }
+    my $fh = FileHandle->new($manifest) or Carp::croak("Couldn't open $manifest: $!");
+    local($/) = "\n";
+    while (<$fh>) {
+       next if /^\s*\#/;
+       my($file) = /(\S+)/;
+       if ($file =~ m|Bundle/$what$|) {
+           $bu = $file;
+           return $CPAN::META->catfile($where,$bu);
+       }
+    }
+    Carp::croak("Could't find a Bundle file in $where");
+}
+
 #-> sub CPAN::Bundle::inst_file ;
 sub inst_file {
     my($self) = @_;
@@ -2532,9 +2710,10 @@ sub inst_file {
     ($me = $self->id) =~ s/.*://;
     $inst_file = $CPAN::META->catfile($CPAN::Config->{'cpan_home'},"Bundle", "$me.pm");
     return $self->{'INST_FILE'} = $inst_file if -f $inst_file;
-    $inst_file = $self->SUPER::inst_file;
-    return $self->{'INST_FILE'} = $inst_file if -f $inst_file;
-    return $self->{'INST_FILE'}; # even if undefined?
+#    $inst_file = 
+    $self->SUPER::inst_file;
+#    return $self->{'INST_FILE'} = $inst_file if -f $inst_file;
+#    return $self->{'INST_FILE'}; # even if undefined?
 }
 
 #-> sub CPAN::Bundle::rematein ;
@@ -2556,6 +2735,13 @@ explicitly a file $s.
     }
 }
 
+#sub CPAN::Bundle::xs_file
+sub xs_file {
+    # If a bundle contains another that contains an xs_file we have
+    # here, we just don't bother I suppose
+    return 0;
+}
+
 #-> sub CPAN::Bundle::force ;
 sub force   { shift->rematein('force',@_); }
 #-> sub CPAN::Bundle::get ;
@@ -2608,7 +2794,7 @@ sub as_string {
                         $sprintf2,
                         'CPAN_USERID',
                         $userid,
-                        $CPAN::META->instance(CPAN::Author,$userid)->fullname
+                        CPAN::Shell->expand('Author',$userid)->fullname
                        )
     }
     push @m, sprintf $sprintf, 'CPAN_VERSION', $self->{CPAN_VERSION} if $self->{CPAN_VERSION};
@@ -2652,7 +2838,10 @@ sub as_string {
        close $fh;
        $self->{MANPAGE} = join " ", @result;
     }
-    push @m, sprintf $sprintf, 'MANPAGE', $self->{MANPAGE} if $self->{MANPAGE};
+    my($item);
+    for $item (qw/MANPAGE CONTAINS/) {
+       push @m, sprintf $sprintf, $item, $self->{$item} if exists $self->{$item};
+    }
     push @m, sprintf $sprintf, 'INST_FILE', $local_file || "(not installed)";
     push @m, sprintf $sprintf, 'INST_VERSION', $self->inst_version if $local_file;
     join "", @m, "\n";
@@ -2720,10 +2909,14 @@ sub install {
     if (defined $inst_file) {
        $have = $self->inst_version;
     }
-    if ($inst_file && $have >= $latest && not exists $self->{'force_update'}) {
-       print $self->id, " is up to date.\n";
-    } else {
-       $doit = 1;
+    if (1){ # A block for scoping $^W, the if is just for the visual
+            # appeal
+       local($^W)=0;
+       if ($inst_file && $have >= $latest && not exists $self->{'force_update'}) {
+           print $self->id, " is up to date.\n";
+       } else {
+           $doit = 1;
+       }
     }
     $self->rematein('install') if $doit;
 }
@@ -2742,6 +2935,7 @@ sub inst_file {
            return $pmfile;
        }
     }
+    return;
 }
 
 #-> sub CPAN::Module::xs_file ;
@@ -2757,6 +2951,7 @@ sub xs_file {
            return $xsfile;
        }
     }
+    return;
 }
 
 #-> sub CPAN::Module::inst_version ;
@@ -2771,7 +2966,11 @@ sub inst_version {
     $have;
 }
 
+# Do this after you have set up the whole inheritance
+CPAN::Config->load unless defined $CPAN::No_Config_is_ok;
+
 1;
+__END__
 
 =head1 NAME
 
@@ -2947,13 +3146,80 @@ perl breaks binary compatibility. If one of the modules that CPAN uses
 is in turn depending on binary compatibility (so you cannot run CPAN
 commands), then you should try the CPAN::Nox module for recovery.
 
+=head2 The 4 Classes: Authors, Bundles, Modules, Distributions
+
+Although it may be considered internal, the class hierarchie does
+matter for both users and programmer. CPAN.pm deals with above
+mentioned four classes, and all those classes share a set of
+methods. It is a classical single polymorphism that is in effect.  A
+metaclass object registers all objects of all kinds and indexes them
+with a string. The strings referencing objects have a separated
+namespace (well, not completely separated):
+
+         Namespace                         Class
+
+   words containing a "/" (slash)      Distribution
+    words starting with Bundle::          Bundle
+          everything else            Module or Author
+
+Modules know their associated Distribution objects. They always refer
+to the most recent official release. Developers may mark their
+releases as unstable development versions (by inserting an underbar
+into the visible version number), so not always is the default
+distribution for a given module the really hottest and newest. If a
+module Foo circulates on CPAN in both version 1.23 and 1.23_90,
+CPAN.pm offers a convenient way to install version 1.23 by saying
+
+    install Foo
+
+This would install the complete distribution file (say
+BAR/Foo-1.23.tar.gz) with all accompanying material in there. But if
+you would like to install version 1.23_90, you need to know where the
+distribution file resides on CPAN relative to the authors/id/
+directory. If the author is BAR, this might be BAR/Foo-1.23_90.tar.gz,
+so he would have say
+
+    install BAR/Foo-1.23_90.tar.gz
+
+The first example will be driven by an object of the class
+CPAN::Module, the second by an object of class Distribution.
+
 =head2 ProgrammerE<39>s interface
 
 If you do not enter the shell, the available shell commands are both
 available as methods (C<CPAN::Shell-E<gt>install(...)>) and as
-functions in the calling package (C<install(...)>). The
-programmerE<39>s interface has beta status. Do not heavily rely on it,
-changes may still be necessary.
+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. The commands that produce listings
+of modules (C<r>, C<autobundle>, C<u>) return a list of the IDs of all
+modules within the list.
+
+=over 2
+
+=item expand($type,@things)
+
+The IDs of all objects available within a program are strings that can
+be expanded to the corresponding real objects with the
+C<CPAN::Shell-E<gt>expand()> method. Expand returns a 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 Programming Examples
+
+This enables the programmer to do operations like these:
+
+    # install everything that is outdated on my disk:
+    perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)'
+
+    # install my favorite programs if necessary:
+    for $mod (qw(Net::FTP MD5 Data::Dumper)){
+        my $obj = CPAN::Shell->expand('Module',$mod);
+        $obj->install;
+    }
+
+=back
 
 =head2 Cache Manager