xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/CPAN.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gate# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*-
2*0Sstevel@tonic-gatepackage CPAN;
3*0Sstevel@tonic-gate$VERSION = '1.76_01';
4*0Sstevel@tonic-gate$VERSION = eval $VERSION;
5*0Sstevel@tonic-gate# $Id: CPAN.pm,v 1.412 2003/07/31 14:53:04 k Exp $
6*0Sstevel@tonic-gate
7*0Sstevel@tonic-gate# only used during development:
8*0Sstevel@tonic-gate$Revision = "";
9*0Sstevel@tonic-gate# $Revision = "[".substr(q$Revision: 1.412 $, 10)."]";
10*0Sstevel@tonic-gate
11*0Sstevel@tonic-gateuse Carp ();
12*0Sstevel@tonic-gateuse Config ();
13*0Sstevel@tonic-gateuse Cwd ();
14*0Sstevel@tonic-gateuse DirHandle;
15*0Sstevel@tonic-gateuse Exporter ();
16*0Sstevel@tonic-gateuse ExtUtils::MakeMaker (); # $SelfLoader::DEBUG=1;
17*0Sstevel@tonic-gateuse File::Basename ();
18*0Sstevel@tonic-gateuse File::Copy ();
19*0Sstevel@tonic-gateuse File::Find;
20*0Sstevel@tonic-gateuse File::Path ();
21*0Sstevel@tonic-gateuse FileHandle ();
22*0Sstevel@tonic-gateuse Safe ();
23*0Sstevel@tonic-gateuse Text::ParseWords ();
24*0Sstevel@tonic-gateuse Text::Wrap;
25*0Sstevel@tonic-gateuse File::Spec;
26*0Sstevel@tonic-gateuse Sys::Hostname;
27*0Sstevel@tonic-gateno lib "."; # we need to run chdir all over and we would get at wrong
28*0Sstevel@tonic-gate            # libraries there
29*0Sstevel@tonic-gate
30*0Sstevel@tonic-gaterequire Mac::BuildTools if $^O eq 'MacOS';
31*0Sstevel@tonic-gate
32*0Sstevel@tonic-gateEND { $End++; &cleanup; }
33*0Sstevel@tonic-gate
34*0Sstevel@tonic-gate%CPAN::DEBUG = qw[
35*0Sstevel@tonic-gate		  CPAN              1
36*0Sstevel@tonic-gate		  Index             2
37*0Sstevel@tonic-gate		  InfoObj           4
38*0Sstevel@tonic-gate		  Author            8
39*0Sstevel@tonic-gate		  Distribution     16
40*0Sstevel@tonic-gate		  Bundle           32
41*0Sstevel@tonic-gate		  Module           64
42*0Sstevel@tonic-gate		  CacheMgr        128
43*0Sstevel@tonic-gate		  Complete        256
44*0Sstevel@tonic-gate		  FTP             512
45*0Sstevel@tonic-gate		  Shell          1024
46*0Sstevel@tonic-gate		  Eval           2048
47*0Sstevel@tonic-gate		  Config         4096
48*0Sstevel@tonic-gate		  Tarzip         8192
49*0Sstevel@tonic-gate		  Version       16384
50*0Sstevel@tonic-gate		  Queue         32768
51*0Sstevel@tonic-gate];
52*0Sstevel@tonic-gate
53*0Sstevel@tonic-gate$CPAN::DEBUG ||= 0;
54*0Sstevel@tonic-gate$CPAN::Signal ||= 0;
55*0Sstevel@tonic-gate$CPAN::Frontend ||= "CPAN::Shell";
56*0Sstevel@tonic-gate$CPAN::Defaultsite ||= "ftp://ftp.perl.org/pub/CPAN";
57*0Sstevel@tonic-gate
58*0Sstevel@tonic-gatepackage CPAN;
59*0Sstevel@tonic-gateuse strict qw(vars);
60*0Sstevel@tonic-gate
61*0Sstevel@tonic-gateuse vars qw($VERSION @EXPORT $AUTOLOAD $DEBUG $META $HAS_USABLE $term
62*0Sstevel@tonic-gate            $Revision $Signal $End $Suppress_readline $Frontend
63*0Sstevel@tonic-gate            $Defaultsite $Have_warned);
64*0Sstevel@tonic-gate
65*0Sstevel@tonic-gate@CPAN::ISA = qw(CPAN::Debug Exporter);
66*0Sstevel@tonic-gate
67*0Sstevel@tonic-gate@EXPORT = qw(
68*0Sstevel@tonic-gate	     autobundle bundle expand force get cvs_import
69*0Sstevel@tonic-gate	     install make readme recompile shell test clean
70*0Sstevel@tonic-gate	    );
71*0Sstevel@tonic-gate
72*0Sstevel@tonic-gate#-> sub CPAN::AUTOLOAD ;
73*0Sstevel@tonic-gatesub AUTOLOAD {
74*0Sstevel@tonic-gate    my($l) = $AUTOLOAD;
75*0Sstevel@tonic-gate    $l =~ s/.*:://;
76*0Sstevel@tonic-gate    my(%EXPORT);
77*0Sstevel@tonic-gate    @EXPORT{@EXPORT} = '';
78*0Sstevel@tonic-gate    CPAN::Config->load unless $CPAN::Config_loaded++;
79*0Sstevel@tonic-gate    if (exists $EXPORT{$l}){
80*0Sstevel@tonic-gate	CPAN::Shell->$l(@_);
81*0Sstevel@tonic-gate    } else {
82*0Sstevel@tonic-gate	$CPAN::Frontend->mywarn(qq{Unknown command "$AUTOLOAD". }.
83*0Sstevel@tonic-gate				qq{Type ? for help.
84*0Sstevel@tonic-gate});
85*0Sstevel@tonic-gate    }
86*0Sstevel@tonic-gate}
87*0Sstevel@tonic-gate
88*0Sstevel@tonic-gate#-> sub CPAN::shell ;
89*0Sstevel@tonic-gatesub shell {
90*0Sstevel@tonic-gate    my($self) = @_;
91*0Sstevel@tonic-gate    $Suppress_readline = ! -t STDIN unless defined $Suppress_readline;
92*0Sstevel@tonic-gate    CPAN::Config->load unless $CPAN::Config_loaded++;
93*0Sstevel@tonic-gate
94*0Sstevel@tonic-gate    my $oprompt = shift || "cpan> ";
95*0Sstevel@tonic-gate    my $prompt = $oprompt;
96*0Sstevel@tonic-gate    my $commandline = shift || "";
97*0Sstevel@tonic-gate
98*0Sstevel@tonic-gate    local($^W) = 1;
99*0Sstevel@tonic-gate    unless ($Suppress_readline) {
100*0Sstevel@tonic-gate	require Term::ReadLine;
101*0Sstevel@tonic-gate        if (! $term
102*0Sstevel@tonic-gate            or
103*0Sstevel@tonic-gate            $term->ReadLine eq "Term::ReadLine::Stub"
104*0Sstevel@tonic-gate           ) {
105*0Sstevel@tonic-gate            $term = Term::ReadLine->new('CPAN Monitor');
106*0Sstevel@tonic-gate        }
107*0Sstevel@tonic-gate	if ($term->ReadLine eq "Term::ReadLine::Gnu") {
108*0Sstevel@tonic-gate	    my $attribs = $term->Attribs;
109*0Sstevel@tonic-gate	     $attribs->{attempted_completion_function} = sub {
110*0Sstevel@tonic-gate		 &CPAN::Complete::gnu_cpl;
111*0Sstevel@tonic-gate	     }
112*0Sstevel@tonic-gate	} else {
113*0Sstevel@tonic-gate	    $readline::rl_completion_function =
114*0Sstevel@tonic-gate		$readline::rl_completion_function = 'CPAN::Complete::cpl';
115*0Sstevel@tonic-gate	}
116*0Sstevel@tonic-gate        if (my $histfile = $CPAN::Config->{'histfile'}) {{
117*0Sstevel@tonic-gate            unless ($term->can("AddHistory")) {
118*0Sstevel@tonic-gate                $CPAN::Frontend->mywarn("Terminal does not support AddHistory.\n");
119*0Sstevel@tonic-gate                last;
120*0Sstevel@tonic-gate            }
121*0Sstevel@tonic-gate            my($fh) = FileHandle->new;
122*0Sstevel@tonic-gate            open $fh, "<$histfile" or last;
123*0Sstevel@tonic-gate            local $/ = "\n";
124*0Sstevel@tonic-gate            while (<$fh>) {
125*0Sstevel@tonic-gate                chomp;
126*0Sstevel@tonic-gate                $term->AddHistory($_);
127*0Sstevel@tonic-gate            }
128*0Sstevel@tonic-gate            close $fh;
129*0Sstevel@tonic-gate        }}
130*0Sstevel@tonic-gate	# $term->OUT is autoflushed anyway
131*0Sstevel@tonic-gate	my $odef = select STDERR;
132*0Sstevel@tonic-gate	$| = 1;
133*0Sstevel@tonic-gate	select STDOUT;
134*0Sstevel@tonic-gate	$| = 1;
135*0Sstevel@tonic-gate	select $odef;
136*0Sstevel@tonic-gate    }
137*0Sstevel@tonic-gate
138*0Sstevel@tonic-gate    # no strict; # I do not recall why no strict was here (2000-09-03)
139*0Sstevel@tonic-gate    $META->checklock();
140*0Sstevel@tonic-gate    my $cwd = CPAN::anycwd();
141*0Sstevel@tonic-gate    my $try_detect_readline;
142*0Sstevel@tonic-gate    $try_detect_readline = $term->ReadLine eq "Term::ReadLine::Stub" if $term;
143*0Sstevel@tonic-gate    my $rl_avail = $Suppress_readline ? "suppressed" :
144*0Sstevel@tonic-gate	($term->ReadLine ne "Term::ReadLine::Stub") ? "enabled" :
145*0Sstevel@tonic-gate	    "available (try 'install Bundle::CPAN')";
146*0Sstevel@tonic-gate
147*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(
148*0Sstevel@tonic-gate			     sprintf qq{
149*0Sstevel@tonic-gatecpan shell -- CPAN exploration and modules installation (v%s%s)
150*0Sstevel@tonic-gateReadLine support %s
151*0Sstevel@tonic-gate
152*0Sstevel@tonic-gate},
153*0Sstevel@tonic-gate                             $CPAN::VERSION,
154*0Sstevel@tonic-gate                             $CPAN::Revision,
155*0Sstevel@tonic-gate                             $rl_avail
156*0Sstevel@tonic-gate                            )
157*0Sstevel@tonic-gate        unless $CPAN::Config->{'inhibit_startup_message'} ;
158*0Sstevel@tonic-gate    my($continuation) = "";
159*0Sstevel@tonic-gate  SHELLCOMMAND: while () {
160*0Sstevel@tonic-gate	if ($Suppress_readline) {
161*0Sstevel@tonic-gate	    print $prompt;
162*0Sstevel@tonic-gate	    last SHELLCOMMAND unless defined ($_ = <> );
163*0Sstevel@tonic-gate	    chomp;
164*0Sstevel@tonic-gate	} else {
165*0Sstevel@tonic-gate	    last SHELLCOMMAND unless
166*0Sstevel@tonic-gate                defined ($_ = $term->readline($prompt, $commandline));
167*0Sstevel@tonic-gate	}
168*0Sstevel@tonic-gate	$_ = "$continuation$_" if $continuation;
169*0Sstevel@tonic-gate	s/^\s+//;
170*0Sstevel@tonic-gate	next SHELLCOMMAND if /^$/;
171*0Sstevel@tonic-gate	$_ = 'h' if /^\s*\?/;
172*0Sstevel@tonic-gate	if (/^(?:q(?:uit)?|bye|exit)$/i) {
173*0Sstevel@tonic-gate	    last SHELLCOMMAND;
174*0Sstevel@tonic-gate	} elsif (s/\\$//s) {
175*0Sstevel@tonic-gate	    chomp;
176*0Sstevel@tonic-gate	    $continuation = $_;
177*0Sstevel@tonic-gate	    $prompt = "    > ";
178*0Sstevel@tonic-gate	} elsif (/^\!/) {
179*0Sstevel@tonic-gate	    s/^\!//;
180*0Sstevel@tonic-gate	    my($eval) = $_;
181*0Sstevel@tonic-gate	    package CPAN::Eval;
182*0Sstevel@tonic-gate	    use vars qw($import_done);
183*0Sstevel@tonic-gate	    CPAN->import(':DEFAULT') unless $import_done++;
184*0Sstevel@tonic-gate	    CPAN->debug("eval[$eval]") if $CPAN::DEBUG;
185*0Sstevel@tonic-gate	    eval($eval);
186*0Sstevel@tonic-gate	    warn $@ if $@;
187*0Sstevel@tonic-gate	    $continuation = "";
188*0Sstevel@tonic-gate	    $prompt = $oprompt;
189*0Sstevel@tonic-gate	} elsif (/./) {
190*0Sstevel@tonic-gate	    my(@line);
191*0Sstevel@tonic-gate	    if ($] < 5.00322) { # parsewords had a bug until recently
192*0Sstevel@tonic-gate		@line = split;
193*0Sstevel@tonic-gate	    } else {
194*0Sstevel@tonic-gate		eval { @line = Text::ParseWords::shellwords($_) };
195*0Sstevel@tonic-gate		warn($@), next SHELLCOMMAND if $@;
196*0Sstevel@tonic-gate                warn("Text::Parsewords could not parse the line [$_]"),
197*0Sstevel@tonic-gate                    next SHELLCOMMAND unless @line;
198*0Sstevel@tonic-gate	    }
199*0Sstevel@tonic-gate	    $CPAN::META->debug("line[".join("|",@line)."]") if $CPAN::DEBUG;
200*0Sstevel@tonic-gate	    my $command = shift @line;
201*0Sstevel@tonic-gate	    eval { CPAN::Shell->$command(@line) };
202*0Sstevel@tonic-gate	    warn $@ if $@;
203*0Sstevel@tonic-gate	    chdir $cwd or $CPAN::Frontend->mydie(qq{Could not chdir to "$cwd": $!});
204*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("\n");
205*0Sstevel@tonic-gate	    $continuation = "";
206*0Sstevel@tonic-gate	    $prompt = $oprompt;
207*0Sstevel@tonic-gate	}
208*0Sstevel@tonic-gate    } continue {
209*0Sstevel@tonic-gate      $commandline = ""; # I do want to be able to pass a default to
210*0Sstevel@tonic-gate                         # shell, but on the second command I see no
211*0Sstevel@tonic-gate                         # use in that
212*0Sstevel@tonic-gate      $Signal=0;
213*0Sstevel@tonic-gate      CPAN::Queue->nullify_queue;
214*0Sstevel@tonic-gate      if ($try_detect_readline) {
215*0Sstevel@tonic-gate	if ($CPAN::META->has_inst("Term::ReadLine::Gnu")
216*0Sstevel@tonic-gate	    ||
217*0Sstevel@tonic-gate	    $CPAN::META->has_inst("Term::ReadLine::Perl")
218*0Sstevel@tonic-gate	   ) {
219*0Sstevel@tonic-gate	    delete $INC{"Term/ReadLine.pm"};
220*0Sstevel@tonic-gate	    my $redef = 0;
221*0Sstevel@tonic-gate	    local($SIG{__WARN__}) = CPAN::Shell::paintdots_onreload(\$redef);
222*0Sstevel@tonic-gate	    require Term::ReadLine;
223*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("\n$redef subroutines in ".
224*0Sstevel@tonic-gate				     "Term::ReadLine redefined\n");
225*0Sstevel@tonic-gate            @_ = ($oprompt,"");
226*0Sstevel@tonic-gate	    goto &shell;
227*0Sstevel@tonic-gate	}
228*0Sstevel@tonic-gate      }
229*0Sstevel@tonic-gate    }
230*0Sstevel@tonic-gate    chdir $cwd or $CPAN::Frontend->mydie(qq{Could not chdir to "$cwd": $!});
231*0Sstevel@tonic-gate}
232*0Sstevel@tonic-gate
233*0Sstevel@tonic-gatepackage CPAN::CacheMgr;
234*0Sstevel@tonic-gate@CPAN::CacheMgr::ISA = qw(CPAN::InfoObj CPAN);
235*0Sstevel@tonic-gateuse File::Find;
236*0Sstevel@tonic-gate
237*0Sstevel@tonic-gatepackage CPAN::Config;
238*0Sstevel@tonic-gateuse vars qw(%can $dot_cpan);
239*0Sstevel@tonic-gate
240*0Sstevel@tonic-gate%can = (
241*0Sstevel@tonic-gate  'commit' => "Commit changes to disk",
242*0Sstevel@tonic-gate  'defaults' => "Reload defaults from disk",
243*0Sstevel@tonic-gate  'init'   => "Interactive setting of all options",
244*0Sstevel@tonic-gate);
245*0Sstevel@tonic-gate
246*0Sstevel@tonic-gatepackage CPAN::FTP;
247*0Sstevel@tonic-gateuse vars qw($Ua $Thesite $Themethod);
248*0Sstevel@tonic-gate@CPAN::FTP::ISA = qw(CPAN::Debug);
249*0Sstevel@tonic-gate
250*0Sstevel@tonic-gatepackage CPAN::LWP::UserAgent;
251*0Sstevel@tonic-gateuse vars qw(@ISA $USER $PASSWD $SETUPDONE);
252*0Sstevel@tonic-gate# we delay requiring LWP::UserAgent and setting up inheritence until we need it
253*0Sstevel@tonic-gate
254*0Sstevel@tonic-gatepackage CPAN::Complete;
255*0Sstevel@tonic-gate@CPAN::Complete::ISA = qw(CPAN::Debug);
256*0Sstevel@tonic-gate@CPAN::Complete::COMMANDS = sort qw(
257*0Sstevel@tonic-gate		       ! a b d h i m o q r u autobundle clean dump
258*0Sstevel@tonic-gate		       make test install force readme reload look
259*0Sstevel@tonic-gate                       cvs_import ls
260*0Sstevel@tonic-gate) unless @CPAN::Complete::COMMANDS;
261*0Sstevel@tonic-gate
262*0Sstevel@tonic-gatepackage CPAN::Index;
263*0Sstevel@tonic-gateuse vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03);
264*0Sstevel@tonic-gate@CPAN::Index::ISA = qw(CPAN::Debug);
265*0Sstevel@tonic-gate$LAST_TIME ||= 0;
266*0Sstevel@tonic-gate$DATE_OF_03 ||= 0;
267*0Sstevel@tonic-gate# use constant PROTOCOL => "2.0"; # outcommented to avoid warning on upgrade from 1.57
268*0Sstevel@tonic-gatesub PROTOCOL { 2.0 }
269*0Sstevel@tonic-gate
270*0Sstevel@tonic-gatepackage CPAN::InfoObj;
271*0Sstevel@tonic-gate@CPAN::InfoObj::ISA = qw(CPAN::Debug);
272*0Sstevel@tonic-gate
273*0Sstevel@tonic-gatepackage CPAN::Author;
274*0Sstevel@tonic-gate@CPAN::Author::ISA = qw(CPAN::InfoObj);
275*0Sstevel@tonic-gate
276*0Sstevel@tonic-gatepackage CPAN::Distribution;
277*0Sstevel@tonic-gate@CPAN::Distribution::ISA = qw(CPAN::InfoObj);
278*0Sstevel@tonic-gate
279*0Sstevel@tonic-gatepackage CPAN::Bundle;
280*0Sstevel@tonic-gate@CPAN::Bundle::ISA = qw(CPAN::Module);
281*0Sstevel@tonic-gate
282*0Sstevel@tonic-gatepackage CPAN::Module;
283*0Sstevel@tonic-gate@CPAN::Module::ISA = qw(CPAN::InfoObj);
284*0Sstevel@tonic-gate
285*0Sstevel@tonic-gatepackage CPAN::Exception::RecursiveDependency;
286*0Sstevel@tonic-gateuse overload '""' => "as_string";
287*0Sstevel@tonic-gate
288*0Sstevel@tonic-gatesub new {
289*0Sstevel@tonic-gate    my($class) = shift;
290*0Sstevel@tonic-gate    my($deps) = shift;
291*0Sstevel@tonic-gate    my @deps;
292*0Sstevel@tonic-gate    my %seen;
293*0Sstevel@tonic-gate    for my $dep (@$deps) {
294*0Sstevel@tonic-gate        push @deps, $dep;
295*0Sstevel@tonic-gate        last if $seen{$dep}++;
296*0Sstevel@tonic-gate    }
297*0Sstevel@tonic-gate    bless { deps => \@deps }, $class;
298*0Sstevel@tonic-gate}
299*0Sstevel@tonic-gate
300*0Sstevel@tonic-gatesub as_string {
301*0Sstevel@tonic-gate    my($self) = shift;
302*0Sstevel@tonic-gate    "\nRecursive dependency detected:\n    " .
303*0Sstevel@tonic-gate        join("\n => ", @{$self->{deps}}) .
304*0Sstevel@tonic-gate            ".\nCannot continue.\n";
305*0Sstevel@tonic-gate}
306*0Sstevel@tonic-gate
307*0Sstevel@tonic-gatepackage CPAN::Shell;
308*0Sstevel@tonic-gateuse vars qw($AUTOLOAD @ISA $COLOR_REGISTERED $ADVANCED_QUERY $PRINT_ORNAMENTING);
309*0Sstevel@tonic-gate@CPAN::Shell::ISA = qw(CPAN::Debug);
310*0Sstevel@tonic-gate$COLOR_REGISTERED ||= 0;
311*0Sstevel@tonic-gate$PRINT_ORNAMENTING ||= 0;
312*0Sstevel@tonic-gate
313*0Sstevel@tonic-gate#-> sub CPAN::Shell::AUTOLOAD ;
314*0Sstevel@tonic-gatesub AUTOLOAD {
315*0Sstevel@tonic-gate    my($autoload) = $AUTOLOAD;
316*0Sstevel@tonic-gate    my $class = shift(@_);
317*0Sstevel@tonic-gate    # warn "autoload[$autoload] class[$class]";
318*0Sstevel@tonic-gate    $autoload =~ s/.*:://;
319*0Sstevel@tonic-gate    if ($autoload =~ /^w/) {
320*0Sstevel@tonic-gate	if ($CPAN::META->has_inst('CPAN::WAIT')) {
321*0Sstevel@tonic-gate	    CPAN::WAIT->$autoload(@_);
322*0Sstevel@tonic-gate	} else {
323*0Sstevel@tonic-gate	    $CPAN::Frontend->mywarn(qq{
324*0Sstevel@tonic-gateCommands starting with "w" require CPAN::WAIT to be installed.
325*0Sstevel@tonic-gatePlease consider installing CPAN::WAIT to use the fulltext index.
326*0Sstevel@tonic-gateFor this you just need to type
327*0Sstevel@tonic-gate    install CPAN::WAIT
328*0Sstevel@tonic-gate});
329*0Sstevel@tonic-gate	}
330*0Sstevel@tonic-gate    } else {
331*0Sstevel@tonic-gate	$CPAN::Frontend->mywarn(qq{Unknown command '$autoload'. }.
332*0Sstevel@tonic-gate				qq{Type ? for help.
333*0Sstevel@tonic-gate});
334*0Sstevel@tonic-gate    }
335*0Sstevel@tonic-gate}
336*0Sstevel@tonic-gate
337*0Sstevel@tonic-gatepackage CPAN::Tarzip;
338*0Sstevel@tonic-gateuse vars qw($AUTOLOAD @ISA $BUGHUNTING);
339*0Sstevel@tonic-gate@CPAN::Tarzip::ISA = qw(CPAN::Debug);
340*0Sstevel@tonic-gate$BUGHUNTING = 0; # released code must have turned off
341*0Sstevel@tonic-gate
342*0Sstevel@tonic-gatepackage CPAN::Queue;
343*0Sstevel@tonic-gate
344*0Sstevel@tonic-gate# One use of the queue is to determine if we should or shouldn't
345*0Sstevel@tonic-gate# announce the availability of a new CPAN module
346*0Sstevel@tonic-gate
347*0Sstevel@tonic-gate# Now we try to use it for dependency tracking. For that to happen
348*0Sstevel@tonic-gate# we need to draw a dependency tree and do the leaves first. This can
349*0Sstevel@tonic-gate# easily be reached by running CPAN.pm recursively, but we don't want
350*0Sstevel@tonic-gate# to waste memory and run into deep recursion. So what we can do is
351*0Sstevel@tonic-gate# this:
352*0Sstevel@tonic-gate
353*0Sstevel@tonic-gate# CPAN::Queue is the package where the queue is maintained. Dependencies
354*0Sstevel@tonic-gate# often have high priority and must be brought to the head of the queue,
355*0Sstevel@tonic-gate# possibly by jumping the queue if they are already there. My first code
356*0Sstevel@tonic-gate# attempt tried to be extremely correct. Whenever a module needed
357*0Sstevel@tonic-gate# immediate treatment, I either unshifted it to the front of the queue,
358*0Sstevel@tonic-gate# or, if it was already in the queue, I spliced and let it bypass the
359*0Sstevel@tonic-gate# others. This became a too correct model that made it impossible to put
360*0Sstevel@tonic-gate# an item more than once into the queue. Why would you need that? Well,
361*0Sstevel@tonic-gate# you need temporary duplicates as the manager of the queue is a loop
362*0Sstevel@tonic-gate# that
363*0Sstevel@tonic-gate#
364*0Sstevel@tonic-gate#  (1) looks at the first item in the queue without shifting it off
365*0Sstevel@tonic-gate#
366*0Sstevel@tonic-gate#  (2) cares for the item
367*0Sstevel@tonic-gate#
368*0Sstevel@tonic-gate#  (3) removes the item from the queue, *even if its agenda failed and
369*0Sstevel@tonic-gate#      even if the item isn't the first in the queue anymore* (that way
370*0Sstevel@tonic-gate#      protecting against never ending queues)
371*0Sstevel@tonic-gate#
372*0Sstevel@tonic-gate# So if an item has prerequisites, the installation fails now, but we
373*0Sstevel@tonic-gate# want to retry later. That's easy if we have it twice in the queue.
374*0Sstevel@tonic-gate#
375*0Sstevel@tonic-gate# I also expect insane dependency situations where an item gets more
376*0Sstevel@tonic-gate# than two lives in the queue. Simplest example is triggered by 'install
377*0Sstevel@tonic-gate# Foo Foo Foo'. People make this kind of mistakes and I don't want to
378*0Sstevel@tonic-gate# get in the way. I wanted the queue manager to be a dumb servant, not
379*0Sstevel@tonic-gate# one that knows everything.
380*0Sstevel@tonic-gate#
381*0Sstevel@tonic-gate# Who would I tell in this model that the user wants to be asked before
382*0Sstevel@tonic-gate# processing? I can't attach that information to the module object,
383*0Sstevel@tonic-gate# because not modules are installed but distributions. So I'd have to
384*0Sstevel@tonic-gate# tell the distribution object that it should ask the user before
385*0Sstevel@tonic-gate# processing. Where would the question be triggered then? Most probably
386*0Sstevel@tonic-gate# in CPAN::Distribution::rematein.
387*0Sstevel@tonic-gate# Hope that makes sense, my head is a bit off:-) -- AK
388*0Sstevel@tonic-gate
389*0Sstevel@tonic-gateuse vars qw{ @All };
390*0Sstevel@tonic-gate
391*0Sstevel@tonic-gate# CPAN::Queue::new ;
392*0Sstevel@tonic-gatesub new {
393*0Sstevel@tonic-gate  my($class,$s) = @_;
394*0Sstevel@tonic-gate  my $self = bless { qmod => $s }, $class;
395*0Sstevel@tonic-gate  push @All, $self;
396*0Sstevel@tonic-gate  return $self;
397*0Sstevel@tonic-gate}
398*0Sstevel@tonic-gate
399*0Sstevel@tonic-gate# CPAN::Queue::first ;
400*0Sstevel@tonic-gatesub first {
401*0Sstevel@tonic-gate  my $obj = $All[0];
402*0Sstevel@tonic-gate  $obj->{qmod};
403*0Sstevel@tonic-gate}
404*0Sstevel@tonic-gate
405*0Sstevel@tonic-gate# CPAN::Queue::delete_first ;
406*0Sstevel@tonic-gatesub delete_first {
407*0Sstevel@tonic-gate  my($class,$what) = @_;
408*0Sstevel@tonic-gate  my $i;
409*0Sstevel@tonic-gate  for my $i (0..$#All) {
410*0Sstevel@tonic-gate    if (  $All[$i]->{qmod} eq $what ) {
411*0Sstevel@tonic-gate      splice @All, $i, 1;
412*0Sstevel@tonic-gate      return;
413*0Sstevel@tonic-gate    }
414*0Sstevel@tonic-gate  }
415*0Sstevel@tonic-gate}
416*0Sstevel@tonic-gate
417*0Sstevel@tonic-gate# CPAN::Queue::jumpqueue ;
418*0Sstevel@tonic-gatesub jumpqueue {
419*0Sstevel@tonic-gate    my $class = shift;
420*0Sstevel@tonic-gate    my @what = @_;
421*0Sstevel@tonic-gate    CPAN->debug(sprintf("before jumpqueue All[%s] what[%s]",
422*0Sstevel@tonic-gate                        join(",",map {$_->{qmod}} @All),
423*0Sstevel@tonic-gate                        join(",",@what)
424*0Sstevel@tonic-gate                       )) if $CPAN::DEBUG;
425*0Sstevel@tonic-gate  WHAT: for my $what (reverse @what) {
426*0Sstevel@tonic-gate        my $jumped = 0;
427*0Sstevel@tonic-gate        for (my $i=0; $i<$#All;$i++) { #prevent deep recursion
428*0Sstevel@tonic-gate            CPAN->debug("i[$All[$i]]what[$what]") if $CPAN::DEBUG;
429*0Sstevel@tonic-gate            if ($All[$i]->{qmod} eq $what){
430*0Sstevel@tonic-gate                $jumped++;
431*0Sstevel@tonic-gate                if ($jumped > 100) { # one's OK if e.g. just
432*0Sstevel@tonic-gate                                     # processing now; more are OK if
433*0Sstevel@tonic-gate                                     # user typed it several times
434*0Sstevel@tonic-gate                    $CPAN::Frontend->mywarn(
435*0Sstevel@tonic-gateqq{Object [$what] queued more than 100 times, ignoring}
436*0Sstevel@tonic-gate				 );
437*0Sstevel@tonic-gate                    next WHAT;
438*0Sstevel@tonic-gate                }
439*0Sstevel@tonic-gate            }
440*0Sstevel@tonic-gate        }
441*0Sstevel@tonic-gate        my $obj = bless { qmod => $what }, $class;
442*0Sstevel@tonic-gate        unshift @All, $obj;
443*0Sstevel@tonic-gate    }
444*0Sstevel@tonic-gate    CPAN->debug(sprintf("after jumpqueue All[%s] what[%s]",
445*0Sstevel@tonic-gate                        join(",",map {$_->{qmod}} @All),
446*0Sstevel@tonic-gate                        join(",",@what)
447*0Sstevel@tonic-gate                       )) if $CPAN::DEBUG;
448*0Sstevel@tonic-gate}
449*0Sstevel@tonic-gate
450*0Sstevel@tonic-gate# CPAN::Queue::exists ;
451*0Sstevel@tonic-gatesub exists {
452*0Sstevel@tonic-gate  my($self,$what) = @_;
453*0Sstevel@tonic-gate  my @all = map { $_->{qmod} } @All;
454*0Sstevel@tonic-gate  my $exists = grep { $_->{qmod} eq $what } @All;
455*0Sstevel@tonic-gate  # warn "in exists what[$what] all[@all] exists[$exists]";
456*0Sstevel@tonic-gate  $exists;
457*0Sstevel@tonic-gate}
458*0Sstevel@tonic-gate
459*0Sstevel@tonic-gate# CPAN::Queue::delete ;
460*0Sstevel@tonic-gatesub delete {
461*0Sstevel@tonic-gate  my($self,$mod) = @_;
462*0Sstevel@tonic-gate  @All = grep { $_->{qmod} ne $mod } @All;
463*0Sstevel@tonic-gate}
464*0Sstevel@tonic-gate
465*0Sstevel@tonic-gate# CPAN::Queue::nullify_queue ;
466*0Sstevel@tonic-gatesub nullify_queue {
467*0Sstevel@tonic-gate  @All = ();
468*0Sstevel@tonic-gate}
469*0Sstevel@tonic-gate
470*0Sstevel@tonic-gate
471*0Sstevel@tonic-gate
472*0Sstevel@tonic-gatepackage CPAN;
473*0Sstevel@tonic-gate
474*0Sstevel@tonic-gate$META ||= CPAN->new; # In case we re-eval ourselves we need the ||
475*0Sstevel@tonic-gate
476*0Sstevel@tonic-gate# from here on only subs.
477*0Sstevel@tonic-gate################################################################################
478*0Sstevel@tonic-gate
479*0Sstevel@tonic-gate#-> sub CPAN::all_objects ;
480*0Sstevel@tonic-gatesub all_objects {
481*0Sstevel@tonic-gate    my($mgr,$class) = @_;
482*0Sstevel@tonic-gate    CPAN::Config->load unless $CPAN::Config_loaded++;
483*0Sstevel@tonic-gate    CPAN->debug("mgr[$mgr] class[$class]") if $CPAN::DEBUG;
484*0Sstevel@tonic-gate    CPAN::Index->reload;
485*0Sstevel@tonic-gate    values %{ $META->{readwrite}{$class} }; # unsafe meta access, ok
486*0Sstevel@tonic-gate}
487*0Sstevel@tonic-gate*all = \&all_objects;
488*0Sstevel@tonic-gate
489*0Sstevel@tonic-gate# Called by shell, not in batch mode. In batch mode I see no risk in
490*0Sstevel@tonic-gate# having many processes updating something as installations are
491*0Sstevel@tonic-gate# continually checked at runtime. In shell mode I suspect it is
492*0Sstevel@tonic-gate# unintentional to open more than one shell at a time
493*0Sstevel@tonic-gate
494*0Sstevel@tonic-gate#-> sub CPAN::checklock ;
495*0Sstevel@tonic-gatesub checklock {
496*0Sstevel@tonic-gate    my($self) = @_;
497*0Sstevel@tonic-gate    my $lockfile = File::Spec->catfile($CPAN::Config->{cpan_home},".lock");
498*0Sstevel@tonic-gate    if (-f $lockfile && -M _ > 0) {
499*0Sstevel@tonic-gate	my $fh = FileHandle->new($lockfile) or
500*0Sstevel@tonic-gate            $CPAN::Frontend->mydie("Could not open $lockfile: $!");
501*0Sstevel@tonic-gate	my $otherpid  = <$fh>;
502*0Sstevel@tonic-gate	my $otherhost = <$fh>;
503*0Sstevel@tonic-gate	$fh->close;
504*0Sstevel@tonic-gate	if (defined $otherpid && $otherpid) {
505*0Sstevel@tonic-gate	    chomp $otherpid;
506*0Sstevel@tonic-gate        }
507*0Sstevel@tonic-gate	if (defined $otherhost && $otherhost) {
508*0Sstevel@tonic-gate	    chomp $otherhost;
509*0Sstevel@tonic-gate	}
510*0Sstevel@tonic-gate	my $thishost  = hostname();
511*0Sstevel@tonic-gate	if (defined $otherhost && defined $thishost &&
512*0Sstevel@tonic-gate	    $otherhost ne '' && $thishost ne '' &&
513*0Sstevel@tonic-gate	    $otherhost ne $thishost) {
514*0Sstevel@tonic-gate            $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile $lockfile\n".
515*0Sstevel@tonic-gate                                           "reports other host $otherhost and other process $otherpid.\n".
516*0Sstevel@tonic-gate                                           "Cannot proceed.\n"));
517*0Sstevel@tonic-gate	}
518*0Sstevel@tonic-gate	elsif (defined $otherpid && $otherpid) {
519*0Sstevel@tonic-gate	    return if $$ == $otherpid; # should never happen
520*0Sstevel@tonic-gate	    $CPAN::Frontend->mywarn(
521*0Sstevel@tonic-gate				    qq{
522*0Sstevel@tonic-gateThere seems to be running another CPAN process (pid $otherpid).  Contacting...
523*0Sstevel@tonic-gate});
524*0Sstevel@tonic-gate	    if (kill 0, $otherpid) {
525*0Sstevel@tonic-gate		$CPAN::Frontend->mydie(qq{Other job is running.
526*0Sstevel@tonic-gateYou may want to kill it and delete the lockfile, maybe. On UNIX try:
527*0Sstevel@tonic-gate    kill $otherpid
528*0Sstevel@tonic-gate    rm $lockfile
529*0Sstevel@tonic-gate});
530*0Sstevel@tonic-gate	    } elsif (-w $lockfile) {
531*0Sstevel@tonic-gate		my($ans) =
532*0Sstevel@tonic-gate		    ExtUtils::MakeMaker::prompt
533*0Sstevel@tonic-gate			(qq{Other job not responding. Shall I overwrite }.
534*0Sstevel@tonic-gate			 qq{the lockfile? (Y/N)},"y");
535*0Sstevel@tonic-gate		$CPAN::Frontend->myexit("Ok, bye\n")
536*0Sstevel@tonic-gate		    unless $ans =~ /^y/i;
537*0Sstevel@tonic-gate	    } else {
538*0Sstevel@tonic-gate		Carp::croak(
539*0Sstevel@tonic-gate			    qq{Lockfile $lockfile not writeable by you. }.
540*0Sstevel@tonic-gate			    qq{Cannot proceed.\n}.
541*0Sstevel@tonic-gate			    qq{    On UNIX try:\n}.
542*0Sstevel@tonic-gate			    qq{    rm $lockfile\n}.
543*0Sstevel@tonic-gate			    qq{  and then rerun us.\n}
544*0Sstevel@tonic-gate			   );
545*0Sstevel@tonic-gate	    }
546*0Sstevel@tonic-gate	} else {
547*0Sstevel@tonic-gate            $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile $lockfile\n".
548*0Sstevel@tonic-gate                                           "reports other process with ID ".
549*0Sstevel@tonic-gate                                           "$otherpid. Cannot proceed.\n"));
550*0Sstevel@tonic-gate        }
551*0Sstevel@tonic-gate    }
552*0Sstevel@tonic-gate    my $dotcpan = $CPAN::Config->{cpan_home};
553*0Sstevel@tonic-gate    eval { File::Path::mkpath($dotcpan);};
554*0Sstevel@tonic-gate    if ($@) {
555*0Sstevel@tonic-gate      # A special case at least for Jarkko.
556*0Sstevel@tonic-gate      my $firsterror = $@;
557*0Sstevel@tonic-gate      my $seconderror;
558*0Sstevel@tonic-gate      my $symlinkcpan;
559*0Sstevel@tonic-gate      if (-l $dotcpan) {
560*0Sstevel@tonic-gate	$symlinkcpan = readlink $dotcpan;
561*0Sstevel@tonic-gate	die "readlink $dotcpan failed: $!" unless defined $symlinkcpan;
562*0Sstevel@tonic-gate	eval { File::Path::mkpath($symlinkcpan); };
563*0Sstevel@tonic-gate	if ($@) {
564*0Sstevel@tonic-gate	  $seconderror = $@;
565*0Sstevel@tonic-gate	} else {
566*0Sstevel@tonic-gate	  $CPAN::Frontend->mywarn(qq{
567*0Sstevel@tonic-gateWorking directory $symlinkcpan created.
568*0Sstevel@tonic-gate});
569*0Sstevel@tonic-gate	}
570*0Sstevel@tonic-gate      }
571*0Sstevel@tonic-gate      unless (-d $dotcpan) {
572*0Sstevel@tonic-gate	my $diemess = qq{
573*0Sstevel@tonic-gateYour configuration suggests "$dotcpan" as your
574*0Sstevel@tonic-gateCPAN.pm working directory. I could not create this directory due
575*0Sstevel@tonic-gateto this error: $firsterror\n};
576*0Sstevel@tonic-gate	$diemess .= qq{
577*0Sstevel@tonic-gateAs "$dotcpan" is a symlink to "$symlinkcpan",
578*0Sstevel@tonic-gateI tried to create that, but I failed with this error: $seconderror
579*0Sstevel@tonic-gate} if $seconderror;
580*0Sstevel@tonic-gate	$diemess .= qq{
581*0Sstevel@tonic-gatePlease make sure the directory exists and is writable.
582*0Sstevel@tonic-gate};
583*0Sstevel@tonic-gate	$CPAN::Frontend->mydie($diemess);
584*0Sstevel@tonic-gate      }
585*0Sstevel@tonic-gate    }
586*0Sstevel@tonic-gate    my $fh;
587*0Sstevel@tonic-gate    unless ($fh = FileHandle->new(">$lockfile")) {
588*0Sstevel@tonic-gate	if ($! =~ /Permission/) {
589*0Sstevel@tonic-gate	    my $incc = $INC{'CPAN/Config.pm'};
590*0Sstevel@tonic-gate	    my $myincc = File::Spec->catfile($ENV{HOME},'.cpan','CPAN','MyConfig.pm');
591*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(qq{
592*0Sstevel@tonic-gate
593*0Sstevel@tonic-gateYour configuration suggests that CPAN.pm should use a working
594*0Sstevel@tonic-gatedirectory of
595*0Sstevel@tonic-gate    $CPAN::Config->{cpan_home}
596*0Sstevel@tonic-gateUnfortunately we could not create the lock file
597*0Sstevel@tonic-gate    $lockfile
598*0Sstevel@tonic-gatedue to permission problems.
599*0Sstevel@tonic-gate
600*0Sstevel@tonic-gatePlease make sure that the configuration variable
601*0Sstevel@tonic-gate    \$CPAN::Config->{cpan_home}
602*0Sstevel@tonic-gatepoints to a directory where you can write a .lock file. You can set
603*0Sstevel@tonic-gatethis variable in either
604*0Sstevel@tonic-gate    $incc
605*0Sstevel@tonic-gateor
606*0Sstevel@tonic-gate    $myincc
607*0Sstevel@tonic-gate
608*0Sstevel@tonic-gate});
609*0Sstevel@tonic-gate	}
610*0Sstevel@tonic-gate	$CPAN::Frontend->mydie("Could not open >$lockfile: $!");
611*0Sstevel@tonic-gate    }
612*0Sstevel@tonic-gate    $fh->print($$, "\n");
613*0Sstevel@tonic-gate    $fh->print(hostname(), "\n");
614*0Sstevel@tonic-gate    $self->{LOCK} = $lockfile;
615*0Sstevel@tonic-gate    $fh->close;
616*0Sstevel@tonic-gate    $SIG{TERM} = sub {
617*0Sstevel@tonic-gate      &cleanup;
618*0Sstevel@tonic-gate      $CPAN::Frontend->mydie("Got SIGTERM, leaving");
619*0Sstevel@tonic-gate    };
620*0Sstevel@tonic-gate    $SIG{INT} = sub {
621*0Sstevel@tonic-gate      # no blocks!!!
622*0Sstevel@tonic-gate      &cleanup if $Signal;
623*0Sstevel@tonic-gate      $CPAN::Frontend->mydie("Got another SIGINT") if $Signal;
624*0Sstevel@tonic-gate      print "Caught SIGINT\n";
625*0Sstevel@tonic-gate      $Signal++;
626*0Sstevel@tonic-gate    };
627*0Sstevel@tonic-gate
628*0Sstevel@tonic-gate#       From: Larry Wall <larry@wall.org>
629*0Sstevel@tonic-gate#       Subject: Re: deprecating SIGDIE
630*0Sstevel@tonic-gate#       To: perl5-porters@perl.org
631*0Sstevel@tonic-gate#       Date: Thu, 30 Sep 1999 14:58:40 -0700 (PDT)
632*0Sstevel@tonic-gate#
633*0Sstevel@tonic-gate#       The original intent of __DIE__ was only to allow you to substitute one
634*0Sstevel@tonic-gate#       kind of death for another on an application-wide basis without respect
635*0Sstevel@tonic-gate#       to whether you were in an eval or not.  As a global backstop, it should
636*0Sstevel@tonic-gate#       not be used any more lightly (or any more heavily :-) than class
637*0Sstevel@tonic-gate#       UNIVERSAL.  Any attempt to build a general exception model on it should
638*0Sstevel@tonic-gate#       be politely squashed.  Any bug that causes every eval {} to have to be
639*0Sstevel@tonic-gate#       modified should be not so politely squashed.
640*0Sstevel@tonic-gate#
641*0Sstevel@tonic-gate#       Those are my current opinions.  It is also my optinion that polite
642*0Sstevel@tonic-gate#       arguments degenerate to personal arguments far too frequently, and that
643*0Sstevel@tonic-gate#       when they do, it's because both people wanted it to, or at least didn't
644*0Sstevel@tonic-gate#       sufficiently want it not to.
645*0Sstevel@tonic-gate#
646*0Sstevel@tonic-gate#       Larry
647*0Sstevel@tonic-gate
648*0Sstevel@tonic-gate    # global backstop to cleanup if we should really die
649*0Sstevel@tonic-gate    $SIG{__DIE__} = \&cleanup;
650*0Sstevel@tonic-gate    $self->debug("Signal handler set.") if $CPAN::DEBUG;
651*0Sstevel@tonic-gate}
652*0Sstevel@tonic-gate
653*0Sstevel@tonic-gate#-> sub CPAN::DESTROY ;
654*0Sstevel@tonic-gatesub DESTROY {
655*0Sstevel@tonic-gate    &cleanup; # need an eval?
656*0Sstevel@tonic-gate}
657*0Sstevel@tonic-gate
658*0Sstevel@tonic-gate#-> sub CPAN::anycwd ;
659*0Sstevel@tonic-gatesub anycwd () {
660*0Sstevel@tonic-gate    my $getcwd;
661*0Sstevel@tonic-gate    $getcwd = $CPAN::Config->{'getcwd'} || 'cwd';
662*0Sstevel@tonic-gate    CPAN->$getcwd();
663*0Sstevel@tonic-gate}
664*0Sstevel@tonic-gate
665*0Sstevel@tonic-gate#-> sub CPAN::cwd ;
666*0Sstevel@tonic-gatesub cwd {Cwd::cwd();}
667*0Sstevel@tonic-gate
668*0Sstevel@tonic-gate#-> sub CPAN::getcwd ;
669*0Sstevel@tonic-gatesub getcwd {Cwd::getcwd();}
670*0Sstevel@tonic-gate
671*0Sstevel@tonic-gate#-> sub CPAN::exists ;
672*0Sstevel@tonic-gatesub exists {
673*0Sstevel@tonic-gate    my($mgr,$class,$id) = @_;
674*0Sstevel@tonic-gate    CPAN::Config->load unless $CPAN::Config_loaded++;
675*0Sstevel@tonic-gate    CPAN::Index->reload;
676*0Sstevel@tonic-gate    ### Carp::croak "exists called without class argument" unless $class;
677*0Sstevel@tonic-gate    $id ||= "";
678*0Sstevel@tonic-gate    exists $META->{readonly}{$class}{$id} or
679*0Sstevel@tonic-gate        exists $META->{readwrite}{$class}{$id}; # unsafe meta access, ok
680*0Sstevel@tonic-gate}
681*0Sstevel@tonic-gate
682*0Sstevel@tonic-gate#-> sub CPAN::delete ;
683*0Sstevel@tonic-gatesub delete {
684*0Sstevel@tonic-gate  my($mgr,$class,$id) = @_;
685*0Sstevel@tonic-gate  delete $META->{readonly}{$class}{$id}; # unsafe meta access, ok
686*0Sstevel@tonic-gate  delete $META->{readwrite}{$class}{$id}; # unsafe meta access, ok
687*0Sstevel@tonic-gate}
688*0Sstevel@tonic-gate
689*0Sstevel@tonic-gate#-> sub CPAN::has_usable
690*0Sstevel@tonic-gate# has_inst is sometimes too optimistic, we should replace it with this
691*0Sstevel@tonic-gate# has_usable whenever a case is given
692*0Sstevel@tonic-gatesub has_usable {
693*0Sstevel@tonic-gate    my($self,$mod,$message) = @_;
694*0Sstevel@tonic-gate    return 1 if $HAS_USABLE->{$mod};
695*0Sstevel@tonic-gate    my $has_inst = $self->has_inst($mod,$message);
696*0Sstevel@tonic-gate    return unless $has_inst;
697*0Sstevel@tonic-gate    my $usable;
698*0Sstevel@tonic-gate    $usable = {
699*0Sstevel@tonic-gate               LWP => [ # we frequently had "Can't locate object
700*0Sstevel@tonic-gate                        # method "new" via package "LWP::UserAgent" at
701*0Sstevel@tonic-gate                        # (eval 69) line 2006
702*0Sstevel@tonic-gate                       sub {require LWP},
703*0Sstevel@tonic-gate                       sub {require LWP::UserAgent},
704*0Sstevel@tonic-gate                       sub {require HTTP::Request},
705*0Sstevel@tonic-gate                       sub {require URI::URL},
706*0Sstevel@tonic-gate                      ],
707*0Sstevel@tonic-gate               Net::FTP => [
708*0Sstevel@tonic-gate                            sub {require Net::FTP},
709*0Sstevel@tonic-gate                            sub {require Net::Config},
710*0Sstevel@tonic-gate                           ]
711*0Sstevel@tonic-gate              };
712*0Sstevel@tonic-gate    if ($usable->{$mod}) {
713*0Sstevel@tonic-gate      for my $c (0..$#{$usable->{$mod}}) {
714*0Sstevel@tonic-gate        my $code = $usable->{$mod}[$c];
715*0Sstevel@tonic-gate        my $ret = eval { &$code() };
716*0Sstevel@tonic-gate        if ($@) {
717*0Sstevel@tonic-gate          warn "DEBUG: c[$c]\$\@[$@]ret[$ret]";
718*0Sstevel@tonic-gate          return;
719*0Sstevel@tonic-gate        }
720*0Sstevel@tonic-gate      }
721*0Sstevel@tonic-gate    }
722*0Sstevel@tonic-gate    return $HAS_USABLE->{$mod} = 1;
723*0Sstevel@tonic-gate}
724*0Sstevel@tonic-gate
725*0Sstevel@tonic-gate#-> sub CPAN::has_inst
726*0Sstevel@tonic-gatesub has_inst {
727*0Sstevel@tonic-gate    my($self,$mod,$message) = @_;
728*0Sstevel@tonic-gate    Carp::croak("CPAN->has_inst() called without an argument")
729*0Sstevel@tonic-gate	unless defined $mod;
730*0Sstevel@tonic-gate    if (defined $message && $message eq "no"
731*0Sstevel@tonic-gate        ||
732*0Sstevel@tonic-gate        exists $CPAN::META->{dontload_hash}{$mod} # unsafe meta access, ok
733*0Sstevel@tonic-gate        ||
734*0Sstevel@tonic-gate        exists $CPAN::Config->{dontload_hash}{$mod}
735*0Sstevel@tonic-gate       ) {
736*0Sstevel@tonic-gate      $CPAN::META->{dontload_hash}{$mod}||=1; # unsafe meta access, ok
737*0Sstevel@tonic-gate      return 0;
738*0Sstevel@tonic-gate    }
739*0Sstevel@tonic-gate    my $file = $mod;
740*0Sstevel@tonic-gate    my $obj;
741*0Sstevel@tonic-gate    $file =~ s|::|/|g;
742*0Sstevel@tonic-gate    $file =~ s|/|\\|g if $^O eq 'MSWin32';
743*0Sstevel@tonic-gate    $file .= ".pm";
744*0Sstevel@tonic-gate    if ($INC{$file}) {
745*0Sstevel@tonic-gate	# checking %INC is wrong, because $INC{LWP} may be true
746*0Sstevel@tonic-gate	# although $INC{"URI/URL.pm"} may have failed. But as
747*0Sstevel@tonic-gate	# I really want to say "bla loaded OK", I have to somehow
748*0Sstevel@tonic-gate	# cache results.
749*0Sstevel@tonic-gate	### warn "$file in %INC"; #debug
750*0Sstevel@tonic-gate	return 1;
751*0Sstevel@tonic-gate    } elsif (eval { require $file }) {
752*0Sstevel@tonic-gate	# eval is good: if we haven't yet read the database it's
753*0Sstevel@tonic-gate	# perfect and if we have installed the module in the meantime,
754*0Sstevel@tonic-gate	# it tries again. The second require is only a NOOP returning
755*0Sstevel@tonic-gate	# 1 if we had success, otherwise it's retrying
756*0Sstevel@tonic-gate
757*0Sstevel@tonic-gate	$CPAN::Frontend->myprint("CPAN: $mod loaded ok\n");
758*0Sstevel@tonic-gate	if ($mod eq "CPAN::WAIT") {
759*0Sstevel@tonic-gate	    push @CPAN::Shell::ISA, CPAN::WAIT;
760*0Sstevel@tonic-gate	}
761*0Sstevel@tonic-gate	return 1;
762*0Sstevel@tonic-gate    } elsif ($mod eq "Net::FTP") {
763*0Sstevel@tonic-gate	$CPAN::Frontend->mywarn(qq{
764*0Sstevel@tonic-gate  Please, install Net::FTP as soon as possible. CPAN.pm installs it for you
765*0Sstevel@tonic-gate  if you just type
766*0Sstevel@tonic-gate      install Bundle::libnet
767*0Sstevel@tonic-gate
768*0Sstevel@tonic-gate}) unless $Have_warned->{"Net::FTP"}++;
769*0Sstevel@tonic-gate	sleep 3;
770*0Sstevel@tonic-gate    } elsif ($mod eq "Digest::MD5"){
771*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(qq{
772*0Sstevel@tonic-gate  CPAN: MD5 security checks disabled because Digest::MD5 not installed.
773*0Sstevel@tonic-gate  Please consider installing the Digest::MD5 module.
774*0Sstevel@tonic-gate
775*0Sstevel@tonic-gate});
776*0Sstevel@tonic-gate	sleep 2;
777*0Sstevel@tonic-gate    } else {
778*0Sstevel@tonic-gate	delete $INC{$file}; # if it inc'd LWP but failed during, say, URI
779*0Sstevel@tonic-gate    }
780*0Sstevel@tonic-gate    return 0;
781*0Sstevel@tonic-gate}
782*0Sstevel@tonic-gate
783*0Sstevel@tonic-gate#-> sub CPAN::instance ;
784*0Sstevel@tonic-gatesub instance {
785*0Sstevel@tonic-gate    my($mgr,$class,$id) = @_;
786*0Sstevel@tonic-gate    CPAN::Index->reload;
787*0Sstevel@tonic-gate    $id ||= "";
788*0Sstevel@tonic-gate    # unsafe meta access, ok?
789*0Sstevel@tonic-gate    return $META->{readwrite}{$class}{$id} if exists $META->{readwrite}{$class}{$id};
790*0Sstevel@tonic-gate    $META->{readwrite}{$class}{$id} ||= $class->new(ID => $id);
791*0Sstevel@tonic-gate}
792*0Sstevel@tonic-gate
793*0Sstevel@tonic-gate#-> sub CPAN::new ;
794*0Sstevel@tonic-gatesub new {
795*0Sstevel@tonic-gate    bless {}, shift;
796*0Sstevel@tonic-gate}
797*0Sstevel@tonic-gate
798*0Sstevel@tonic-gate#-> sub CPAN::cleanup ;
799*0Sstevel@tonic-gatesub cleanup {
800*0Sstevel@tonic-gate  # warn "cleanup called with arg[@_] End[$End] Signal[$Signal]";
801*0Sstevel@tonic-gate  local $SIG{__DIE__} = '';
802*0Sstevel@tonic-gate  my($message) = @_;
803*0Sstevel@tonic-gate  my $i = 0;
804*0Sstevel@tonic-gate  my $ineval = 0;
805*0Sstevel@tonic-gate  my($subroutine);
806*0Sstevel@tonic-gate  while ((undef,undef,undef,$subroutine) = caller(++$i)) {
807*0Sstevel@tonic-gate      $ineval = 1, last if
808*0Sstevel@tonic-gate	  $subroutine eq '(eval)';
809*0Sstevel@tonic-gate  }
810*0Sstevel@tonic-gate  return if $ineval && !$End;
811*0Sstevel@tonic-gate  return unless defined $META->{LOCK};
812*0Sstevel@tonic-gate  return unless -f $META->{LOCK};
813*0Sstevel@tonic-gate  $META->savehist;
814*0Sstevel@tonic-gate  unlink $META->{LOCK};
815*0Sstevel@tonic-gate  # require Carp;
816*0Sstevel@tonic-gate  # Carp::cluck("DEBUGGING");
817*0Sstevel@tonic-gate  $CPAN::Frontend->mywarn("Lockfile removed.\n");
818*0Sstevel@tonic-gate}
819*0Sstevel@tonic-gate
820*0Sstevel@tonic-gate#-> sub CPAN::savehist
821*0Sstevel@tonic-gatesub savehist {
822*0Sstevel@tonic-gate    my($self) = @_;
823*0Sstevel@tonic-gate    my($histfile,$histsize);
824*0Sstevel@tonic-gate    unless ($histfile = $CPAN::Config->{'histfile'}){
825*0Sstevel@tonic-gate        $CPAN::Frontend->mywarn("No history written (no histfile specified).\n");
826*0Sstevel@tonic-gate        return;
827*0Sstevel@tonic-gate    }
828*0Sstevel@tonic-gate    $histsize = $CPAN::Config->{'histsize'} || 100;
829*0Sstevel@tonic-gate    if ($CPAN::term){
830*0Sstevel@tonic-gate        unless ($CPAN::term->can("GetHistory")) {
831*0Sstevel@tonic-gate            $CPAN::Frontend->mywarn("Terminal does not support GetHistory.\n");
832*0Sstevel@tonic-gate            return;
833*0Sstevel@tonic-gate        }
834*0Sstevel@tonic-gate    } else {
835*0Sstevel@tonic-gate        return;
836*0Sstevel@tonic-gate    }
837*0Sstevel@tonic-gate    my @h = $CPAN::term->GetHistory;
838*0Sstevel@tonic-gate    splice @h, 0, @h-$histsize if @h>$histsize;
839*0Sstevel@tonic-gate    my($fh) = FileHandle->new;
840*0Sstevel@tonic-gate    open $fh, ">$histfile" or $CPAN::Frontend->mydie("Couldn't open >$histfile: $!");
841*0Sstevel@tonic-gate    local $\ = local $, = "\n";
842*0Sstevel@tonic-gate    print $fh @h;
843*0Sstevel@tonic-gate    close $fh;
844*0Sstevel@tonic-gate}
845*0Sstevel@tonic-gate
846*0Sstevel@tonic-gatesub is_tested {
847*0Sstevel@tonic-gate    my($self,$what) = @_;
848*0Sstevel@tonic-gate    $self->{is_tested}{$what} = 1;
849*0Sstevel@tonic-gate}
850*0Sstevel@tonic-gate
851*0Sstevel@tonic-gatesub is_installed {
852*0Sstevel@tonic-gate    my($self,$what) = @_;
853*0Sstevel@tonic-gate    delete $self->{is_tested}{$what};
854*0Sstevel@tonic-gate}
855*0Sstevel@tonic-gate
856*0Sstevel@tonic-gatesub set_perl5lib {
857*0Sstevel@tonic-gate    my($self) = @_;
858*0Sstevel@tonic-gate    $self->{is_tested} ||= {};
859*0Sstevel@tonic-gate    return unless %{$self->{is_tested}};
860*0Sstevel@tonic-gate    my $env = $ENV{PERL5LIB};
861*0Sstevel@tonic-gate    $env = $ENV{PERLLIB} unless defined $env;
862*0Sstevel@tonic-gate    my @env;
863*0Sstevel@tonic-gate    push @env, $env if defined $env and length $env;
864*0Sstevel@tonic-gate    my @dirs = map {("$_/blib/arch", "$_/blib/lib")} keys %{$self->{is_tested}};
865*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Prepending @dirs to PERL5LIB.\n");
866*0Sstevel@tonic-gate    $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env;
867*0Sstevel@tonic-gate}
868*0Sstevel@tonic-gate
869*0Sstevel@tonic-gatepackage CPAN::CacheMgr;
870*0Sstevel@tonic-gate
871*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::as_string ;
872*0Sstevel@tonic-gatesub as_string {
873*0Sstevel@tonic-gate    eval { require Data::Dumper };
874*0Sstevel@tonic-gate    if ($@) {
875*0Sstevel@tonic-gate	return shift->SUPER::as_string;
876*0Sstevel@tonic-gate    } else {
877*0Sstevel@tonic-gate	return Data::Dumper::Dumper(shift);
878*0Sstevel@tonic-gate    }
879*0Sstevel@tonic-gate}
880*0Sstevel@tonic-gate
881*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::cachesize ;
882*0Sstevel@tonic-gatesub cachesize {
883*0Sstevel@tonic-gate    shift->{DU};
884*0Sstevel@tonic-gate}
885*0Sstevel@tonic-gate
886*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::tidyup ;
887*0Sstevel@tonic-gatesub tidyup {
888*0Sstevel@tonic-gate  my($self) = @_;
889*0Sstevel@tonic-gate  return unless -d $self->{ID};
890*0Sstevel@tonic-gate  while ($self->{DU} > $self->{'MAX'} ) {
891*0Sstevel@tonic-gate    my($toremove) = shift @{$self->{FIFO}};
892*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(sprintf(
893*0Sstevel@tonic-gate				     "Deleting from cache".
894*0Sstevel@tonic-gate				     ": $toremove (%.1f>%.1f MB)\n",
895*0Sstevel@tonic-gate				     $self->{DU}, $self->{'MAX'})
896*0Sstevel@tonic-gate			    );
897*0Sstevel@tonic-gate    return if $CPAN::Signal;
898*0Sstevel@tonic-gate    $self->force_clean_cache($toremove);
899*0Sstevel@tonic-gate    return if $CPAN::Signal;
900*0Sstevel@tonic-gate  }
901*0Sstevel@tonic-gate}
902*0Sstevel@tonic-gate
903*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::dir ;
904*0Sstevel@tonic-gatesub dir {
905*0Sstevel@tonic-gate    shift->{ID};
906*0Sstevel@tonic-gate}
907*0Sstevel@tonic-gate
908*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::entries ;
909*0Sstevel@tonic-gatesub entries {
910*0Sstevel@tonic-gate    my($self,$dir) = @_;
911*0Sstevel@tonic-gate    return unless defined $dir;
912*0Sstevel@tonic-gate    $self->debug("reading dir[$dir]") if $CPAN::DEBUG;
913*0Sstevel@tonic-gate    $dir ||= $self->{ID};
914*0Sstevel@tonic-gate    my($cwd) = CPAN::anycwd();
915*0Sstevel@tonic-gate    chdir $dir or Carp::croak("Can't chdir to $dir: $!");
916*0Sstevel@tonic-gate    my $dh = DirHandle->new(File::Spec->curdir)
917*0Sstevel@tonic-gate        or Carp::croak("Couldn't opendir $dir: $!");
918*0Sstevel@tonic-gate    my(@entries);
919*0Sstevel@tonic-gate    for ($dh->read) {
920*0Sstevel@tonic-gate	next if $_ eq "." || $_ eq "..";
921*0Sstevel@tonic-gate	if (-f $_) {
922*0Sstevel@tonic-gate	    push @entries, File::Spec->catfile($dir,$_);
923*0Sstevel@tonic-gate	} elsif (-d _) {
924*0Sstevel@tonic-gate	    push @entries, File::Spec->catdir($dir,$_);
925*0Sstevel@tonic-gate	} else {
926*0Sstevel@tonic-gate	    $CPAN::Frontend->mywarn("Warning: weird direntry in $dir: $_\n");
927*0Sstevel@tonic-gate	}
928*0Sstevel@tonic-gate    }
929*0Sstevel@tonic-gate    chdir $cwd or Carp::croak("Can't chdir to $cwd: $!");
930*0Sstevel@tonic-gate    sort { -M $b <=> -M $a} @entries;
931*0Sstevel@tonic-gate}
932*0Sstevel@tonic-gate
933*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::disk_usage ;
934*0Sstevel@tonic-gatesub disk_usage {
935*0Sstevel@tonic-gate    my($self,$dir) = @_;
936*0Sstevel@tonic-gate    return if exists $self->{SIZE}{$dir};
937*0Sstevel@tonic-gate    return if $CPAN::Signal;
938*0Sstevel@tonic-gate    my($Du) = 0;
939*0Sstevel@tonic-gate    find(
940*0Sstevel@tonic-gate	 sub {
941*0Sstevel@tonic-gate	   $File::Find::prune++ if $CPAN::Signal;
942*0Sstevel@tonic-gate	   return if -l $_;
943*0Sstevel@tonic-gate	   if ($^O eq 'MacOS') {
944*0Sstevel@tonic-gate	     require Mac::Files;
945*0Sstevel@tonic-gate	     my $cat  = Mac::Files::FSpGetCatInfo($_);
946*0Sstevel@tonic-gate	     $Du += $cat->ioFlLgLen() + $cat->ioFlRLgLen() if $cat;
947*0Sstevel@tonic-gate	   } else {
948*0Sstevel@tonic-gate	     $Du += (-s _);
949*0Sstevel@tonic-gate	   }
950*0Sstevel@tonic-gate	 },
951*0Sstevel@tonic-gate	 $dir
952*0Sstevel@tonic-gate	);
953*0Sstevel@tonic-gate    return if $CPAN::Signal;
954*0Sstevel@tonic-gate    $self->{SIZE}{$dir} = $Du/1024/1024;
955*0Sstevel@tonic-gate    push @{$self->{FIFO}}, $dir;
956*0Sstevel@tonic-gate    $self->debug("measured $dir is $Du") if $CPAN::DEBUG;
957*0Sstevel@tonic-gate    $self->{DU} += $Du/1024/1024;
958*0Sstevel@tonic-gate    $self->{DU};
959*0Sstevel@tonic-gate}
960*0Sstevel@tonic-gate
961*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::force_clean_cache ;
962*0Sstevel@tonic-gatesub force_clean_cache {
963*0Sstevel@tonic-gate    my($self,$dir) = @_;
964*0Sstevel@tonic-gate    return unless -e $dir;
965*0Sstevel@tonic-gate    $self->debug("have to rmtree $dir, will free $self->{SIZE}{$dir}")
966*0Sstevel@tonic-gate	if $CPAN::DEBUG;
967*0Sstevel@tonic-gate    File::Path::rmtree($dir);
968*0Sstevel@tonic-gate    $self->{DU} -= $self->{SIZE}{$dir};
969*0Sstevel@tonic-gate    delete $self->{SIZE}{$dir};
970*0Sstevel@tonic-gate}
971*0Sstevel@tonic-gate
972*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::new ;
973*0Sstevel@tonic-gatesub new {
974*0Sstevel@tonic-gate    my $class = shift;
975*0Sstevel@tonic-gate    my $time = time;
976*0Sstevel@tonic-gate    my($debug,$t2);
977*0Sstevel@tonic-gate    $debug = "";
978*0Sstevel@tonic-gate    my $self = {
979*0Sstevel@tonic-gate		ID => $CPAN::Config->{'build_dir'},
980*0Sstevel@tonic-gate		MAX => $CPAN::Config->{'build_cache'},
981*0Sstevel@tonic-gate		SCAN => $CPAN::Config->{'scan_cache'} || 'atstart',
982*0Sstevel@tonic-gate		DU => 0
983*0Sstevel@tonic-gate	       };
984*0Sstevel@tonic-gate    File::Path::mkpath($self->{ID});
985*0Sstevel@tonic-gate    my $dh = DirHandle->new($self->{ID});
986*0Sstevel@tonic-gate    bless $self, $class;
987*0Sstevel@tonic-gate    $self->scan_cache;
988*0Sstevel@tonic-gate    $t2 = time;
989*0Sstevel@tonic-gate    $debug .= "timing of CacheMgr->new: ".($t2 - $time);
990*0Sstevel@tonic-gate    $time = $t2;
991*0Sstevel@tonic-gate    CPAN->debug($debug) if $CPAN::DEBUG;
992*0Sstevel@tonic-gate    $self;
993*0Sstevel@tonic-gate}
994*0Sstevel@tonic-gate
995*0Sstevel@tonic-gate#-> sub CPAN::CacheMgr::scan_cache ;
996*0Sstevel@tonic-gatesub scan_cache {
997*0Sstevel@tonic-gate    my $self = shift;
998*0Sstevel@tonic-gate    return if $self->{SCAN} eq 'never';
999*0Sstevel@tonic-gate    $CPAN::Frontend->mydie("Unknown scan_cache argument: $self->{SCAN}")
1000*0Sstevel@tonic-gate	unless $self->{SCAN} eq 'atstart';
1001*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(
1002*0Sstevel@tonic-gate			     sprintf("Scanning cache %s for sizes\n",
1003*0Sstevel@tonic-gate				     $self->{ID}));
1004*0Sstevel@tonic-gate    my $e;
1005*0Sstevel@tonic-gate    for $e ($self->entries($self->{ID})) {
1006*0Sstevel@tonic-gate	next if $e eq ".." || $e eq ".";
1007*0Sstevel@tonic-gate	$self->disk_usage($e);
1008*0Sstevel@tonic-gate	return if $CPAN::Signal;
1009*0Sstevel@tonic-gate    }
1010*0Sstevel@tonic-gate    $self->tidyup;
1011*0Sstevel@tonic-gate}
1012*0Sstevel@tonic-gate
1013*0Sstevel@tonic-gatepackage CPAN::Debug;
1014*0Sstevel@tonic-gate
1015*0Sstevel@tonic-gate#-> sub CPAN::Debug::debug ;
1016*0Sstevel@tonic-gatesub debug {
1017*0Sstevel@tonic-gate    my($self,$arg) = @_;
1018*0Sstevel@tonic-gate    my($caller,$func,$line,@rest) = caller(1); # caller(0) eg
1019*0Sstevel@tonic-gate                                               # Complete, caller(1)
1020*0Sstevel@tonic-gate                                               # eg readline
1021*0Sstevel@tonic-gate    ($caller) = caller(0);
1022*0Sstevel@tonic-gate    $caller =~ s/.*:://;
1023*0Sstevel@tonic-gate    $arg = "" unless defined $arg;
1024*0Sstevel@tonic-gate    my $rest = join "|", map { defined $_ ? $_ : "UNDEF" } @rest;
1025*0Sstevel@tonic-gate    if ($CPAN::DEBUG{$caller} & $CPAN::DEBUG){
1026*0Sstevel@tonic-gate	if ($arg and ref $arg) {
1027*0Sstevel@tonic-gate	    eval { require Data::Dumper };
1028*0Sstevel@tonic-gate	    if ($@) {
1029*0Sstevel@tonic-gate		$CPAN::Frontend->myprint($arg->as_string);
1030*0Sstevel@tonic-gate	    } else {
1031*0Sstevel@tonic-gate		$CPAN::Frontend->myprint(Data::Dumper::Dumper($arg));
1032*0Sstevel@tonic-gate	    }
1033*0Sstevel@tonic-gate	} else {
1034*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("Debug($caller:$func,$line,[$rest]): $arg\n");
1035*0Sstevel@tonic-gate	}
1036*0Sstevel@tonic-gate    }
1037*0Sstevel@tonic-gate}
1038*0Sstevel@tonic-gate
1039*0Sstevel@tonic-gatepackage CPAN::Config;
1040*0Sstevel@tonic-gate
1041*0Sstevel@tonic-gate#-> sub CPAN::Config::edit ;
1042*0Sstevel@tonic-gate# returns true on successful action
1043*0Sstevel@tonic-gatesub edit {
1044*0Sstevel@tonic-gate    my($self,@args) = @_;
1045*0Sstevel@tonic-gate    return unless @args;
1046*0Sstevel@tonic-gate    CPAN->debug("self[$self]args[".join(" | ",@args)."]");
1047*0Sstevel@tonic-gate    my($o,$str,$func,$args,$key_exists);
1048*0Sstevel@tonic-gate    $o = shift @args;
1049*0Sstevel@tonic-gate    if($can{$o}) {
1050*0Sstevel@tonic-gate	$self->$o(@args);
1051*0Sstevel@tonic-gate	return 1;
1052*0Sstevel@tonic-gate    } else {
1053*0Sstevel@tonic-gate        CPAN->debug("o[$o]") if $CPAN::DEBUG;
1054*0Sstevel@tonic-gate	if ($o =~ /list$/) {
1055*0Sstevel@tonic-gate	    $func = shift @args;
1056*0Sstevel@tonic-gate	    $func ||= "";
1057*0Sstevel@tonic-gate            CPAN->debug("func[$func]") if $CPAN::DEBUG;
1058*0Sstevel@tonic-gate            my $changed;
1059*0Sstevel@tonic-gate	    # Let's avoid eval, it's easier to comprehend without.
1060*0Sstevel@tonic-gate	    if ($func eq "push") {
1061*0Sstevel@tonic-gate		push @{$CPAN::Config->{$o}}, @args;
1062*0Sstevel@tonic-gate                $changed = 1;
1063*0Sstevel@tonic-gate	    } elsif ($func eq "pop") {
1064*0Sstevel@tonic-gate		pop @{$CPAN::Config->{$o}};
1065*0Sstevel@tonic-gate                $changed = 1;
1066*0Sstevel@tonic-gate	    } elsif ($func eq "shift") {
1067*0Sstevel@tonic-gate		shift @{$CPAN::Config->{$o}};
1068*0Sstevel@tonic-gate                $changed = 1;
1069*0Sstevel@tonic-gate	    } elsif ($func eq "unshift") {
1070*0Sstevel@tonic-gate		unshift @{$CPAN::Config->{$o}}, @args;
1071*0Sstevel@tonic-gate                $changed = 1;
1072*0Sstevel@tonic-gate	    } elsif ($func eq "splice") {
1073*0Sstevel@tonic-gate		splice @{$CPAN::Config->{$o}}, @args;
1074*0Sstevel@tonic-gate                $changed = 1;
1075*0Sstevel@tonic-gate	    } elsif (@args) {
1076*0Sstevel@tonic-gate		$CPAN::Config->{$o} = [@args];
1077*0Sstevel@tonic-gate                $changed = 1;
1078*0Sstevel@tonic-gate	    } else {
1079*0Sstevel@tonic-gate                $self->prettyprint($o);
1080*0Sstevel@tonic-gate	    }
1081*0Sstevel@tonic-gate            if ($o eq "urllist" && $changed) {
1082*0Sstevel@tonic-gate                # reset the cached values
1083*0Sstevel@tonic-gate                undef $CPAN::FTP::Thesite;
1084*0Sstevel@tonic-gate                undef $CPAN::FTP::Themethod;
1085*0Sstevel@tonic-gate            }
1086*0Sstevel@tonic-gate            return $changed;
1087*0Sstevel@tonic-gate	} else {
1088*0Sstevel@tonic-gate	    $CPAN::Config->{$o} = $args[0] if defined $args[0];
1089*0Sstevel@tonic-gate	    $self->prettyprint($o);
1090*0Sstevel@tonic-gate	}
1091*0Sstevel@tonic-gate    }
1092*0Sstevel@tonic-gate}
1093*0Sstevel@tonic-gate
1094*0Sstevel@tonic-gatesub prettyprint {
1095*0Sstevel@tonic-gate  my($self,$k) = @_;
1096*0Sstevel@tonic-gate  my $v = $CPAN::Config->{$k};
1097*0Sstevel@tonic-gate  if (ref $v) {
1098*0Sstevel@tonic-gate    my(@report) = ref $v eq "ARRAY" ?
1099*0Sstevel@tonic-gate        @$v :
1100*0Sstevel@tonic-gate            map { sprintf("   %-18s => %s\n",
1101*0Sstevel@tonic-gate                          $_,
1102*0Sstevel@tonic-gate                          defined $v->{$_} ? $v->{$_} : "UNDEFINED"
1103*0Sstevel@tonic-gate                         )} keys %$v;
1104*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(
1105*0Sstevel@tonic-gate                             join(
1106*0Sstevel@tonic-gate                                  "",
1107*0Sstevel@tonic-gate                                  sprintf(
1108*0Sstevel@tonic-gate                                          "    %-18s\n",
1109*0Sstevel@tonic-gate                                          $k
1110*0Sstevel@tonic-gate                                         ),
1111*0Sstevel@tonic-gate                                  map {"\t$_\n"} @report
1112*0Sstevel@tonic-gate                                 )
1113*0Sstevel@tonic-gate                            );
1114*0Sstevel@tonic-gate  } elsif (defined $v) {
1115*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(sprintf "    %-18s %s\n", $k, $v);
1116*0Sstevel@tonic-gate  } else {
1117*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(sprintf "    %-18s %s\n", $k, "UNDEFINED");
1118*0Sstevel@tonic-gate  }
1119*0Sstevel@tonic-gate}
1120*0Sstevel@tonic-gate
1121*0Sstevel@tonic-gate#-> sub CPAN::Config::commit ;
1122*0Sstevel@tonic-gatesub commit {
1123*0Sstevel@tonic-gate    my($self,$configpm) = @_;
1124*0Sstevel@tonic-gate    unless (defined $configpm){
1125*0Sstevel@tonic-gate	$configpm ||= $INC{"CPAN/MyConfig.pm"};
1126*0Sstevel@tonic-gate	$configpm ||= $INC{"CPAN/Config.pm"};
1127*0Sstevel@tonic-gate	$configpm || Carp::confess(q{
1128*0Sstevel@tonic-gateCPAN::Config::commit called without an argument.
1129*0Sstevel@tonic-gatePlease specify a filename where to save the configuration or try
1130*0Sstevel@tonic-gate"o conf init" to have an interactive course through configing.
1131*0Sstevel@tonic-gate});
1132*0Sstevel@tonic-gate    }
1133*0Sstevel@tonic-gate    my($mode);
1134*0Sstevel@tonic-gate    if (-f $configpm) {
1135*0Sstevel@tonic-gate	$mode = (stat $configpm)[2];
1136*0Sstevel@tonic-gate	if ($mode && ! -w _) {
1137*0Sstevel@tonic-gate	    Carp::confess("$configpm is not writable");
1138*0Sstevel@tonic-gate	}
1139*0Sstevel@tonic-gate    }
1140*0Sstevel@tonic-gate
1141*0Sstevel@tonic-gate    my $msg;
1142*0Sstevel@tonic-gate    $msg = <<EOF unless $configpm =~ /MyConfig/;
1143*0Sstevel@tonic-gate
1144*0Sstevel@tonic-gate# This is CPAN.pm's systemwide configuration file. This file provides
1145*0Sstevel@tonic-gate# defaults for users, and the values can be changed in a per-user
1146*0Sstevel@tonic-gate# configuration file. The user-config file is being looked for as
1147*0Sstevel@tonic-gate# ~/.cpan/CPAN/MyConfig.pm.
1148*0Sstevel@tonic-gate
1149*0Sstevel@tonic-gateEOF
1150*0Sstevel@tonic-gate    $msg ||= "\n";
1151*0Sstevel@tonic-gate    my($fh) = FileHandle->new;
1152*0Sstevel@tonic-gate    rename $configpm, "$configpm~" if -f $configpm;
1153*0Sstevel@tonic-gate    open $fh, ">$configpm" or
1154*0Sstevel@tonic-gate        $CPAN::Frontend->mydie("Couldn't open >$configpm: $!");
1155*0Sstevel@tonic-gate    $fh->print(qq[$msg\$CPAN::Config = \{\n]);
1156*0Sstevel@tonic-gate    foreach (sort keys %$CPAN::Config) {
1157*0Sstevel@tonic-gate	$fh->print(
1158*0Sstevel@tonic-gate		   "  '$_' => ",
1159*0Sstevel@tonic-gate		   ExtUtils::MakeMaker::neatvalue($CPAN::Config->{$_}),
1160*0Sstevel@tonic-gate		   ",\n"
1161*0Sstevel@tonic-gate		  );
1162*0Sstevel@tonic-gate    }
1163*0Sstevel@tonic-gate
1164*0Sstevel@tonic-gate    $fh->print("};\n1;\n__END__\n");
1165*0Sstevel@tonic-gate    close $fh;
1166*0Sstevel@tonic-gate
1167*0Sstevel@tonic-gate    #$mode = 0444 | ( $mode & 0111 ? 0111 : 0 );
1168*0Sstevel@tonic-gate    #chmod $mode, $configpm;
1169*0Sstevel@tonic-gate###why was that so?    $self->defaults;
1170*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("commit: wrote $configpm\n");
1171*0Sstevel@tonic-gate    1;
1172*0Sstevel@tonic-gate}
1173*0Sstevel@tonic-gate
1174*0Sstevel@tonic-gate*default = \&defaults;
1175*0Sstevel@tonic-gate#-> sub CPAN::Config::defaults ;
1176*0Sstevel@tonic-gatesub defaults {
1177*0Sstevel@tonic-gate    my($self) = @_;
1178*0Sstevel@tonic-gate    $self->unload;
1179*0Sstevel@tonic-gate    $self->load;
1180*0Sstevel@tonic-gate    1;
1181*0Sstevel@tonic-gate}
1182*0Sstevel@tonic-gate
1183*0Sstevel@tonic-gatesub init {
1184*0Sstevel@tonic-gate    my($self) = @_;
1185*0Sstevel@tonic-gate    undef $CPAN::Config->{'inhibit_startup_message'}; # lazy trick to
1186*0Sstevel@tonic-gate                                                      # have the least
1187*0Sstevel@tonic-gate                                                      # important
1188*0Sstevel@tonic-gate                                                      # variable
1189*0Sstevel@tonic-gate                                                      # undefined
1190*0Sstevel@tonic-gate    $self->load;
1191*0Sstevel@tonic-gate    1;
1192*0Sstevel@tonic-gate}
1193*0Sstevel@tonic-gate
1194*0Sstevel@tonic-gate# This is a piece of repeated code that is abstracted here for
1195*0Sstevel@tonic-gate# maintainability.  RMB
1196*0Sstevel@tonic-gate#
1197*0Sstevel@tonic-gatesub _configpmtest {
1198*0Sstevel@tonic-gate    my($configpmdir, $configpmtest) = @_;
1199*0Sstevel@tonic-gate    if (-w $configpmtest) {
1200*0Sstevel@tonic-gate        return $configpmtest;
1201*0Sstevel@tonic-gate    } elsif (-w $configpmdir) {
1202*0Sstevel@tonic-gate        #_#_# following code dumped core on me with 5.003_11, a.k.
1203*0Sstevel@tonic-gate        my $configpm_bak = "$configpmtest.bak";
1204*0Sstevel@tonic-gate        unlink $configpm_bak if -f $configpm_bak;
1205*0Sstevel@tonic-gate        if( -f $configpmtest ) {
1206*0Sstevel@tonic-gate            if( rename $configpmtest, $configpm_bak ) {
1207*0Sstevel@tonic-gate                $CPAN::Frontend->mywarn(<<END)
1208*0Sstevel@tonic-gateOld configuration file $configpmtest
1209*0Sstevel@tonic-gate    moved to $configpm_bak
1210*0Sstevel@tonic-gateEND
1211*0Sstevel@tonic-gate	    }
1212*0Sstevel@tonic-gate	}
1213*0Sstevel@tonic-gate	my $fh = FileHandle->new;
1214*0Sstevel@tonic-gate	if ($fh->open(">$configpmtest")) {
1215*0Sstevel@tonic-gate	    $fh->print("1;\n");
1216*0Sstevel@tonic-gate	    return $configpmtest;
1217*0Sstevel@tonic-gate	} else {
1218*0Sstevel@tonic-gate	    # Should never happen
1219*0Sstevel@tonic-gate	    Carp::confess("Cannot open >$configpmtest");
1220*0Sstevel@tonic-gate	}
1221*0Sstevel@tonic-gate    } else { return }
1222*0Sstevel@tonic-gate}
1223*0Sstevel@tonic-gate
1224*0Sstevel@tonic-gate#-> sub CPAN::Config::load ;
1225*0Sstevel@tonic-gatesub load {
1226*0Sstevel@tonic-gate    my($self) = shift;
1227*0Sstevel@tonic-gate    my(@miss);
1228*0Sstevel@tonic-gate    use Carp;
1229*0Sstevel@tonic-gate    eval {require CPAN::Config;};       # We eval because of some
1230*0Sstevel@tonic-gate                                        # MakeMaker problems
1231*0Sstevel@tonic-gate    unless ($dot_cpan++){
1232*0Sstevel@tonic-gate      unshift @INC, File::Spec->catdir($ENV{HOME},".cpan");
1233*0Sstevel@tonic-gate      eval {require CPAN::MyConfig;};   # where you can override
1234*0Sstevel@tonic-gate                                        # system wide settings
1235*0Sstevel@tonic-gate      shift @INC;
1236*0Sstevel@tonic-gate    }
1237*0Sstevel@tonic-gate    return unless @miss = $self->missing_config_data;
1238*0Sstevel@tonic-gate
1239*0Sstevel@tonic-gate    require CPAN::FirstTime;
1240*0Sstevel@tonic-gate    my($configpm,$fh,$redo,$theycalled);
1241*0Sstevel@tonic-gate    $redo ||= "";
1242*0Sstevel@tonic-gate    $theycalled++ if @miss==1 && $miss[0] eq 'inhibit_startup_message';
1243*0Sstevel@tonic-gate    if (defined $INC{"CPAN/Config.pm"} && -w $INC{"CPAN/Config.pm"}) {
1244*0Sstevel@tonic-gate	$configpm = $INC{"CPAN/Config.pm"};
1245*0Sstevel@tonic-gate	$redo++;
1246*0Sstevel@tonic-gate    } elsif (defined $INC{"CPAN/MyConfig.pm"} && -w $INC{"CPAN/MyConfig.pm"}) {
1247*0Sstevel@tonic-gate	$configpm = $INC{"CPAN/MyConfig.pm"};
1248*0Sstevel@tonic-gate	$redo++;
1249*0Sstevel@tonic-gate    } else {
1250*0Sstevel@tonic-gate	my($path_to_cpan) = File::Basename::dirname($INC{"CPAN.pm"});
1251*0Sstevel@tonic-gate	my($configpmdir) = File::Spec->catdir($path_to_cpan,"CPAN");
1252*0Sstevel@tonic-gate	my($configpmtest) = File::Spec->catfile($configpmdir,"Config.pm");
1253*0Sstevel@tonic-gate	if (-d $configpmdir or File::Path::mkpath($configpmdir)) {
1254*0Sstevel@tonic-gate	    $configpm = _configpmtest($configpmdir,$configpmtest);
1255*0Sstevel@tonic-gate	}
1256*0Sstevel@tonic-gate	unless ($configpm) {
1257*0Sstevel@tonic-gate	    $configpmdir = File::Spec->catdir($ENV{HOME},".cpan","CPAN");
1258*0Sstevel@tonic-gate	    File::Path::mkpath($configpmdir);
1259*0Sstevel@tonic-gate	    $configpmtest = File::Spec->catfile($configpmdir,"MyConfig.pm");
1260*0Sstevel@tonic-gate	    $configpm = _configpmtest($configpmdir,$configpmtest);
1261*0Sstevel@tonic-gate	    unless ($configpm) {
1262*0Sstevel@tonic-gate		Carp::confess(qq{WARNING: CPAN.pm is unable to }.
1263*0Sstevel@tonic-gate			      qq{create a configuration file.});
1264*0Sstevel@tonic-gate	    }
1265*0Sstevel@tonic-gate	}
1266*0Sstevel@tonic-gate    }
1267*0Sstevel@tonic-gate    local($") = ", ";
1268*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(<<END) if $redo && ! $theycalled;
1269*0Sstevel@tonic-gateWe have to reconfigure CPAN.pm due to following uninitialized parameters:
1270*0Sstevel@tonic-gate
1271*0Sstevel@tonic-gate@miss
1272*0Sstevel@tonic-gateEND
1273*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(qq{
1274*0Sstevel@tonic-gate$configpm initialized.
1275*0Sstevel@tonic-gate});
1276*0Sstevel@tonic-gate    sleep 2;
1277*0Sstevel@tonic-gate    CPAN::FirstTime::init($configpm);
1278*0Sstevel@tonic-gate}
1279*0Sstevel@tonic-gate
1280*0Sstevel@tonic-gate#-> sub CPAN::Config::missing_config_data ;
1281*0Sstevel@tonic-gatesub missing_config_data {
1282*0Sstevel@tonic-gate    my(@miss);
1283*0Sstevel@tonic-gate    for (
1284*0Sstevel@tonic-gate         "cpan_home", "keep_source_where", "build_dir", "build_cache",
1285*0Sstevel@tonic-gate         "scan_cache", "index_expire", "gzip", "tar", "unzip", "make",
1286*0Sstevel@tonic-gate         "pager",
1287*0Sstevel@tonic-gate         "makepl_arg", "make_arg", "make_install_arg", "urllist",
1288*0Sstevel@tonic-gate         "inhibit_startup_message", "ftp_proxy", "http_proxy", "no_proxy",
1289*0Sstevel@tonic-gate         "prerequisites_policy",
1290*0Sstevel@tonic-gate         "cache_metadata",
1291*0Sstevel@tonic-gate        ) {
1292*0Sstevel@tonic-gate	push @miss, $_ unless defined $CPAN::Config->{$_};
1293*0Sstevel@tonic-gate    }
1294*0Sstevel@tonic-gate    return @miss;
1295*0Sstevel@tonic-gate}
1296*0Sstevel@tonic-gate
1297*0Sstevel@tonic-gate#-> sub CPAN::Config::unload ;
1298*0Sstevel@tonic-gatesub unload {
1299*0Sstevel@tonic-gate    delete $INC{'CPAN/MyConfig.pm'};
1300*0Sstevel@tonic-gate    delete $INC{'CPAN/Config.pm'};
1301*0Sstevel@tonic-gate}
1302*0Sstevel@tonic-gate
1303*0Sstevel@tonic-gate#-> sub CPAN::Config::help ;
1304*0Sstevel@tonic-gatesub help {
1305*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(q[
1306*0Sstevel@tonic-gateKnown options:
1307*0Sstevel@tonic-gate  defaults  reload default config values from disk
1308*0Sstevel@tonic-gate  commit    commit session changes to disk
1309*0Sstevel@tonic-gate  init      go through a dialog to set all parameters
1310*0Sstevel@tonic-gate
1311*0Sstevel@tonic-gateYou may edit key values in the follow fashion (the "o" is a literal
1312*0Sstevel@tonic-gateletter o):
1313*0Sstevel@tonic-gate
1314*0Sstevel@tonic-gate  o conf build_cache 15
1315*0Sstevel@tonic-gate
1316*0Sstevel@tonic-gate  o conf build_dir "/foo/bar"
1317*0Sstevel@tonic-gate
1318*0Sstevel@tonic-gate  o conf urllist shift
1319*0Sstevel@tonic-gate
1320*0Sstevel@tonic-gate  o conf urllist unshift ftp://ftp.foo.bar/
1321*0Sstevel@tonic-gate
1322*0Sstevel@tonic-gate]);
1323*0Sstevel@tonic-gate    undef; #don't reprint CPAN::Config
1324*0Sstevel@tonic-gate}
1325*0Sstevel@tonic-gate
1326*0Sstevel@tonic-gate#-> sub CPAN::Config::cpl ;
1327*0Sstevel@tonic-gatesub cpl {
1328*0Sstevel@tonic-gate    my($word,$line,$pos) = @_;
1329*0Sstevel@tonic-gate    $word ||= "";
1330*0Sstevel@tonic-gate    CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG;
1331*0Sstevel@tonic-gate    my(@words) = split " ", substr($line,0,$pos+1);
1332*0Sstevel@tonic-gate    if (
1333*0Sstevel@tonic-gate	defined($words[2])
1334*0Sstevel@tonic-gate	and
1335*0Sstevel@tonic-gate	(
1336*0Sstevel@tonic-gate	 $words[2] =~ /list$/ && @words == 3
1337*0Sstevel@tonic-gate	 ||
1338*0Sstevel@tonic-gate	 $words[2] =~ /list$/ && @words == 4 && length($word)
1339*0Sstevel@tonic-gate	)
1340*0Sstevel@tonic-gate       ) {
1341*0Sstevel@tonic-gate	return grep /^\Q$word\E/, qw(splice shift unshift pop push);
1342*0Sstevel@tonic-gate    } elsif (@words >= 4) {
1343*0Sstevel@tonic-gate	return ();
1344*0Sstevel@tonic-gate    }
1345*0Sstevel@tonic-gate    my(@o_conf) = (keys %CPAN::Config::can, keys %$CPAN::Config);
1346*0Sstevel@tonic-gate    return grep /^\Q$word\E/, @o_conf;
1347*0Sstevel@tonic-gate}
1348*0Sstevel@tonic-gate
1349*0Sstevel@tonic-gatepackage CPAN::Shell;
1350*0Sstevel@tonic-gate
1351*0Sstevel@tonic-gate#-> sub CPAN::Shell::h ;
1352*0Sstevel@tonic-gatesub h {
1353*0Sstevel@tonic-gate    my($class,$about) = @_;
1354*0Sstevel@tonic-gate    if (defined $about) {
1355*0Sstevel@tonic-gate	$CPAN::Frontend->myprint("Detailed help not yet implemented\n");
1356*0Sstevel@tonic-gate    } else {
1357*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(q{
1358*0Sstevel@tonic-gateDisplay Information
1359*0Sstevel@tonic-gate command  argument          description
1360*0Sstevel@tonic-gate a,b,d,m  WORD or /REGEXP/  about authors, bundles, distributions, modules
1361*0Sstevel@tonic-gate i        WORD or /REGEXP/  about anything of above
1362*0Sstevel@tonic-gate r        NONE              reinstall recommendations
1363*0Sstevel@tonic-gate ls       AUTHOR            about files in the author's directory
1364*0Sstevel@tonic-gate
1365*0Sstevel@tonic-gateDownload, Test, Make, Install...
1366*0Sstevel@tonic-gate get                        download
1367*0Sstevel@tonic-gate make                       make (implies get)
1368*0Sstevel@tonic-gate test      MODULES,         make test (implies make)
1369*0Sstevel@tonic-gate install   DISTS, BUNDLES   make install (implies test)
1370*0Sstevel@tonic-gate clean                      make clean
1371*0Sstevel@tonic-gate look                       open subshell in these dists' directories
1372*0Sstevel@tonic-gate readme                     display these dists' README files
1373*0Sstevel@tonic-gate
1374*0Sstevel@tonic-gateOther
1375*0Sstevel@tonic-gate h,?           display this menu       ! perl-code   eval a perl command
1376*0Sstevel@tonic-gate o conf [opt]  set and query options   q             quit the cpan shell
1377*0Sstevel@tonic-gate reload cpan   load CPAN.pm again      reload index  load newer indices
1378*0Sstevel@tonic-gate autobundle    Snapshot                force cmd     unconditionally do cmd});
1379*0Sstevel@tonic-gate    }
1380*0Sstevel@tonic-gate}
1381*0Sstevel@tonic-gate
1382*0Sstevel@tonic-gate*help = \&h;
1383*0Sstevel@tonic-gate
1384*0Sstevel@tonic-gate#-> sub CPAN::Shell::a ;
1385*0Sstevel@tonic-gatesub a {
1386*0Sstevel@tonic-gate  my($self,@arg) = @_;
1387*0Sstevel@tonic-gate  # authors are always UPPERCASE
1388*0Sstevel@tonic-gate  for (@arg) {
1389*0Sstevel@tonic-gate    $_ = uc $_ unless /=/;
1390*0Sstevel@tonic-gate  }
1391*0Sstevel@tonic-gate  $CPAN::Frontend->myprint($self->format_result('Author',@arg));
1392*0Sstevel@tonic-gate}
1393*0Sstevel@tonic-gate
1394*0Sstevel@tonic-gate#-> sub CPAN::Shell::ls ;
1395*0Sstevel@tonic-gatesub ls      {
1396*0Sstevel@tonic-gate    my($self,@arg) = @_;
1397*0Sstevel@tonic-gate    my @accept;
1398*0Sstevel@tonic-gate    for (@arg) {
1399*0Sstevel@tonic-gate        unless (/^[A-Z\-]+$/i) {
1400*0Sstevel@tonic-gate            $CPAN::Frontend->mywarn("ls command rejects argument $_: not an author\n");
1401*0Sstevel@tonic-gate            next;
1402*0Sstevel@tonic-gate        }
1403*0Sstevel@tonic-gate        push @accept, uc $_;
1404*0Sstevel@tonic-gate    }
1405*0Sstevel@tonic-gate    for my $a (@accept){
1406*0Sstevel@tonic-gate        my $author = $self->expand('Author',$a) or die "No author found for $a";
1407*0Sstevel@tonic-gate        $author->ls;
1408*0Sstevel@tonic-gate    }
1409*0Sstevel@tonic-gate}
1410*0Sstevel@tonic-gate
1411*0Sstevel@tonic-gate#-> sub CPAN::Shell::local_bundles ;
1412*0Sstevel@tonic-gatesub local_bundles {
1413*0Sstevel@tonic-gate    my($self,@which) = @_;
1414*0Sstevel@tonic-gate    my($incdir,$bdir,$dh);
1415*0Sstevel@tonic-gate    foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) {
1416*0Sstevel@tonic-gate        my @bbase = "Bundle";
1417*0Sstevel@tonic-gate        while (my $bbase = shift @bbase) {
1418*0Sstevel@tonic-gate            $bdir = File::Spec->catdir($incdir,split /::/, $bbase);
1419*0Sstevel@tonic-gate            CPAN->debug("bdir[$bdir]\@bbase[@bbase]") if $CPAN::DEBUG;
1420*0Sstevel@tonic-gate            if ($dh = DirHandle->new($bdir)) { # may fail
1421*0Sstevel@tonic-gate                my($entry);
1422*0Sstevel@tonic-gate                for $entry ($dh->read) {
1423*0Sstevel@tonic-gate                    next if $entry =~ /^\./;
1424*0Sstevel@tonic-gate                    if (-d File::Spec->catdir($bdir,$entry)){
1425*0Sstevel@tonic-gate                        push @bbase, "$bbase\::$entry";
1426*0Sstevel@tonic-gate                    } else {
1427*0Sstevel@tonic-gate                        next unless $entry =~ s/\.pm(?!\n)\Z//;
1428*0Sstevel@tonic-gate                        $CPAN::META->instance('CPAN::Bundle',"$bbase\::$entry");
1429*0Sstevel@tonic-gate                    }
1430*0Sstevel@tonic-gate                }
1431*0Sstevel@tonic-gate            }
1432*0Sstevel@tonic-gate        }
1433*0Sstevel@tonic-gate    }
1434*0Sstevel@tonic-gate}
1435*0Sstevel@tonic-gate
1436*0Sstevel@tonic-gate#-> sub CPAN::Shell::b ;
1437*0Sstevel@tonic-gatesub b {
1438*0Sstevel@tonic-gate    my($self,@which) = @_;
1439*0Sstevel@tonic-gate    CPAN->debug("which[@which]") if $CPAN::DEBUG;
1440*0Sstevel@tonic-gate    $self->local_bundles;
1441*0Sstevel@tonic-gate    $CPAN::Frontend->myprint($self->format_result('Bundle',@which));
1442*0Sstevel@tonic-gate}
1443*0Sstevel@tonic-gate
1444*0Sstevel@tonic-gate#-> sub CPAN::Shell::d ;
1445*0Sstevel@tonic-gatesub d { $CPAN::Frontend->myprint(shift->format_result('Distribution',@_));}
1446*0Sstevel@tonic-gate
1447*0Sstevel@tonic-gate#-> sub CPAN::Shell::m ;
1448*0Sstevel@tonic-gatesub m { # emacs confused here }; sub mimimimimi { # emacs in sync here
1449*0Sstevel@tonic-gate    my $self = shift;
1450*0Sstevel@tonic-gate    $CPAN::Frontend->myprint($self->format_result('Module',@_));
1451*0Sstevel@tonic-gate}
1452*0Sstevel@tonic-gate
1453*0Sstevel@tonic-gate#-> sub CPAN::Shell::i ;
1454*0Sstevel@tonic-gatesub i {
1455*0Sstevel@tonic-gate    my($self) = shift;
1456*0Sstevel@tonic-gate    my(@args) = @_;
1457*0Sstevel@tonic-gate    my(@type,$type,@m);
1458*0Sstevel@tonic-gate    @type = qw/Author Bundle Distribution Module/;
1459*0Sstevel@tonic-gate    @args = '/./' unless @args;
1460*0Sstevel@tonic-gate    my(@result);
1461*0Sstevel@tonic-gate    for $type (@type) {
1462*0Sstevel@tonic-gate	push @result, $self->expand($type,@args);
1463*0Sstevel@tonic-gate    }
1464*0Sstevel@tonic-gate    my $result = @result == 1 ?
1465*0Sstevel@tonic-gate	$result[0]->as_string :
1466*0Sstevel@tonic-gate            @result == 0 ?
1467*0Sstevel@tonic-gate                "No objects found of any type for argument @args\n" :
1468*0Sstevel@tonic-gate                    join("",
1469*0Sstevel@tonic-gate                         (map {$_->as_glimpse} @result),
1470*0Sstevel@tonic-gate                         scalar @result, " items found\n",
1471*0Sstevel@tonic-gate                        );
1472*0Sstevel@tonic-gate    $CPAN::Frontend->myprint($result);
1473*0Sstevel@tonic-gate}
1474*0Sstevel@tonic-gate
1475*0Sstevel@tonic-gate#-> sub CPAN::Shell::o ;
1476*0Sstevel@tonic-gate
1477*0Sstevel@tonic-gate# CPAN::Shell::o and CPAN::Config::edit are closely related. 'o conf'
1478*0Sstevel@tonic-gate# should have been called set and 'o debug' maybe 'set debug'
1479*0Sstevel@tonic-gatesub o {
1480*0Sstevel@tonic-gate    my($self,$o_type,@o_what) = @_;
1481*0Sstevel@tonic-gate    $o_type ||= "";
1482*0Sstevel@tonic-gate    CPAN->debug("o_type[$o_type] o_what[".join(" | ",@o_what)."]\n");
1483*0Sstevel@tonic-gate    if ($o_type eq 'conf') {
1484*0Sstevel@tonic-gate	shift @o_what if @o_what && $o_what[0] eq 'help';
1485*0Sstevel@tonic-gate	if (!@o_what) { # print all things, "o conf"
1486*0Sstevel@tonic-gate	    my($k,$v);
1487*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("CPAN::Config options");
1488*0Sstevel@tonic-gate	    if (exists $INC{'CPAN/Config.pm'}) {
1489*0Sstevel@tonic-gate	      $CPAN::Frontend->myprint(" from $INC{'CPAN/Config.pm'}");
1490*0Sstevel@tonic-gate	    }
1491*0Sstevel@tonic-gate	    if (exists $INC{'CPAN/MyConfig.pm'}) {
1492*0Sstevel@tonic-gate	      $CPAN::Frontend->myprint(" and $INC{'CPAN/MyConfig.pm'}");
1493*0Sstevel@tonic-gate	    }
1494*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(":\n");
1495*0Sstevel@tonic-gate	    for $k (sort keys %CPAN::Config::can) {
1496*0Sstevel@tonic-gate		$v = $CPAN::Config::can{$k};
1497*0Sstevel@tonic-gate		$CPAN::Frontend->myprint(sprintf "    %-18s %s\n", $k, $v);
1498*0Sstevel@tonic-gate	    }
1499*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("\n");
1500*0Sstevel@tonic-gate	    for $k (sort keys %$CPAN::Config) {
1501*0Sstevel@tonic-gate                CPAN::Config->prettyprint($k);
1502*0Sstevel@tonic-gate	    }
1503*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("\n");
1504*0Sstevel@tonic-gate	} elsif (!CPAN::Config->edit(@o_what)) {
1505*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(qq{Type 'o conf' to view configuration }.
1506*0Sstevel@tonic-gate                                     qq{edit options\n\n});
1507*0Sstevel@tonic-gate	}
1508*0Sstevel@tonic-gate    } elsif ($o_type eq 'debug') {
1509*0Sstevel@tonic-gate	my(%valid);
1510*0Sstevel@tonic-gate	@o_what = () if defined $o_what[0] && $o_what[0] =~ /help/i;
1511*0Sstevel@tonic-gate	if (@o_what) {
1512*0Sstevel@tonic-gate	    while (@o_what) {
1513*0Sstevel@tonic-gate		my($what) = shift @o_what;
1514*0Sstevel@tonic-gate                if ($what =~ s/^-// && exists $CPAN::DEBUG{$what}) {
1515*0Sstevel@tonic-gate                    $CPAN::DEBUG &= $CPAN::DEBUG ^ $CPAN::DEBUG{$what};
1516*0Sstevel@tonic-gate                    next;
1517*0Sstevel@tonic-gate                }
1518*0Sstevel@tonic-gate		if ( exists $CPAN::DEBUG{$what} ) {
1519*0Sstevel@tonic-gate		    $CPAN::DEBUG |= $CPAN::DEBUG{$what};
1520*0Sstevel@tonic-gate		} elsif ($what =~ /^\d/) {
1521*0Sstevel@tonic-gate		    $CPAN::DEBUG = $what;
1522*0Sstevel@tonic-gate		} elsif (lc $what eq 'all') {
1523*0Sstevel@tonic-gate		    my($max) = 0;
1524*0Sstevel@tonic-gate		    for (values %CPAN::DEBUG) {
1525*0Sstevel@tonic-gate			$max += $_;
1526*0Sstevel@tonic-gate		    }
1527*0Sstevel@tonic-gate		    $CPAN::DEBUG = $max;
1528*0Sstevel@tonic-gate		} else {
1529*0Sstevel@tonic-gate		    my($known) = 0;
1530*0Sstevel@tonic-gate		    for (keys %CPAN::DEBUG) {
1531*0Sstevel@tonic-gate			next unless lc($_) eq lc($what);
1532*0Sstevel@tonic-gate			$CPAN::DEBUG |= $CPAN::DEBUG{$_};
1533*0Sstevel@tonic-gate			$known = 1;
1534*0Sstevel@tonic-gate		    }
1535*0Sstevel@tonic-gate		    $CPAN::Frontend->myprint("unknown argument [$what]\n")
1536*0Sstevel@tonic-gate			unless $known;
1537*0Sstevel@tonic-gate		}
1538*0Sstevel@tonic-gate	    }
1539*0Sstevel@tonic-gate	} else {
1540*0Sstevel@tonic-gate	  my $raw = "Valid options for debug are ".
1541*0Sstevel@tonic-gate	      join(", ",sort(keys %CPAN::DEBUG), 'all').
1542*0Sstevel@tonic-gate		  qq{ or a number. Completion works on the options. }.
1543*0Sstevel@tonic-gate		      qq{Case is ignored.};
1544*0Sstevel@tonic-gate	  require Text::Wrap;
1545*0Sstevel@tonic-gate	  $CPAN::Frontend->myprint(Text::Wrap::fill("","",$raw));
1546*0Sstevel@tonic-gate	  $CPAN::Frontend->myprint("\n\n");
1547*0Sstevel@tonic-gate	}
1548*0Sstevel@tonic-gate	if ($CPAN::DEBUG) {
1549*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("Options set for debugging:\n");
1550*0Sstevel@tonic-gate	    my($k,$v);
1551*0Sstevel@tonic-gate	    for $k (sort {$CPAN::DEBUG{$a} <=> $CPAN::DEBUG{$b}} keys %CPAN::DEBUG) {
1552*0Sstevel@tonic-gate		$v = $CPAN::DEBUG{$k};
1553*0Sstevel@tonic-gate		$CPAN::Frontend->myprint(sprintf "    %-14s(%s)\n", $k, $v)
1554*0Sstevel@tonic-gate                    if $v & $CPAN::DEBUG;
1555*0Sstevel@tonic-gate	    }
1556*0Sstevel@tonic-gate	} else {
1557*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("Debugging turned off completely.\n");
1558*0Sstevel@tonic-gate	}
1559*0Sstevel@tonic-gate    } else {
1560*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(qq{
1561*0Sstevel@tonic-gateKnown options:
1562*0Sstevel@tonic-gate  conf    set or get configuration variables
1563*0Sstevel@tonic-gate  debug   set or get debugging options
1564*0Sstevel@tonic-gate});
1565*0Sstevel@tonic-gate    }
1566*0Sstevel@tonic-gate}
1567*0Sstevel@tonic-gate
1568*0Sstevel@tonic-gatesub paintdots_onreload {
1569*0Sstevel@tonic-gate    my($ref) = shift;
1570*0Sstevel@tonic-gate    sub {
1571*0Sstevel@tonic-gate	if ( $_[0] =~ /[Ss]ubroutine ([\w:]+) redefined/ ) {
1572*0Sstevel@tonic-gate	    my($subr) = $1;
1573*0Sstevel@tonic-gate	    ++$$ref;
1574*0Sstevel@tonic-gate	    local($|) = 1;
1575*0Sstevel@tonic-gate	    # $CPAN::Frontend->myprint(".($subr)");
1576*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(".");
1577*0Sstevel@tonic-gate	    return;
1578*0Sstevel@tonic-gate	}
1579*0Sstevel@tonic-gate	warn @_;
1580*0Sstevel@tonic-gate    };
1581*0Sstevel@tonic-gate}
1582*0Sstevel@tonic-gate
1583*0Sstevel@tonic-gate#-> sub CPAN::Shell::reload ;
1584*0Sstevel@tonic-gatesub reload {
1585*0Sstevel@tonic-gate    my($self,$command,@arg) = @_;
1586*0Sstevel@tonic-gate    $command ||= "";
1587*0Sstevel@tonic-gate    $self->debug("self[$self]command[$command]arg[@arg]") if $CPAN::DEBUG;
1588*0Sstevel@tonic-gate    if ($command =~ /cpan/i) {
1589*0Sstevel@tonic-gate        for my $f (qw(CPAN.pm CPAN/FirstTime.pm)) {
1590*0Sstevel@tonic-gate            next unless $INC{$f};
1591*0Sstevel@tonic-gate            CPAN->debug("reloading the whole $f") if $CPAN::DEBUG;
1592*0Sstevel@tonic-gate            my $fh = FileHandle->new($INC{$f});
1593*0Sstevel@tonic-gate            local($/);
1594*0Sstevel@tonic-gate            my $redef = 0;
1595*0Sstevel@tonic-gate            local($SIG{__WARN__}) = paintdots_onreload(\$redef);
1596*0Sstevel@tonic-gate            eval <$fh>;
1597*0Sstevel@tonic-gate            warn $@ if $@;
1598*0Sstevel@tonic-gate            $CPAN::Frontend->myprint("\n$redef subroutines redefined\n");
1599*0Sstevel@tonic-gate        }
1600*0Sstevel@tonic-gate    } elsif ($command =~ /index/) {
1601*0Sstevel@tonic-gate      CPAN::Index->force_reload;
1602*0Sstevel@tonic-gate    } else {
1603*0Sstevel@tonic-gate      $CPAN::Frontend->myprint(qq{cpan     re-evals the CPAN.pm file
1604*0Sstevel@tonic-gateindex    re-reads the index files\n});
1605*0Sstevel@tonic-gate    }
1606*0Sstevel@tonic-gate}
1607*0Sstevel@tonic-gate
1608*0Sstevel@tonic-gate#-> sub CPAN::Shell::_binary_extensions ;
1609*0Sstevel@tonic-gatesub _binary_extensions {
1610*0Sstevel@tonic-gate    my($self) = shift @_;
1611*0Sstevel@tonic-gate    my(@result,$module,%seen,%need,$headerdone);
1612*0Sstevel@tonic-gate    for $module ($self->expand('Module','/./')) {
1613*0Sstevel@tonic-gate	my $file  = $module->cpan_file;
1614*0Sstevel@tonic-gate	next if $file eq "N/A";
1615*0Sstevel@tonic-gate	next if $file =~ /^Contact Author/;
1616*0Sstevel@tonic-gate        my $dist = $CPAN::META->instance('CPAN::Distribution',$file);
1617*0Sstevel@tonic-gate	next if $dist->isa_perl;
1618*0Sstevel@tonic-gate	next unless $module->xs_file;
1619*0Sstevel@tonic-gate	local($|) = 1;
1620*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(".");
1621*0Sstevel@tonic-gate	push @result, $module;
1622*0Sstevel@tonic-gate    }
1623*0Sstevel@tonic-gate#    print join " | ", @result;
1624*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("\n");
1625*0Sstevel@tonic-gate    return @result;
1626*0Sstevel@tonic-gate}
1627*0Sstevel@tonic-gate
1628*0Sstevel@tonic-gate#-> sub CPAN::Shell::recompile ;
1629*0Sstevel@tonic-gatesub recompile {
1630*0Sstevel@tonic-gate    my($self) = shift @_;
1631*0Sstevel@tonic-gate    my($module,@module,$cpan_file,%dist);
1632*0Sstevel@tonic-gate    @module = $self->_binary_extensions();
1633*0Sstevel@tonic-gate    for $module (@module){  # we force now and compile later, so we
1634*0Sstevel@tonic-gate                            # don't do it twice
1635*0Sstevel@tonic-gate	$cpan_file = $module->cpan_file;
1636*0Sstevel@tonic-gate	my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file);
1637*0Sstevel@tonic-gate	$pack->force;
1638*0Sstevel@tonic-gate	$dist{$cpan_file}++;
1639*0Sstevel@tonic-gate    }
1640*0Sstevel@tonic-gate    for $cpan_file (sort keys %dist) {
1641*0Sstevel@tonic-gate	$CPAN::Frontend->myprint("  CPAN: Recompiling $cpan_file\n\n");
1642*0Sstevel@tonic-gate	my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file);
1643*0Sstevel@tonic-gate	$pack->install;
1644*0Sstevel@tonic-gate	$CPAN::Signal = 0; # it's tempting to reset Signal, so we can
1645*0Sstevel@tonic-gate                           # stop a package from recompiling,
1646*0Sstevel@tonic-gate                           # e.g. IO-1.12 when we have perl5.003_10
1647*0Sstevel@tonic-gate    }
1648*0Sstevel@tonic-gate}
1649*0Sstevel@tonic-gate
1650*0Sstevel@tonic-gate#-> sub CPAN::Shell::_u_r_common ;
1651*0Sstevel@tonic-gatesub _u_r_common {
1652*0Sstevel@tonic-gate    my($self) = shift @_;
1653*0Sstevel@tonic-gate    my($what) = shift @_;
1654*0Sstevel@tonic-gate    CPAN->debug("self[$self] what[$what] args[@_]") if $CPAN::DEBUG;
1655*0Sstevel@tonic-gate    Carp::croak "Usage: \$obj->_u_r_common(a|r|u)" unless
1656*0Sstevel@tonic-gate          $what && $what =~ /^[aru]$/;
1657*0Sstevel@tonic-gate    my(@args) = @_;
1658*0Sstevel@tonic-gate    @args = '/./' unless @args;
1659*0Sstevel@tonic-gate    my(@result,$module,%seen,%need,$headerdone,
1660*0Sstevel@tonic-gate       $version_undefs,$version_zeroes);
1661*0Sstevel@tonic-gate    $version_undefs = $version_zeroes = 0;
1662*0Sstevel@tonic-gate    my $sprintf = "%s%-25s%s %9s %9s  %s\n";
1663*0Sstevel@tonic-gate    my @expand = $self->expand('Module',@args);
1664*0Sstevel@tonic-gate    my $expand = scalar @expand;
1665*0Sstevel@tonic-gate    if (0) { # Looks like noise to me, was very useful for debugging
1666*0Sstevel@tonic-gate             # for metadata cache
1667*0Sstevel@tonic-gate        $CPAN::Frontend->myprint(sprintf "%d matches in the database\n", $expand);
1668*0Sstevel@tonic-gate    }
1669*0Sstevel@tonic-gate    for $module (@expand) {
1670*0Sstevel@tonic-gate	my $file  = $module->cpan_file;
1671*0Sstevel@tonic-gate	next unless defined $file; # ??
1672*0Sstevel@tonic-gate	my($latest) = $module->cpan_version;
1673*0Sstevel@tonic-gate	my($inst_file) = $module->inst_file;
1674*0Sstevel@tonic-gate	my($have);
1675*0Sstevel@tonic-gate	return if $CPAN::Signal;
1676*0Sstevel@tonic-gate	if ($inst_file){
1677*0Sstevel@tonic-gate	    if ($what eq "a") {
1678*0Sstevel@tonic-gate		$have = $module->inst_version;
1679*0Sstevel@tonic-gate	    } elsif ($what eq "r") {
1680*0Sstevel@tonic-gate		$have = $module->inst_version;
1681*0Sstevel@tonic-gate		local($^W) = 0;
1682*0Sstevel@tonic-gate		if ($have eq "undef"){
1683*0Sstevel@tonic-gate		    $version_undefs++;
1684*0Sstevel@tonic-gate		} elsif ($have == 0){
1685*0Sstevel@tonic-gate		    $version_zeroes++;
1686*0Sstevel@tonic-gate		}
1687*0Sstevel@tonic-gate		next unless CPAN::Version->vgt($latest, $have);
1688*0Sstevel@tonic-gate# to be pedantic we should probably say:
1689*0Sstevel@tonic-gate#    && !($have eq "undef" && $latest ne "undef" && $latest gt "");
1690*0Sstevel@tonic-gate# to catch the case where CPAN has a version 0 and we have a version undef
1691*0Sstevel@tonic-gate	    } elsif ($what eq "u") {
1692*0Sstevel@tonic-gate		next;
1693*0Sstevel@tonic-gate	    }
1694*0Sstevel@tonic-gate	} else {
1695*0Sstevel@tonic-gate	    if ($what eq "a") {
1696*0Sstevel@tonic-gate		next;
1697*0Sstevel@tonic-gate	    } elsif ($what eq "r") {
1698*0Sstevel@tonic-gate		next;
1699*0Sstevel@tonic-gate	    } elsif ($what eq "u") {
1700*0Sstevel@tonic-gate		$have = "-";
1701*0Sstevel@tonic-gate	    }
1702*0Sstevel@tonic-gate	}
1703*0Sstevel@tonic-gate	return if $CPAN::Signal; # this is sometimes lengthy
1704*0Sstevel@tonic-gate	$seen{$file} ||= 0;
1705*0Sstevel@tonic-gate	if ($what eq "a") {
1706*0Sstevel@tonic-gate	    push @result, sprintf "%s %s\n", $module->id, $have;
1707*0Sstevel@tonic-gate	} elsif ($what eq "r") {
1708*0Sstevel@tonic-gate	    push @result, $module->id;
1709*0Sstevel@tonic-gate	    next if $seen{$file}++;
1710*0Sstevel@tonic-gate	} elsif ($what eq "u") {
1711*0Sstevel@tonic-gate	    push @result, $module->id;
1712*0Sstevel@tonic-gate	    next if $seen{$file}++;
1713*0Sstevel@tonic-gate	    next if $file =~ /^Contact/;
1714*0Sstevel@tonic-gate	}
1715*0Sstevel@tonic-gate	unless ($headerdone++){
1716*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("\n");
1717*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(sprintf(
1718*0Sstevel@tonic-gate                                             $sprintf,
1719*0Sstevel@tonic-gate                                             "",
1720*0Sstevel@tonic-gate                                             "Package namespace",
1721*0Sstevel@tonic-gate                                             "",
1722*0Sstevel@tonic-gate                                             "installed",
1723*0Sstevel@tonic-gate                                             "latest",
1724*0Sstevel@tonic-gate                                             "in CPAN file"
1725*0Sstevel@tonic-gate                                            ));
1726*0Sstevel@tonic-gate	}
1727*0Sstevel@tonic-gate        my $color_on = "";
1728*0Sstevel@tonic-gate        my $color_off = "";
1729*0Sstevel@tonic-gate        if (
1730*0Sstevel@tonic-gate            $COLOR_REGISTERED
1731*0Sstevel@tonic-gate            &&
1732*0Sstevel@tonic-gate            $CPAN::META->has_inst("Term::ANSIColor")
1733*0Sstevel@tonic-gate            &&
1734*0Sstevel@tonic-gate            $module->{RO}{description}
1735*0Sstevel@tonic-gate           ) {
1736*0Sstevel@tonic-gate            $color_on = Term::ANSIColor::color("green");
1737*0Sstevel@tonic-gate            $color_off = Term::ANSIColor::color("reset");
1738*0Sstevel@tonic-gate        }
1739*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(sprintf $sprintf,
1740*0Sstevel@tonic-gate                                 $color_on,
1741*0Sstevel@tonic-gate                                 $module->id,
1742*0Sstevel@tonic-gate                                 $color_off,
1743*0Sstevel@tonic-gate                                 $have,
1744*0Sstevel@tonic-gate                                 $latest,
1745*0Sstevel@tonic-gate                                 $file);
1746*0Sstevel@tonic-gate	$need{$module->id}++;
1747*0Sstevel@tonic-gate    }
1748*0Sstevel@tonic-gate    unless (%need) {
1749*0Sstevel@tonic-gate	if ($what eq "u") {
1750*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("No modules found for @args\n");
1751*0Sstevel@tonic-gate	} elsif ($what eq "r") {
1752*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("All modules are up to date for @args\n");
1753*0Sstevel@tonic-gate	}
1754*0Sstevel@tonic-gate    }
1755*0Sstevel@tonic-gate    if ($what eq "r") {
1756*0Sstevel@tonic-gate	if ($version_zeroes) {
1757*0Sstevel@tonic-gate	    my $s_has = $version_zeroes > 1 ? "s have" : " has";
1758*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(qq{$version_zeroes installed module$s_has }.
1759*0Sstevel@tonic-gate		qq{a version number of 0\n});
1760*0Sstevel@tonic-gate	}
1761*0Sstevel@tonic-gate	if ($version_undefs) {
1762*0Sstevel@tonic-gate	    my $s_has = $version_undefs > 1 ? "s have" : " has";
1763*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(qq{$version_undefs installed module$s_has no }.
1764*0Sstevel@tonic-gate		qq{parseable version number\n});
1765*0Sstevel@tonic-gate	}
1766*0Sstevel@tonic-gate    }
1767*0Sstevel@tonic-gate    @result;
1768*0Sstevel@tonic-gate}
1769*0Sstevel@tonic-gate
1770*0Sstevel@tonic-gate#-> sub CPAN::Shell::r ;
1771*0Sstevel@tonic-gatesub r {
1772*0Sstevel@tonic-gate    shift->_u_r_common("r",@_);
1773*0Sstevel@tonic-gate}
1774*0Sstevel@tonic-gate
1775*0Sstevel@tonic-gate#-> sub CPAN::Shell::u ;
1776*0Sstevel@tonic-gatesub u {
1777*0Sstevel@tonic-gate    shift->_u_r_common("u",@_);
1778*0Sstevel@tonic-gate}
1779*0Sstevel@tonic-gate
1780*0Sstevel@tonic-gate#-> sub CPAN::Shell::autobundle ;
1781*0Sstevel@tonic-gatesub autobundle {
1782*0Sstevel@tonic-gate    my($self) = shift;
1783*0Sstevel@tonic-gate    CPAN::Config->load unless $CPAN::Config_loaded++;
1784*0Sstevel@tonic-gate    my(@bundle) = $self->_u_r_common("a",@_);
1785*0Sstevel@tonic-gate    my($todir) = File::Spec->catdir($CPAN::Config->{'cpan_home'},"Bundle");
1786*0Sstevel@tonic-gate    File::Path::mkpath($todir);
1787*0Sstevel@tonic-gate    unless (-d $todir) {
1788*0Sstevel@tonic-gate	$CPAN::Frontend->myprint("Couldn't mkdir $todir for some reason\n");
1789*0Sstevel@tonic-gate	return;
1790*0Sstevel@tonic-gate    }
1791*0Sstevel@tonic-gate    my($y,$m,$d) =  (localtime)[5,4,3];
1792*0Sstevel@tonic-gate    $y+=1900;
1793*0Sstevel@tonic-gate    $m++;
1794*0Sstevel@tonic-gate    my($c) = 0;
1795*0Sstevel@tonic-gate    my($me) = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, $c;
1796*0Sstevel@tonic-gate    my($to) = File::Spec->catfile($todir,"$me.pm");
1797*0Sstevel@tonic-gate    while (-f $to) {
1798*0Sstevel@tonic-gate	$me = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, ++$c;
1799*0Sstevel@tonic-gate	$to = File::Spec->catfile($todir,"$me.pm");
1800*0Sstevel@tonic-gate    }
1801*0Sstevel@tonic-gate    my($fh) = FileHandle->new(">$to") or Carp::croak "Can't open >$to: $!";
1802*0Sstevel@tonic-gate    $fh->print(
1803*0Sstevel@tonic-gate	       "package Bundle::$me;\n\n",
1804*0Sstevel@tonic-gate	       "\$VERSION = '0.01';\n\n",
1805*0Sstevel@tonic-gate	       "1;\n\n",
1806*0Sstevel@tonic-gate	       "__END__\n\n",
1807*0Sstevel@tonic-gate	       "=head1 NAME\n\n",
1808*0Sstevel@tonic-gate	       "Bundle::$me - Snapshot of installation on ",
1809*0Sstevel@tonic-gate	       $Config::Config{'myhostname'},
1810*0Sstevel@tonic-gate	       " on ",
1811*0Sstevel@tonic-gate	       scalar(localtime),
1812*0Sstevel@tonic-gate	       "\n\n=head1 SYNOPSIS\n\n",
1813*0Sstevel@tonic-gate	       "perl -MCPAN -e 'install Bundle::$me'\n\n",
1814*0Sstevel@tonic-gate	       "=head1 CONTENTS\n\n",
1815*0Sstevel@tonic-gate	       join("\n", @bundle),
1816*0Sstevel@tonic-gate	       "\n\n=head1 CONFIGURATION\n\n",
1817*0Sstevel@tonic-gate	       Config->myconfig,
1818*0Sstevel@tonic-gate	       "\n\n=head1 AUTHOR\n\n",
1819*0Sstevel@tonic-gate	       "This Bundle has been generated automatically ",
1820*0Sstevel@tonic-gate	       "by the autobundle routine in CPAN.pm.\n",
1821*0Sstevel@tonic-gate	      );
1822*0Sstevel@tonic-gate    $fh->close;
1823*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("\nWrote bundle file
1824*0Sstevel@tonic-gate    $to\n\n");
1825*0Sstevel@tonic-gate}
1826*0Sstevel@tonic-gate
1827*0Sstevel@tonic-gate#-> sub CPAN::Shell::expandany ;
1828*0Sstevel@tonic-gatesub expandany {
1829*0Sstevel@tonic-gate    my($self,$s) = @_;
1830*0Sstevel@tonic-gate    CPAN->debug("s[$s]") if $CPAN::DEBUG;
1831*0Sstevel@tonic-gate    if ($s =~ m|/|) { # looks like a file
1832*0Sstevel@tonic-gate        $s = CPAN::Distribution->normalize($s);
1833*0Sstevel@tonic-gate        return $CPAN::META->instance('CPAN::Distribution',$s);
1834*0Sstevel@tonic-gate        # Distributions spring into existence, not expand
1835*0Sstevel@tonic-gate    } elsif ($s =~ m|^Bundle::|) {
1836*0Sstevel@tonic-gate        $self->local_bundles; # scanning so late for bundles seems
1837*0Sstevel@tonic-gate                              # both attractive and crumpy: always
1838*0Sstevel@tonic-gate                              # current state but easy to forget
1839*0Sstevel@tonic-gate                              # somewhere
1840*0Sstevel@tonic-gate        return $self->expand('Bundle',$s);
1841*0Sstevel@tonic-gate    } else {
1842*0Sstevel@tonic-gate        return $self->expand('Module',$s)
1843*0Sstevel@tonic-gate            if $CPAN::META->exists('CPAN::Module',$s);
1844*0Sstevel@tonic-gate    }
1845*0Sstevel@tonic-gate    return;
1846*0Sstevel@tonic-gate}
1847*0Sstevel@tonic-gate
1848*0Sstevel@tonic-gate#-> sub CPAN::Shell::expand ;
1849*0Sstevel@tonic-gatesub expand {
1850*0Sstevel@tonic-gate    shift;
1851*0Sstevel@tonic-gate    my($type,@args) = @_;
1852*0Sstevel@tonic-gate    my($arg,@m);
1853*0Sstevel@tonic-gate    CPAN->debug("type[$type]args[@args]") if $CPAN::DEBUG;
1854*0Sstevel@tonic-gate    for $arg (@args) {
1855*0Sstevel@tonic-gate	my($regex,$command);
1856*0Sstevel@tonic-gate	if ($arg =~ m|^/(.*)/$|) {
1857*0Sstevel@tonic-gate	    $regex = $1;
1858*0Sstevel@tonic-gate	} elsif ($arg =~ m/=/) {
1859*0Sstevel@tonic-gate            $command = 1;
1860*0Sstevel@tonic-gate        }
1861*0Sstevel@tonic-gate	my $class = "CPAN::$type";
1862*0Sstevel@tonic-gate	my $obj;
1863*0Sstevel@tonic-gate        CPAN->debug(sprintf "class[%s]regex[%s]command[%s]",
1864*0Sstevel@tonic-gate                    $class,
1865*0Sstevel@tonic-gate                    defined $regex ? $regex : "UNDEFINED",
1866*0Sstevel@tonic-gate                    $command || "UNDEFINED",
1867*0Sstevel@tonic-gate                   ) if $CPAN::DEBUG;
1868*0Sstevel@tonic-gate	if (defined $regex) {
1869*0Sstevel@tonic-gate            for $obj (
1870*0Sstevel@tonic-gate                      sort
1871*0Sstevel@tonic-gate                      {$a->id cmp $b->id}
1872*0Sstevel@tonic-gate                      $CPAN::META->all_objects($class)
1873*0Sstevel@tonic-gate                     ) {
1874*0Sstevel@tonic-gate                unless ($obj->id){
1875*0Sstevel@tonic-gate                    # BUG, we got an empty object somewhere
1876*0Sstevel@tonic-gate                    require Data::Dumper;
1877*0Sstevel@tonic-gate                    CPAN->debug(sprintf(
1878*0Sstevel@tonic-gate                                        "Bug in CPAN: Empty id on obj[%s][%s]",
1879*0Sstevel@tonic-gate                                        $obj,
1880*0Sstevel@tonic-gate                                        Data::Dumper::Dumper($obj)
1881*0Sstevel@tonic-gate                                       )) if $CPAN::DEBUG;
1882*0Sstevel@tonic-gate                    next;
1883*0Sstevel@tonic-gate                }
1884*0Sstevel@tonic-gate                push @m, $obj
1885*0Sstevel@tonic-gate                    if $obj->id =~ /$regex/i
1886*0Sstevel@tonic-gate                        or
1887*0Sstevel@tonic-gate                            (
1888*0Sstevel@tonic-gate                             (
1889*0Sstevel@tonic-gate                              $] < 5.00303 ### provide sort of
1890*0Sstevel@tonic-gate                              ### compatibility with 5.003
1891*0Sstevel@tonic-gate                              ||
1892*0Sstevel@tonic-gate                              $obj->can('name')
1893*0Sstevel@tonic-gate                             )
1894*0Sstevel@tonic-gate                             &&
1895*0Sstevel@tonic-gate                             $obj->name  =~ /$regex/i
1896*0Sstevel@tonic-gate                            );
1897*0Sstevel@tonic-gate            }
1898*0Sstevel@tonic-gate        } elsif ($command) {
1899*0Sstevel@tonic-gate            die "equal sign in command disabled (immature interface), ".
1900*0Sstevel@tonic-gate                "you can set
1901*0Sstevel@tonic-gate ! \$CPAN::Shell::ADVANCED_QUERY=1
1902*0Sstevel@tonic-gateto enable it. But please note, this is HIGHLY EXPERIMENTAL code
1903*0Sstevel@tonic-gatethat may go away anytime.\n"
1904*0Sstevel@tonic-gate                    unless $ADVANCED_QUERY;
1905*0Sstevel@tonic-gate            my($method,$criterion) = $arg =~ /(.+?)=(.+)/;
1906*0Sstevel@tonic-gate            my($matchcrit) = $criterion =~ m/^~(.+)/;
1907*0Sstevel@tonic-gate            for my $self (
1908*0Sstevel@tonic-gate                          sort
1909*0Sstevel@tonic-gate                          {$a->id cmp $b->id}
1910*0Sstevel@tonic-gate                          $CPAN::META->all_objects($class)
1911*0Sstevel@tonic-gate                         ) {
1912*0Sstevel@tonic-gate                my $lhs = $self->$method() or next; # () for 5.00503
1913*0Sstevel@tonic-gate                if ($matchcrit) {
1914*0Sstevel@tonic-gate                    push @m, $self if $lhs =~ m/$matchcrit/;
1915*0Sstevel@tonic-gate                } else {
1916*0Sstevel@tonic-gate                    push @m, $self if $lhs eq $criterion;
1917*0Sstevel@tonic-gate                }
1918*0Sstevel@tonic-gate            }
1919*0Sstevel@tonic-gate	} else {
1920*0Sstevel@tonic-gate	    my($xarg) = $arg;
1921*0Sstevel@tonic-gate	    if ( $type eq 'Bundle' ) {
1922*0Sstevel@tonic-gate		$xarg =~ s/^(Bundle::)?(.*)/Bundle::$2/;
1923*0Sstevel@tonic-gate	    } elsif ($type eq "Distribution") {
1924*0Sstevel@tonic-gate                $xarg = CPAN::Distribution->normalize($arg);
1925*0Sstevel@tonic-gate            }
1926*0Sstevel@tonic-gate	    if ($CPAN::META->exists($class,$xarg)) {
1927*0Sstevel@tonic-gate		$obj = $CPAN::META->instance($class,$xarg);
1928*0Sstevel@tonic-gate	    } elsif ($CPAN::META->exists($class,$arg)) {
1929*0Sstevel@tonic-gate		$obj = $CPAN::META->instance($class,$arg);
1930*0Sstevel@tonic-gate	    } else {
1931*0Sstevel@tonic-gate		next;
1932*0Sstevel@tonic-gate	    }
1933*0Sstevel@tonic-gate	    push @m, $obj;
1934*0Sstevel@tonic-gate	}
1935*0Sstevel@tonic-gate    }
1936*0Sstevel@tonic-gate    return wantarray ? @m : $m[0];
1937*0Sstevel@tonic-gate}
1938*0Sstevel@tonic-gate
1939*0Sstevel@tonic-gate#-> sub CPAN::Shell::format_result ;
1940*0Sstevel@tonic-gatesub format_result {
1941*0Sstevel@tonic-gate    my($self) = shift;
1942*0Sstevel@tonic-gate    my($type,@args) = @_;
1943*0Sstevel@tonic-gate    @args = '/./' unless @args;
1944*0Sstevel@tonic-gate    my(@result) = $self->expand($type,@args);
1945*0Sstevel@tonic-gate    my $result = @result == 1 ?
1946*0Sstevel@tonic-gate	$result[0]->as_string :
1947*0Sstevel@tonic-gate            @result == 0 ?
1948*0Sstevel@tonic-gate                "No objects of type $type found for argument @args\n" :
1949*0Sstevel@tonic-gate                    join("",
1950*0Sstevel@tonic-gate                         (map {$_->as_glimpse} @result),
1951*0Sstevel@tonic-gate                         scalar @result, " items found\n",
1952*0Sstevel@tonic-gate                        );
1953*0Sstevel@tonic-gate    $result;
1954*0Sstevel@tonic-gate}
1955*0Sstevel@tonic-gate
1956*0Sstevel@tonic-gate# The only reason for this method is currently to have a reliable
1957*0Sstevel@tonic-gate# debugging utility that reveals which output is going through which
1958*0Sstevel@tonic-gate# channel. No, I don't like the colors ;-)
1959*0Sstevel@tonic-gate
1960*0Sstevel@tonic-gate#-> sub CPAN::Shell::print_ornameted ;
1961*0Sstevel@tonic-gatesub print_ornamented {
1962*0Sstevel@tonic-gate    my($self,$what,$ornament) = @_;
1963*0Sstevel@tonic-gate    my $longest = 0;
1964*0Sstevel@tonic-gate    return unless defined $what;
1965*0Sstevel@tonic-gate
1966*0Sstevel@tonic-gate    if ($CPAN::Config->{term_is_latin}){
1967*0Sstevel@tonic-gate        # courtesy jhi:
1968*0Sstevel@tonic-gate        $what
1969*0Sstevel@tonic-gate            =~ s{([\xC0-\xDF])([\x80-\xBF])}{chr(ord($1)<<6&0xC0|ord($2)&0x3F)}eg; #};
1970*0Sstevel@tonic-gate    }
1971*0Sstevel@tonic-gate    if ($PRINT_ORNAMENTING) {
1972*0Sstevel@tonic-gate	unless (defined &color) {
1973*0Sstevel@tonic-gate	    if ($CPAN::META->has_inst("Term::ANSIColor")) {
1974*0Sstevel@tonic-gate		import Term::ANSIColor "color";
1975*0Sstevel@tonic-gate	    } else {
1976*0Sstevel@tonic-gate		*color = sub { return "" };
1977*0Sstevel@tonic-gate	    }
1978*0Sstevel@tonic-gate	}
1979*0Sstevel@tonic-gate	my $line;
1980*0Sstevel@tonic-gate	for $line (split /\n/, $what) {
1981*0Sstevel@tonic-gate	    $longest = length($line) if length($line) > $longest;
1982*0Sstevel@tonic-gate	}
1983*0Sstevel@tonic-gate	my $sprintf = "%-" . $longest . "s";
1984*0Sstevel@tonic-gate	while ($what){
1985*0Sstevel@tonic-gate	    $what =~ s/(.*\n?)//m;
1986*0Sstevel@tonic-gate	    my $line = $1;
1987*0Sstevel@tonic-gate	    last unless $line;
1988*0Sstevel@tonic-gate	    my($nl) = chomp $line ? "\n" : "";
1989*0Sstevel@tonic-gate	    #	print "line[$line]ornament[$ornament]sprintf[$sprintf]\n";
1990*0Sstevel@tonic-gate	    print color($ornament), sprintf($sprintf,$line), color("reset"), $nl;
1991*0Sstevel@tonic-gate	}
1992*0Sstevel@tonic-gate    } else {
1993*0Sstevel@tonic-gate        # chomp $what;
1994*0Sstevel@tonic-gate        # $what .= "\n"; # newlines unless $PRINT_ORNAMENTING
1995*0Sstevel@tonic-gate	print $what;
1996*0Sstevel@tonic-gate    }
1997*0Sstevel@tonic-gate}
1998*0Sstevel@tonic-gate
1999*0Sstevel@tonic-gatesub myprint {
2000*0Sstevel@tonic-gate    my($self,$what) = @_;
2001*0Sstevel@tonic-gate
2002*0Sstevel@tonic-gate    $self->print_ornamented($what, 'bold blue on_yellow');
2003*0Sstevel@tonic-gate}
2004*0Sstevel@tonic-gate
2005*0Sstevel@tonic-gatesub myexit {
2006*0Sstevel@tonic-gate    my($self,$what) = @_;
2007*0Sstevel@tonic-gate    $self->myprint($what);
2008*0Sstevel@tonic-gate    exit;
2009*0Sstevel@tonic-gate}
2010*0Sstevel@tonic-gate
2011*0Sstevel@tonic-gatesub mywarn {
2012*0Sstevel@tonic-gate    my($self,$what) = @_;
2013*0Sstevel@tonic-gate    $self->print_ornamented($what, 'bold red on_yellow');
2014*0Sstevel@tonic-gate}
2015*0Sstevel@tonic-gate
2016*0Sstevel@tonic-gatesub myconfess {
2017*0Sstevel@tonic-gate    my($self,$what) = @_;
2018*0Sstevel@tonic-gate    $self->print_ornamented($what, 'bold red on_white');
2019*0Sstevel@tonic-gate    Carp::confess "died";
2020*0Sstevel@tonic-gate}
2021*0Sstevel@tonic-gate
2022*0Sstevel@tonic-gatesub mydie {
2023*0Sstevel@tonic-gate    my($self,$what) = @_;
2024*0Sstevel@tonic-gate    $self->print_ornamented($what, 'bold red on_white');
2025*0Sstevel@tonic-gate    die "\n";
2026*0Sstevel@tonic-gate}
2027*0Sstevel@tonic-gate
2028*0Sstevel@tonic-gatesub setup_output {
2029*0Sstevel@tonic-gate    return if -t STDOUT;
2030*0Sstevel@tonic-gate    my $odef = select STDERR;
2031*0Sstevel@tonic-gate    $| = 1;
2032*0Sstevel@tonic-gate    select STDOUT;
2033*0Sstevel@tonic-gate    $| = 1;
2034*0Sstevel@tonic-gate    select $odef;
2035*0Sstevel@tonic-gate}
2036*0Sstevel@tonic-gate
2037*0Sstevel@tonic-gate#-> sub CPAN::Shell::rematein ;
2038*0Sstevel@tonic-gate# RE-adme||MA-ke||TE-st||IN-stall
2039*0Sstevel@tonic-gatesub rematein {
2040*0Sstevel@tonic-gate    shift;
2041*0Sstevel@tonic-gate    my($meth,@some) = @_;
2042*0Sstevel@tonic-gate    my $pragma = "";
2043*0Sstevel@tonic-gate    if ($meth eq 'force') {
2044*0Sstevel@tonic-gate	$pragma = $meth;
2045*0Sstevel@tonic-gate	$meth = shift @some;
2046*0Sstevel@tonic-gate    }
2047*0Sstevel@tonic-gate    setup_output();
2048*0Sstevel@tonic-gate    CPAN->debug("pragma[$pragma]meth[$meth] some[@some]") if $CPAN::DEBUG;
2049*0Sstevel@tonic-gate
2050*0Sstevel@tonic-gate    # Here is the place to set "test_count" on all involved parties to
2051*0Sstevel@tonic-gate    # 0. We then can pass this counter on to the involved
2052*0Sstevel@tonic-gate    # distributions and those can refuse to test if test_count > X. In
2053*0Sstevel@tonic-gate    # the first stab at it we could use a 1 for "X".
2054*0Sstevel@tonic-gate
2055*0Sstevel@tonic-gate    # But when do I reset the distributions to start with 0 again?
2056*0Sstevel@tonic-gate    # Jost suggested to have a random or cycling interaction ID that
2057*0Sstevel@tonic-gate    # we pass through. But the ID is something that is just left lying
2058*0Sstevel@tonic-gate    # around in addition to the counter, so I'd prefer to set the
2059*0Sstevel@tonic-gate    # counter to 0 now, and repeat at the end of the loop. But what
2060*0Sstevel@tonic-gate    # about dependencies? They appear later and are not reset, they
2061*0Sstevel@tonic-gate    # enter the queue but not its copy. How do they get a sensible
2062*0Sstevel@tonic-gate    # test_count?
2063*0Sstevel@tonic-gate
2064*0Sstevel@tonic-gate    # construct the queue
2065*0Sstevel@tonic-gate    my($s,@s,@qcopy);
2066*0Sstevel@tonic-gate    foreach $s (@some) {
2067*0Sstevel@tonic-gate	my $obj;
2068*0Sstevel@tonic-gate	if (ref $s) {
2069*0Sstevel@tonic-gate            CPAN->debug("s is an object[$s]") if $CPAN::DEBUG;
2070*0Sstevel@tonic-gate	    $obj = $s;
2071*0Sstevel@tonic-gate	} elsif ($s =~ m|^/|) { # looks like a regexp
2072*0Sstevel@tonic-gate            $CPAN::Frontend->mywarn("Sorry, $meth with a regular expression is ".
2073*0Sstevel@tonic-gate                                    "not supported\n");
2074*0Sstevel@tonic-gate            sleep 2;
2075*0Sstevel@tonic-gate            next;
2076*0Sstevel@tonic-gate	} else {
2077*0Sstevel@tonic-gate            CPAN->debug("calling expandany [$s]") if $CPAN::DEBUG;
2078*0Sstevel@tonic-gate	    $obj = CPAN::Shell->expandany($s);
2079*0Sstevel@tonic-gate	}
2080*0Sstevel@tonic-gate	if (ref $obj) {
2081*0Sstevel@tonic-gate            $obj->color_cmd_tmps(0,1);
2082*0Sstevel@tonic-gate            CPAN::Queue->new($obj->id);
2083*0Sstevel@tonic-gate            push @qcopy, $obj;
2084*0Sstevel@tonic-gate	} elsif ($CPAN::META->exists('CPAN::Author',$s)) {
2085*0Sstevel@tonic-gate	    $obj = $CPAN::META->instance('CPAN::Author',$s);
2086*0Sstevel@tonic-gate            if ($meth =~ /^(dump|ls)$/) {
2087*0Sstevel@tonic-gate                $obj->$meth();
2088*0Sstevel@tonic-gate            } else {
2089*0Sstevel@tonic-gate                $CPAN::Frontend->myprint(
2090*0Sstevel@tonic-gate                                         join "",
2091*0Sstevel@tonic-gate                                         "Don't be silly, you can't $meth ",
2092*0Sstevel@tonic-gate                                         $obj->fullname,
2093*0Sstevel@tonic-gate                                         " ;-)\n"
2094*0Sstevel@tonic-gate                                        );
2095*0Sstevel@tonic-gate                sleep 2;
2096*0Sstevel@tonic-gate            }
2097*0Sstevel@tonic-gate	} else {
2098*0Sstevel@tonic-gate	    $CPAN::Frontend
2099*0Sstevel@tonic-gate		->myprint(qq{Warning: Cannot $meth $s, }.
2100*0Sstevel@tonic-gate			  qq{don\'t know what it is.
2101*0Sstevel@tonic-gateTry the command
2102*0Sstevel@tonic-gate
2103*0Sstevel@tonic-gate    i /$s/
2104*0Sstevel@tonic-gate
2105*0Sstevel@tonic-gateto find objects with matching identifiers.
2106*0Sstevel@tonic-gate});
2107*0Sstevel@tonic-gate            sleep 2;
2108*0Sstevel@tonic-gate	}
2109*0Sstevel@tonic-gate    }
2110*0Sstevel@tonic-gate
2111*0Sstevel@tonic-gate    # queuerunner (please be warned: when I started to change the
2112*0Sstevel@tonic-gate    # queue to hold objects instead of names, I made one or two
2113*0Sstevel@tonic-gate    # mistakes and never found which. I reverted back instead)
2114*0Sstevel@tonic-gate    while ($s = CPAN::Queue->first) {
2115*0Sstevel@tonic-gate        my $obj;
2116*0Sstevel@tonic-gate	if (ref $s) {
2117*0Sstevel@tonic-gate	    $obj = $s; # I do not believe, we would survive if this happened
2118*0Sstevel@tonic-gate	} else {
2119*0Sstevel@tonic-gate	    $obj = CPAN::Shell->expandany($s);
2120*0Sstevel@tonic-gate	}
2121*0Sstevel@tonic-gate        if ($pragma
2122*0Sstevel@tonic-gate            &&
2123*0Sstevel@tonic-gate            ($] < 5.00303 || $obj->can($pragma))){
2124*0Sstevel@tonic-gate            ### compatibility with 5.003
2125*0Sstevel@tonic-gate            $obj->$pragma($meth); # the pragma "force" in
2126*0Sstevel@tonic-gate                                  # "CPAN::Distribution" must know
2127*0Sstevel@tonic-gate                                  # what we are intending
2128*0Sstevel@tonic-gate        }
2129*0Sstevel@tonic-gate        if ($]>=5.00303 && $obj->can('called_for')) {
2130*0Sstevel@tonic-gate            $obj->called_for($s);
2131*0Sstevel@tonic-gate        }
2132*0Sstevel@tonic-gate        CPAN->debug(
2133*0Sstevel@tonic-gate                    qq{pragma[$pragma]meth[$meth]obj[$obj]as_string\[}.
2134*0Sstevel@tonic-gate                    $obj->as_string.
2135*0Sstevel@tonic-gate                    qq{\]}
2136*0Sstevel@tonic-gate                   ) if $CPAN::DEBUG;
2137*0Sstevel@tonic-gate
2138*0Sstevel@tonic-gate        if ($obj->$meth()){
2139*0Sstevel@tonic-gate            CPAN::Queue->delete($s);
2140*0Sstevel@tonic-gate        } else {
2141*0Sstevel@tonic-gate            CPAN->debug("failed");
2142*0Sstevel@tonic-gate        }
2143*0Sstevel@tonic-gate
2144*0Sstevel@tonic-gate        $obj->undelay;
2145*0Sstevel@tonic-gate	CPAN::Queue->delete_first($s);
2146*0Sstevel@tonic-gate    }
2147*0Sstevel@tonic-gate    for my $obj (@qcopy) {
2148*0Sstevel@tonic-gate        $obj->color_cmd_tmps(0,0);
2149*0Sstevel@tonic-gate    }
2150*0Sstevel@tonic-gate}
2151*0Sstevel@tonic-gate
2152*0Sstevel@tonic-gate#-> sub CPAN::Shell::dump ;
2153*0Sstevel@tonic-gatesub dump    { shift->rematein('dump',@_); }
2154*0Sstevel@tonic-gate#-> sub CPAN::Shell::force ;
2155*0Sstevel@tonic-gatesub force   { shift->rematein('force',@_); }
2156*0Sstevel@tonic-gate#-> sub CPAN::Shell::get ;
2157*0Sstevel@tonic-gatesub get     { shift->rematein('get',@_); }
2158*0Sstevel@tonic-gate#-> sub CPAN::Shell::readme ;
2159*0Sstevel@tonic-gatesub readme  { shift->rematein('readme',@_); }
2160*0Sstevel@tonic-gate#-> sub CPAN::Shell::make ;
2161*0Sstevel@tonic-gatesub make    { shift->rematein('make',@_); }
2162*0Sstevel@tonic-gate#-> sub CPAN::Shell::test ;
2163*0Sstevel@tonic-gatesub test    { shift->rematein('test',@_); }
2164*0Sstevel@tonic-gate#-> sub CPAN::Shell::install ;
2165*0Sstevel@tonic-gatesub install { shift->rematein('install',@_); }
2166*0Sstevel@tonic-gate#-> sub CPAN::Shell::clean ;
2167*0Sstevel@tonic-gatesub clean   { shift->rematein('clean',@_); }
2168*0Sstevel@tonic-gate#-> sub CPAN::Shell::look ;
2169*0Sstevel@tonic-gatesub look   { shift->rematein('look',@_); }
2170*0Sstevel@tonic-gate#-> sub CPAN::Shell::cvs_import ;
2171*0Sstevel@tonic-gatesub cvs_import   { shift->rematein('cvs_import',@_); }
2172*0Sstevel@tonic-gate
2173*0Sstevel@tonic-gatepackage CPAN::LWP::UserAgent;
2174*0Sstevel@tonic-gate
2175*0Sstevel@tonic-gatesub config {
2176*0Sstevel@tonic-gate    return if $SETUPDONE;
2177*0Sstevel@tonic-gate    if ($CPAN::META->has_usable('LWP::UserAgent')) {
2178*0Sstevel@tonic-gate        require LWP::UserAgent;
2179*0Sstevel@tonic-gate        @ISA = qw(Exporter LWP::UserAgent);
2180*0Sstevel@tonic-gate        $SETUPDONE++;
2181*0Sstevel@tonic-gate    } else {
2182*0Sstevel@tonic-gate        $CPAN::Frontend->mywarn("LWP::UserAgent not available\n");
2183*0Sstevel@tonic-gate    }
2184*0Sstevel@tonic-gate}
2185*0Sstevel@tonic-gate
2186*0Sstevel@tonic-gatesub get_basic_credentials {
2187*0Sstevel@tonic-gate    my($self, $realm, $uri, $proxy) = @_;
2188*0Sstevel@tonic-gate    return unless $proxy;
2189*0Sstevel@tonic-gate    if ($USER && $PASSWD) {
2190*0Sstevel@tonic-gate    } elsif (defined $CPAN::Config->{proxy_user} &&
2191*0Sstevel@tonic-gate             defined $CPAN::Config->{proxy_pass}) {
2192*0Sstevel@tonic-gate        $USER = $CPAN::Config->{proxy_user};
2193*0Sstevel@tonic-gate        $PASSWD = $CPAN::Config->{proxy_pass};
2194*0Sstevel@tonic-gate    } else {
2195*0Sstevel@tonic-gate        require ExtUtils::MakeMaker;
2196*0Sstevel@tonic-gate        ExtUtils::MakeMaker->import(qw(prompt));
2197*0Sstevel@tonic-gate        $USER = prompt("Proxy authentication needed!
2198*0Sstevel@tonic-gate (Note: to permanently configure username and password run
2199*0Sstevel@tonic-gate   o conf proxy_user your_username
2200*0Sstevel@tonic-gate   o conf proxy_pass your_password
2201*0Sstevel@tonic-gate )\nUsername:");
2202*0Sstevel@tonic-gate        if ($CPAN::META->has_inst("Term::ReadKey")) {
2203*0Sstevel@tonic-gate            Term::ReadKey::ReadMode("noecho");
2204*0Sstevel@tonic-gate        } else {
2205*0Sstevel@tonic-gate            $CPAN::Frontend->mywarn("Warning: Term::ReadKey seems not to be available, your password will be echoed to the terminal!\n");
2206*0Sstevel@tonic-gate        }
2207*0Sstevel@tonic-gate        $PASSWD = prompt("Password:");
2208*0Sstevel@tonic-gate        if ($CPAN::META->has_inst("Term::ReadKey")) {
2209*0Sstevel@tonic-gate            Term::ReadKey::ReadMode("restore");
2210*0Sstevel@tonic-gate        }
2211*0Sstevel@tonic-gate        $CPAN::Frontend->myprint("\n\n");
2212*0Sstevel@tonic-gate    }
2213*0Sstevel@tonic-gate    return($USER,$PASSWD);
2214*0Sstevel@tonic-gate}
2215*0Sstevel@tonic-gate
2216*0Sstevel@tonic-gate# mirror(): Its purpose is to deal with proxy authentication. When we
2217*0Sstevel@tonic-gate# call SUPER::mirror, we relly call the mirror method in
2218*0Sstevel@tonic-gate# LWP::UserAgent. LWP::UserAgent will then call
2219*0Sstevel@tonic-gate# $self->get_basic_credentials or some equivalent and this will be
2220*0Sstevel@tonic-gate# $self->dispatched to our own get_basic_credentials method.
2221*0Sstevel@tonic-gate
2222*0Sstevel@tonic-gate# Our own get_basic_credentials sets $USER and $PASSWD, two globals.
2223*0Sstevel@tonic-gate
2224*0Sstevel@tonic-gate# 407 stands for HTTP_PROXY_AUTHENTICATION_REQUIRED. Which means
2225*0Sstevel@tonic-gate# although we have gone through our get_basic_credentials, the proxy
2226*0Sstevel@tonic-gate# server refuses to connect. This could be a case where the username or
2227*0Sstevel@tonic-gate# password has changed in the meantime, so I'm trying once again without
2228*0Sstevel@tonic-gate# $USER and $PASSWD to give the get_basic_credentials routine another
2229*0Sstevel@tonic-gate# chance to set $USER and $PASSWD.
2230*0Sstevel@tonic-gate
2231*0Sstevel@tonic-gatesub mirror {
2232*0Sstevel@tonic-gate    my($self,$url,$aslocal) = @_;
2233*0Sstevel@tonic-gate    my $result = $self->SUPER::mirror($url,$aslocal);
2234*0Sstevel@tonic-gate    if ($result->code == 407) {
2235*0Sstevel@tonic-gate        undef $USER;
2236*0Sstevel@tonic-gate        undef $PASSWD;
2237*0Sstevel@tonic-gate        $result = $self->SUPER::mirror($url,$aslocal);
2238*0Sstevel@tonic-gate    }
2239*0Sstevel@tonic-gate    $result;
2240*0Sstevel@tonic-gate}
2241*0Sstevel@tonic-gate
2242*0Sstevel@tonic-gatepackage CPAN::FTP;
2243*0Sstevel@tonic-gate
2244*0Sstevel@tonic-gate#-> sub CPAN::FTP::ftp_get ;
2245*0Sstevel@tonic-gatesub ftp_get {
2246*0Sstevel@tonic-gate  my($class,$host,$dir,$file,$target) = @_;
2247*0Sstevel@tonic-gate  $class->debug(
2248*0Sstevel@tonic-gate		qq[Going to fetch file [$file] from dir [$dir]
2249*0Sstevel@tonic-gate	on host [$host] as local [$target]\n]
2250*0Sstevel@tonic-gate		      ) if $CPAN::DEBUG;
2251*0Sstevel@tonic-gate  my $ftp = Net::FTP->new($host);
2252*0Sstevel@tonic-gate  return 0 unless defined $ftp;
2253*0Sstevel@tonic-gate  $ftp->debug(1) if $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG;
2254*0Sstevel@tonic-gate  $class->debug(qq[Going to login("anonymous","$Config::Config{cf_email}")]);
2255*0Sstevel@tonic-gate  unless ( $ftp->login("anonymous",$Config::Config{'cf_email'}) ){
2256*0Sstevel@tonic-gate    warn "Couldn't login on $host";
2257*0Sstevel@tonic-gate    return;
2258*0Sstevel@tonic-gate  }
2259*0Sstevel@tonic-gate  unless ( $ftp->cwd($dir) ){
2260*0Sstevel@tonic-gate    warn "Couldn't cwd $dir";
2261*0Sstevel@tonic-gate    return;
2262*0Sstevel@tonic-gate  }
2263*0Sstevel@tonic-gate  $ftp->binary;
2264*0Sstevel@tonic-gate  $class->debug(qq[Going to ->get("$file","$target")\n]) if $CPAN::DEBUG;
2265*0Sstevel@tonic-gate  unless ( $ftp->get($file,$target) ){
2266*0Sstevel@tonic-gate    warn "Couldn't fetch $file from $host\n";
2267*0Sstevel@tonic-gate    return;
2268*0Sstevel@tonic-gate  }
2269*0Sstevel@tonic-gate  $ftp->quit; # it's ok if this fails
2270*0Sstevel@tonic-gate  return 1;
2271*0Sstevel@tonic-gate}
2272*0Sstevel@tonic-gate
2273*0Sstevel@tonic-gate# If more accuracy is wanted/needed, Chris Leach sent me this patch...
2274*0Sstevel@tonic-gate
2275*0Sstevel@tonic-gate # > *** /install/perl/live/lib/CPAN.pm-	Wed Sep 24 13:08:48 1997
2276*0Sstevel@tonic-gate # > --- /tmp/cp	Wed Sep 24 13:26:40 1997
2277*0Sstevel@tonic-gate # > ***************
2278*0Sstevel@tonic-gate # > *** 1562,1567 ****
2279*0Sstevel@tonic-gate # > --- 1562,1580 ----
2280*0Sstevel@tonic-gate # >       return 1 if substr($url,0,4) eq "file";
2281*0Sstevel@tonic-gate # >       return 1 unless $url =~ m|://([^/]+)|;
2282*0Sstevel@tonic-gate # >       my $host = $1;
2283*0Sstevel@tonic-gate # > +     my $proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'};
2284*0Sstevel@tonic-gate # > +     if ($proxy) {
2285*0Sstevel@tonic-gate # > +         $proxy =~ m|://([^/:]+)|;
2286*0Sstevel@tonic-gate # > +         $proxy = $1;
2287*0Sstevel@tonic-gate # > +         my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'};
2288*0Sstevel@tonic-gate # > +         if ($noproxy) {
2289*0Sstevel@tonic-gate # > +             if ($host !~ /$noproxy$/) {
2290*0Sstevel@tonic-gate # > +                 $host = $proxy;
2291*0Sstevel@tonic-gate # > +             }
2292*0Sstevel@tonic-gate # > +         } else {
2293*0Sstevel@tonic-gate # > +             $host = $proxy;
2294*0Sstevel@tonic-gate # > +         }
2295*0Sstevel@tonic-gate # > +     }
2296*0Sstevel@tonic-gate # >       require Net::Ping;
2297*0Sstevel@tonic-gate # >       return 1 unless $Net::Ping::VERSION >= 2;
2298*0Sstevel@tonic-gate # >       my $p;
2299*0Sstevel@tonic-gate
2300*0Sstevel@tonic-gate
2301*0Sstevel@tonic-gate#-> sub CPAN::FTP::localize ;
2302*0Sstevel@tonic-gatesub localize {
2303*0Sstevel@tonic-gate    my($self,$file,$aslocal,$force) = @_;
2304*0Sstevel@tonic-gate    $force ||= 0;
2305*0Sstevel@tonic-gate    Carp::croak "Usage: ->localize(cpan_file,as_local_file[,$force])"
2306*0Sstevel@tonic-gate	unless defined $aslocal;
2307*0Sstevel@tonic-gate    $self->debug("file[$file] aslocal[$aslocal] force[$force]")
2308*0Sstevel@tonic-gate	if $CPAN::DEBUG;
2309*0Sstevel@tonic-gate
2310*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
2311*0Sstevel@tonic-gate        # Comment by AK on 2000-09-03: Uniq short filenames would be
2312*0Sstevel@tonic-gate        # available in CHECKSUMS file
2313*0Sstevel@tonic-gate        my($name, $path) = File::Basename::fileparse($aslocal, '');
2314*0Sstevel@tonic-gate        if (length($name) > 31) {
2315*0Sstevel@tonic-gate            $name =~ s/(
2316*0Sstevel@tonic-gate                        \.(
2317*0Sstevel@tonic-gate                           readme(\.(gz|Z))? |
2318*0Sstevel@tonic-gate                           (tar\.)?(gz|Z) |
2319*0Sstevel@tonic-gate                           tgz |
2320*0Sstevel@tonic-gate                           zip |
2321*0Sstevel@tonic-gate                           pm\.(gz|Z)
2322*0Sstevel@tonic-gate                          )
2323*0Sstevel@tonic-gate                       )$//x;
2324*0Sstevel@tonic-gate            my $suf = $1;
2325*0Sstevel@tonic-gate            my $size = 31 - length($suf);
2326*0Sstevel@tonic-gate            while (length($name) > $size) {
2327*0Sstevel@tonic-gate                chop $name;
2328*0Sstevel@tonic-gate            }
2329*0Sstevel@tonic-gate            $name .= $suf;
2330*0Sstevel@tonic-gate            $aslocal = File::Spec->catfile($path, $name);
2331*0Sstevel@tonic-gate        }
2332*0Sstevel@tonic-gate    }
2333*0Sstevel@tonic-gate
2334*0Sstevel@tonic-gate    return $aslocal if -f $aslocal && -r _ && !($force & 1);
2335*0Sstevel@tonic-gate    my($restore) = 0;
2336*0Sstevel@tonic-gate    if (-f $aslocal){
2337*0Sstevel@tonic-gate	rename $aslocal, "$aslocal.bak";
2338*0Sstevel@tonic-gate	$restore++;
2339*0Sstevel@tonic-gate    }
2340*0Sstevel@tonic-gate
2341*0Sstevel@tonic-gate    my($aslocal_dir) = File::Basename::dirname($aslocal);
2342*0Sstevel@tonic-gate    File::Path::mkpath($aslocal_dir);
2343*0Sstevel@tonic-gate    $CPAN::Frontend->mywarn(qq{Warning: You are not allowed to write into }.
2344*0Sstevel@tonic-gate	qq{directory "$aslocal_dir".
2345*0Sstevel@tonic-gate    I\'ll continue, but if you encounter problems, they may be due
2346*0Sstevel@tonic-gate    to insufficient permissions.\n}) unless -w $aslocal_dir;
2347*0Sstevel@tonic-gate
2348*0Sstevel@tonic-gate    # Inheritance is not easier to manage than a few if/else branches
2349*0Sstevel@tonic-gate    if ($CPAN::META->has_usable('LWP::UserAgent')) {
2350*0Sstevel@tonic-gate 	unless ($Ua) {
2351*0Sstevel@tonic-gate            CPAN::LWP::UserAgent->config;
2352*0Sstevel@tonic-gate	    eval {$Ua = CPAN::LWP::UserAgent->new;}; # Why is has_usable still not fit enough?
2353*0Sstevel@tonic-gate            if ($@) {
2354*0Sstevel@tonic-gate                $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n")
2355*0Sstevel@tonic-gate                    if $CPAN::DEBUG;
2356*0Sstevel@tonic-gate            } else {
2357*0Sstevel@tonic-gate                my($var);
2358*0Sstevel@tonic-gate                $Ua->proxy('ftp',  $var)
2359*0Sstevel@tonic-gate                    if $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy};
2360*0Sstevel@tonic-gate                $Ua->proxy('http', $var)
2361*0Sstevel@tonic-gate                    if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy};
2362*0Sstevel@tonic-gate
2363*0Sstevel@tonic-gate
2364*0Sstevel@tonic-gate# >>>>> On Wed, 13 Dec 2000 09:21:34 -0500, "Robison, Jonathon (J.M.)" <jrobiso2@visteon.com> said:
2365*0Sstevel@tonic-gate#
2366*0Sstevel@tonic-gate#  > I note that although CPAN.pm can use proxies, it doesn't seem equipped to
2367*0Sstevel@tonic-gate#  > use ones that require basic autorization.
2368*0Sstevel@tonic-gate#
2369*0Sstevel@tonic-gate#  > Example of when I use it manually in my own stuff:
2370*0Sstevel@tonic-gate#
2371*0Sstevel@tonic-gate#  > $ua->proxy(['http','ftp'], http://my.proxy.server:83');
2372*0Sstevel@tonic-gate#  > $req->proxy_authorization_basic("username","password");
2373*0Sstevel@tonic-gate#  > $res = $ua->request($req);
2374*0Sstevel@tonic-gate#
2375*0Sstevel@tonic-gate
2376*0Sstevel@tonic-gate                $Ua->no_proxy($var)
2377*0Sstevel@tonic-gate                    if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy};
2378*0Sstevel@tonic-gate            }
2379*0Sstevel@tonic-gate	}
2380*0Sstevel@tonic-gate    }
2381*0Sstevel@tonic-gate    for my $prx (qw(ftp_proxy http_proxy no_proxy)) {
2382*0Sstevel@tonic-gate        $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx};
2383*0Sstevel@tonic-gate    }
2384*0Sstevel@tonic-gate
2385*0Sstevel@tonic-gate    # Try the list of urls for each single object. We keep a record
2386*0Sstevel@tonic-gate    # where we did get a file from
2387*0Sstevel@tonic-gate    my(@reordered,$last);
2388*0Sstevel@tonic-gate    $CPAN::Config->{urllist} ||= [];
2389*0Sstevel@tonic-gate    unless (ref $CPAN::Config->{urllist} eq 'ARRAY') {
2390*0Sstevel@tonic-gate        warn "Malformed urllist; ignoring.  Configuration file corrupt?\n";
2391*0Sstevel@tonic-gate    }
2392*0Sstevel@tonic-gate    $last = $#{$CPAN::Config->{urllist}};
2393*0Sstevel@tonic-gate    if ($force & 2) { # local cpans probably out of date, don't reorder
2394*0Sstevel@tonic-gate	@reordered = (0..$last);
2395*0Sstevel@tonic-gate    } else {
2396*0Sstevel@tonic-gate	@reordered =
2397*0Sstevel@tonic-gate	    sort {
2398*0Sstevel@tonic-gate		(substr($CPAN::Config->{urllist}[$b],0,4) eq "file")
2399*0Sstevel@tonic-gate		    <=>
2400*0Sstevel@tonic-gate		(substr($CPAN::Config->{urllist}[$a],0,4) eq "file")
2401*0Sstevel@tonic-gate		    or
2402*0Sstevel@tonic-gate		defined($Thesite)
2403*0Sstevel@tonic-gate		    and
2404*0Sstevel@tonic-gate		($b == $Thesite)
2405*0Sstevel@tonic-gate		    <=>
2406*0Sstevel@tonic-gate		($a == $Thesite)
2407*0Sstevel@tonic-gate	    } 0..$last;
2408*0Sstevel@tonic-gate    }
2409*0Sstevel@tonic-gate    my(@levels);
2410*0Sstevel@tonic-gate    if ($Themethod) {
2411*0Sstevel@tonic-gate	@levels = ($Themethod, grep {$_ ne $Themethod} qw/easy hard hardest/);
2412*0Sstevel@tonic-gate    } else {
2413*0Sstevel@tonic-gate	@levels = qw/easy hard hardest/;
2414*0Sstevel@tonic-gate    }
2415*0Sstevel@tonic-gate    @levels = qw/easy/ if $^O eq 'MacOS';
2416*0Sstevel@tonic-gate    my($levelno);
2417*0Sstevel@tonic-gate    for $levelno (0..$#levels) {
2418*0Sstevel@tonic-gate        my $level = $levels[$levelno];
2419*0Sstevel@tonic-gate	my $method = "host$level";
2420*0Sstevel@tonic-gate	my @host_seq = $level eq "easy" ?
2421*0Sstevel@tonic-gate	    @reordered : 0..$last;  # reordered has CDROM up front
2422*0Sstevel@tonic-gate	@host_seq = (0) unless @host_seq;
2423*0Sstevel@tonic-gate	my $ret = $self->$method(\@host_seq,$file,$aslocal);
2424*0Sstevel@tonic-gate	if ($ret) {
2425*0Sstevel@tonic-gate	  $Themethod = $level;
2426*0Sstevel@tonic-gate	  my $now = time;
2427*0Sstevel@tonic-gate	  # utime $now, $now, $aslocal; # too bad, if we do that, we
2428*0Sstevel@tonic-gate                                      # might alter a local mirror
2429*0Sstevel@tonic-gate	  $self->debug("level[$level]") if $CPAN::DEBUG;
2430*0Sstevel@tonic-gate	  return $ret;
2431*0Sstevel@tonic-gate	} else {
2432*0Sstevel@tonic-gate	  unlink $aslocal;
2433*0Sstevel@tonic-gate          last if $CPAN::Signal; # need to cleanup
2434*0Sstevel@tonic-gate	}
2435*0Sstevel@tonic-gate    }
2436*0Sstevel@tonic-gate    unless ($CPAN::Signal) {
2437*0Sstevel@tonic-gate        my(@mess);
2438*0Sstevel@tonic-gate        push @mess,
2439*0Sstevel@tonic-gate            qq{Please check, if the URLs I found in your configuration file \(}.
2440*0Sstevel@tonic-gate                join(", ", @{$CPAN::Config->{urllist}}).
2441*0Sstevel@tonic-gate                    qq{\) are valid. The urllist can be edited.},
2442*0Sstevel@tonic-gate                        qq{E.g. with 'o conf urllist push ftp://myurl/'};
2443*0Sstevel@tonic-gate        $CPAN::Frontend->myprint(Text::Wrap::wrap("","",@mess). "\n\n");
2444*0Sstevel@tonic-gate        sleep 2;
2445*0Sstevel@tonic-gate        $CPAN::Frontend->myprint("Could not fetch $file\n");
2446*0Sstevel@tonic-gate    }
2447*0Sstevel@tonic-gate    if ($restore) {
2448*0Sstevel@tonic-gate	rename "$aslocal.bak", $aslocal;
2449*0Sstevel@tonic-gate	$CPAN::Frontend->myprint("Trying to get away with old file:\n" .
2450*0Sstevel@tonic-gate				 $self->ls($aslocal));
2451*0Sstevel@tonic-gate	return $aslocal;
2452*0Sstevel@tonic-gate    }
2453*0Sstevel@tonic-gate    return;
2454*0Sstevel@tonic-gate}
2455*0Sstevel@tonic-gate
2456*0Sstevel@tonic-gatesub hosteasy {
2457*0Sstevel@tonic-gate    my($self,$host_seq,$file,$aslocal) = @_;
2458*0Sstevel@tonic-gate    my($i);
2459*0Sstevel@tonic-gate  HOSTEASY: for $i (@$host_seq) {
2460*0Sstevel@tonic-gate        my $url = $CPAN::Config->{urllist}[$i] || $CPAN::Defaultsite;
2461*0Sstevel@tonic-gate	$url .= "/" unless substr($url,-1) eq "/";
2462*0Sstevel@tonic-gate	$url .= $file;
2463*0Sstevel@tonic-gate	$self->debug("localizing perlish[$url]") if $CPAN::DEBUG;
2464*0Sstevel@tonic-gate	if ($url =~ /^file:/) {
2465*0Sstevel@tonic-gate	    my $l;
2466*0Sstevel@tonic-gate	    if ($CPAN::META->has_inst('URI::URL')) {
2467*0Sstevel@tonic-gate		my $u =  URI::URL->new($url);
2468*0Sstevel@tonic-gate		$l = $u->path;
2469*0Sstevel@tonic-gate	    } else { # works only on Unix, is poorly constructed, but
2470*0Sstevel@tonic-gate		# hopefully better than nothing.
2471*0Sstevel@tonic-gate		# RFC 1738 says fileurl BNF is
2472*0Sstevel@tonic-gate		# fileurl = "file://" [ host | "localhost" ] "/" fpath
2473*0Sstevel@tonic-gate		# Thanks to "Mark D. Baushke" <mdb@cisco.com> for
2474*0Sstevel@tonic-gate		# the code
2475*0Sstevel@tonic-gate		($l = $url) =~ s|^file://[^/]*/|/|; # discard the host part
2476*0Sstevel@tonic-gate		$l =~ s|^file:||;                   # assume they
2477*0Sstevel@tonic-gate                                                    # meant
2478*0Sstevel@tonic-gate                                                    # file://localhost
2479*0Sstevel@tonic-gate		$l =~ s|^/||s unless -f $l;         # e.g. /P:
2480*0Sstevel@tonic-gate		$self->debug("without URI::URL we try local file $l") if $CPAN::DEBUG;
2481*0Sstevel@tonic-gate	    }
2482*0Sstevel@tonic-gate	    if ( -f $l && -r _) {
2483*0Sstevel@tonic-gate		$Thesite = $i;
2484*0Sstevel@tonic-gate		return $l;
2485*0Sstevel@tonic-gate	    }
2486*0Sstevel@tonic-gate	    # Maybe mirror has compressed it?
2487*0Sstevel@tonic-gate	    if (-f "$l.gz") {
2488*0Sstevel@tonic-gate		$self->debug("found compressed $l.gz") if $CPAN::DEBUG;
2489*0Sstevel@tonic-gate		CPAN::Tarzip->gunzip("$l.gz", $aslocal);
2490*0Sstevel@tonic-gate		if ( -f $aslocal) {
2491*0Sstevel@tonic-gate		    $Thesite = $i;
2492*0Sstevel@tonic-gate		    return $aslocal;
2493*0Sstevel@tonic-gate		}
2494*0Sstevel@tonic-gate	    }
2495*0Sstevel@tonic-gate	}
2496*0Sstevel@tonic-gate        if ($CPAN::META->has_usable('LWP')) {
2497*0Sstevel@tonic-gate	  $CPAN::Frontend->myprint("Fetching with LWP:
2498*0Sstevel@tonic-gate  $url
2499*0Sstevel@tonic-gate");
2500*0Sstevel@tonic-gate	  unless ($Ua) {
2501*0Sstevel@tonic-gate              CPAN::LWP::UserAgent->config;
2502*0Sstevel@tonic-gate              eval { $Ua = CPAN::LWP::UserAgent->new; };
2503*0Sstevel@tonic-gate              if ($@) {
2504*0Sstevel@tonic-gate                  $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n");
2505*0Sstevel@tonic-gate              }
2506*0Sstevel@tonic-gate	  }
2507*0Sstevel@tonic-gate	  my $res = $Ua->mirror($url, $aslocal);
2508*0Sstevel@tonic-gate	  if ($res->is_success) {
2509*0Sstevel@tonic-gate	    $Thesite = $i;
2510*0Sstevel@tonic-gate	    my $now = time;
2511*0Sstevel@tonic-gate	    utime $now, $now, $aslocal; # download time is more
2512*0Sstevel@tonic-gate                                        # important than upload time
2513*0Sstevel@tonic-gate	    return $aslocal;
2514*0Sstevel@tonic-gate	  } elsif ($url !~ /\.gz(?!\n)\Z/) {
2515*0Sstevel@tonic-gate	    my $gzurl = "$url.gz";
2516*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("Fetching with LWP:
2517*0Sstevel@tonic-gate  $gzurl
2518*0Sstevel@tonic-gate");
2519*0Sstevel@tonic-gate	    $res = $Ua->mirror($gzurl, "$aslocal.gz");
2520*0Sstevel@tonic-gate	    if ($res->is_success &&
2521*0Sstevel@tonic-gate		CPAN::Tarzip->gunzip("$aslocal.gz",$aslocal)
2522*0Sstevel@tonic-gate	       ) {
2523*0Sstevel@tonic-gate	      $Thesite = $i;
2524*0Sstevel@tonic-gate	      return $aslocal;
2525*0Sstevel@tonic-gate	    }
2526*0Sstevel@tonic-gate	  } else {
2527*0Sstevel@tonic-gate              $CPAN::Frontend->myprint(sprintf(
2528*0Sstevel@tonic-gate                                               "LWP failed with code[%s] message[%s]\n",
2529*0Sstevel@tonic-gate                                               $res->code,
2530*0Sstevel@tonic-gate                                               $res->message,
2531*0Sstevel@tonic-gate                                              ));
2532*0Sstevel@tonic-gate	    # Alan Burlison informed me that in firewall environments
2533*0Sstevel@tonic-gate	    # Net::FTP can still succeed where LWP fails. So we do not
2534*0Sstevel@tonic-gate	    # skip Net::FTP anymore when LWP is available.
2535*0Sstevel@tonic-gate	  }
2536*0Sstevel@tonic-gate	} else {
2537*0Sstevel@tonic-gate            $CPAN::Frontend->myprint("LWP not available\n");
2538*0Sstevel@tonic-gate	}
2539*0Sstevel@tonic-gate        return if $CPAN::Signal;
2540*0Sstevel@tonic-gate	if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) {
2541*0Sstevel@tonic-gate	    # that's the nice and easy way thanks to Graham
2542*0Sstevel@tonic-gate	    my($host,$dir,$getfile) = ($1,$2,$3);
2543*0Sstevel@tonic-gate	    if ($CPAN::META->has_usable('Net::FTP')) {
2544*0Sstevel@tonic-gate		$dir =~ s|/+|/|g;
2545*0Sstevel@tonic-gate		$CPAN::Frontend->myprint("Fetching with Net::FTP:
2546*0Sstevel@tonic-gate  $url
2547*0Sstevel@tonic-gate");
2548*0Sstevel@tonic-gate		$self->debug("getfile[$getfile]dir[$dir]host[$host]" .
2549*0Sstevel@tonic-gate			     "aslocal[$aslocal]") if $CPAN::DEBUG;
2550*0Sstevel@tonic-gate		if (CPAN::FTP->ftp_get($host,$dir,$getfile,$aslocal)) {
2551*0Sstevel@tonic-gate		    $Thesite = $i;
2552*0Sstevel@tonic-gate		    return $aslocal;
2553*0Sstevel@tonic-gate		}
2554*0Sstevel@tonic-gate		if ($aslocal !~ /\.gz(?!\n)\Z/) {
2555*0Sstevel@tonic-gate		    my $gz = "$aslocal.gz";
2556*0Sstevel@tonic-gate		    $CPAN::Frontend->myprint("Fetching with Net::FTP
2557*0Sstevel@tonic-gate  $url.gz
2558*0Sstevel@tonic-gate");
2559*0Sstevel@tonic-gate		   if (CPAN::FTP->ftp_get($host,
2560*0Sstevel@tonic-gate					   $dir,
2561*0Sstevel@tonic-gate					   "$getfile.gz",
2562*0Sstevel@tonic-gate					   $gz) &&
2563*0Sstevel@tonic-gate			CPAN::Tarzip->gunzip($gz,$aslocal)
2564*0Sstevel@tonic-gate		       ){
2565*0Sstevel@tonic-gate			$Thesite = $i;
2566*0Sstevel@tonic-gate			return $aslocal;
2567*0Sstevel@tonic-gate		    }
2568*0Sstevel@tonic-gate		}
2569*0Sstevel@tonic-gate		# next HOSTEASY;
2570*0Sstevel@tonic-gate	    }
2571*0Sstevel@tonic-gate	}
2572*0Sstevel@tonic-gate        return if $CPAN::Signal;
2573*0Sstevel@tonic-gate    }
2574*0Sstevel@tonic-gate}
2575*0Sstevel@tonic-gate
2576*0Sstevel@tonic-gatesub hosthard {
2577*0Sstevel@tonic-gate  my($self,$host_seq,$file,$aslocal) = @_;
2578*0Sstevel@tonic-gate
2579*0Sstevel@tonic-gate  # Came back if Net::FTP couldn't establish connection (or
2580*0Sstevel@tonic-gate  # failed otherwise) Maybe they are behind a firewall, but they
2581*0Sstevel@tonic-gate  # gave us a socksified (or other) ftp program...
2582*0Sstevel@tonic-gate
2583*0Sstevel@tonic-gate  my($i);
2584*0Sstevel@tonic-gate  my($devnull) = $CPAN::Config->{devnull} || "";
2585*0Sstevel@tonic-gate  # < /dev/null ";
2586*0Sstevel@tonic-gate  my($aslocal_dir) = File::Basename::dirname($aslocal);
2587*0Sstevel@tonic-gate  File::Path::mkpath($aslocal_dir);
2588*0Sstevel@tonic-gate  HOSTHARD: for $i (@$host_seq) {
2589*0Sstevel@tonic-gate	my $url = $CPAN::Config->{urllist}[$i] || $CPAN::Defaultsite;
2590*0Sstevel@tonic-gate	$url .= "/" unless substr($url,-1) eq "/";
2591*0Sstevel@tonic-gate	$url .= $file;
2592*0Sstevel@tonic-gate	my($proto,$host,$dir,$getfile);
2593*0Sstevel@tonic-gate
2594*0Sstevel@tonic-gate	# Courtesy Mark Conty mark_conty@cargill.com change from
2595*0Sstevel@tonic-gate	# if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) {
2596*0Sstevel@tonic-gate	# to
2597*0Sstevel@tonic-gate	if ($url =~ m|^([^:]+)://(.*?)/(.*)/(.*)|) {
2598*0Sstevel@tonic-gate	  # proto not yet used
2599*0Sstevel@tonic-gate	  ($proto,$host,$dir,$getfile) = ($1,$2,$3,$4);
2600*0Sstevel@tonic-gate	} else {
2601*0Sstevel@tonic-gate	  next HOSTHARD; # who said, we could ftp anything except ftp?
2602*0Sstevel@tonic-gate	}
2603*0Sstevel@tonic-gate        next HOSTHARD if $proto eq "file"; # file URLs would have had
2604*0Sstevel@tonic-gate                                           # success above. Likely a bogus URL
2605*0Sstevel@tonic-gate
2606*0Sstevel@tonic-gate	$self->debug("localizing funkyftpwise[$url]") if $CPAN::DEBUG;
2607*0Sstevel@tonic-gate	my($f,$funkyftp);
2608*0Sstevel@tonic-gate	for $f ('lynx','ncftpget','ncftp','wget') {
2609*0Sstevel@tonic-gate	  next unless exists $CPAN::Config->{$f};
2610*0Sstevel@tonic-gate	  $funkyftp = $CPAN::Config->{$f};
2611*0Sstevel@tonic-gate	  next unless defined $funkyftp;
2612*0Sstevel@tonic-gate	  next if $funkyftp =~ /^\s*$/;
2613*0Sstevel@tonic-gate	  my($asl_ungz, $asl_gz);
2614*0Sstevel@tonic-gate	  ($asl_ungz = $aslocal) =~ s/\.gz//;
2615*0Sstevel@tonic-gate          $asl_gz = "$asl_ungz.gz";
2616*0Sstevel@tonic-gate	  my($src_switch) = "";
2617*0Sstevel@tonic-gate	  if ($f eq "lynx"){
2618*0Sstevel@tonic-gate	    $src_switch = " -source";
2619*0Sstevel@tonic-gate	  } elsif ($f eq "ncftp"){
2620*0Sstevel@tonic-gate	    $src_switch = " -c";
2621*0Sstevel@tonic-gate          } elsif ($f eq "wget"){
2622*0Sstevel@tonic-gate              $src_switch = " -O -";
2623*0Sstevel@tonic-gate	  }
2624*0Sstevel@tonic-gate	  my($chdir) = "";
2625*0Sstevel@tonic-gate	  my($stdout_redir) = " > $asl_ungz";
2626*0Sstevel@tonic-gate	  if ($f eq "ncftpget"){
2627*0Sstevel@tonic-gate	    $chdir = "cd $aslocal_dir && ";
2628*0Sstevel@tonic-gate	    $stdout_redir = "";
2629*0Sstevel@tonic-gate	  }
2630*0Sstevel@tonic-gate	  $CPAN::Frontend->myprint(
2631*0Sstevel@tonic-gate				   qq[
2632*0Sstevel@tonic-gateTrying with "$funkyftp$src_switch" to get
2633*0Sstevel@tonic-gate    $url
2634*0Sstevel@tonic-gate]);
2635*0Sstevel@tonic-gate	  my($system) =
2636*0Sstevel@tonic-gate	      "$chdir$funkyftp$src_switch \"$url\" $devnull$stdout_redir";
2637*0Sstevel@tonic-gate	  $self->debug("system[$system]") if $CPAN::DEBUG;
2638*0Sstevel@tonic-gate	  my($wstatus);
2639*0Sstevel@tonic-gate	  if (($wstatus = system($system)) == 0
2640*0Sstevel@tonic-gate	      &&
2641*0Sstevel@tonic-gate	      ($f eq "lynx" ?
2642*0Sstevel@tonic-gate	       -s $asl_ungz # lynx returns 0 when it fails somewhere
2643*0Sstevel@tonic-gate	       : 1
2644*0Sstevel@tonic-gate	      )
2645*0Sstevel@tonic-gate	     ) {
2646*0Sstevel@tonic-gate	    if (-s $aslocal) {
2647*0Sstevel@tonic-gate	      # Looks good
2648*0Sstevel@tonic-gate	    } elsif ($asl_ungz ne $aslocal) {
2649*0Sstevel@tonic-gate	      # test gzip integrity
2650*0Sstevel@tonic-gate	      if (CPAN::Tarzip->gtest($asl_ungz)) {
2651*0Sstevel@tonic-gate                  # e.g. foo.tar is gzipped --> foo.tar.gz
2652*0Sstevel@tonic-gate                  rename $asl_ungz, $aslocal;
2653*0Sstevel@tonic-gate	      } else {
2654*0Sstevel@tonic-gate                  CPAN::Tarzip->gzip($asl_ungz,$asl_gz);
2655*0Sstevel@tonic-gate	      }
2656*0Sstevel@tonic-gate	    }
2657*0Sstevel@tonic-gate	    $Thesite = $i;
2658*0Sstevel@tonic-gate	    return $aslocal;
2659*0Sstevel@tonic-gate	  } elsif ($url !~ /\.gz(?!\n)\Z/) {
2660*0Sstevel@tonic-gate	    unlink $asl_ungz if
2661*0Sstevel@tonic-gate		-f $asl_ungz && -s _ == 0;
2662*0Sstevel@tonic-gate	    my $gz = "$aslocal.gz";
2663*0Sstevel@tonic-gate	    my $gzurl = "$url.gz";
2664*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(
2665*0Sstevel@tonic-gate				     qq[
2666*0Sstevel@tonic-gateTrying with "$funkyftp$src_switch" to get
2667*0Sstevel@tonic-gate  $url.gz
2668*0Sstevel@tonic-gate]);
2669*0Sstevel@tonic-gate	    my($system) = "$funkyftp$src_switch \"$url.gz\" $devnull > $asl_gz";
2670*0Sstevel@tonic-gate	    $self->debug("system[$system]") if $CPAN::DEBUG;
2671*0Sstevel@tonic-gate	    my($wstatus);
2672*0Sstevel@tonic-gate	    if (($wstatus = system($system)) == 0
2673*0Sstevel@tonic-gate		&&
2674*0Sstevel@tonic-gate		-s $asl_gz
2675*0Sstevel@tonic-gate	       ) {
2676*0Sstevel@tonic-gate	      # test gzip integrity
2677*0Sstevel@tonic-gate	      if (CPAN::Tarzip->gtest($asl_gz)) {
2678*0Sstevel@tonic-gate                  CPAN::Tarzip->gunzip($asl_gz,$aslocal);
2679*0Sstevel@tonic-gate	      } else {
2680*0Sstevel@tonic-gate                  # somebody uncompressed file for us?
2681*0Sstevel@tonic-gate                  rename $asl_ungz, $aslocal;
2682*0Sstevel@tonic-gate	      }
2683*0Sstevel@tonic-gate	      $Thesite = $i;
2684*0Sstevel@tonic-gate	      return $aslocal;
2685*0Sstevel@tonic-gate	    } else {
2686*0Sstevel@tonic-gate	      unlink $asl_gz if -f $asl_gz;
2687*0Sstevel@tonic-gate	    }
2688*0Sstevel@tonic-gate	  } else {
2689*0Sstevel@tonic-gate	    my $estatus = $wstatus >> 8;
2690*0Sstevel@tonic-gate	    my $size = -f $aslocal ?
2691*0Sstevel@tonic-gate		", left\n$aslocal with size ".-s _ :
2692*0Sstevel@tonic-gate		    "\nWarning: expected file [$aslocal] doesn't exist";
2693*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(qq{
2694*0Sstevel@tonic-gateSystem call "$system"
2695*0Sstevel@tonic-gatereturned status $estatus (wstat $wstatus)$size
2696*0Sstevel@tonic-gate});
2697*0Sstevel@tonic-gate	  }
2698*0Sstevel@tonic-gate          return if $CPAN::Signal;
2699*0Sstevel@tonic-gate	} # lynx,ncftpget,ncftp
2700*0Sstevel@tonic-gate    } # host
2701*0Sstevel@tonic-gate}
2702*0Sstevel@tonic-gate
2703*0Sstevel@tonic-gatesub hosthardest {
2704*0Sstevel@tonic-gate    my($self,$host_seq,$file,$aslocal) = @_;
2705*0Sstevel@tonic-gate
2706*0Sstevel@tonic-gate    my($i);
2707*0Sstevel@tonic-gate    my($aslocal_dir) = File::Basename::dirname($aslocal);
2708*0Sstevel@tonic-gate    File::Path::mkpath($aslocal_dir);
2709*0Sstevel@tonic-gate    my $ftpbin = $CPAN::Config->{ftp};
2710*0Sstevel@tonic-gate  HOSTHARDEST: for $i (@$host_seq) {
2711*0Sstevel@tonic-gate	unless (length $ftpbin && MM->maybe_command($ftpbin)) {
2712*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("No external ftp command available\n\n");
2713*0Sstevel@tonic-gate	    last HOSTHARDEST;
2714*0Sstevel@tonic-gate	}
2715*0Sstevel@tonic-gate	my $url = $CPAN::Config->{urllist}[$i] || $CPAN::Defaultsite;
2716*0Sstevel@tonic-gate	$url .= "/" unless substr($url,-1) eq "/";
2717*0Sstevel@tonic-gate	$url .= $file;
2718*0Sstevel@tonic-gate	$self->debug("localizing ftpwise[$url]") if $CPAN::DEBUG;
2719*0Sstevel@tonic-gate	unless ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) {
2720*0Sstevel@tonic-gate	    next;
2721*0Sstevel@tonic-gate	}
2722*0Sstevel@tonic-gate	my($host,$dir,$getfile) = ($1,$2,$3);
2723*0Sstevel@tonic-gate	my $timestamp = 0;
2724*0Sstevel@tonic-gate	my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
2725*0Sstevel@tonic-gate	   $ctime,$blksize,$blocks) = stat($aslocal);
2726*0Sstevel@tonic-gate	$timestamp = $mtime ||= 0;
2727*0Sstevel@tonic-gate	my($netrc) = CPAN::FTP::netrc->new;
2728*0Sstevel@tonic-gate	my($netrcfile) = $netrc->netrc;
2729*0Sstevel@tonic-gate	my($verbose) = $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG ? " -v" : "";
2730*0Sstevel@tonic-gate	my $targetfile = File::Basename::basename($aslocal);
2731*0Sstevel@tonic-gate	my(@dialog);
2732*0Sstevel@tonic-gate	push(
2733*0Sstevel@tonic-gate	     @dialog,
2734*0Sstevel@tonic-gate	     "lcd $aslocal_dir",
2735*0Sstevel@tonic-gate	     "cd /",
2736*0Sstevel@tonic-gate	     map("cd $_", split /\//, $dir), # RFC 1738
2737*0Sstevel@tonic-gate	     "bin",
2738*0Sstevel@tonic-gate	     "get $getfile $targetfile",
2739*0Sstevel@tonic-gate	     "quit"
2740*0Sstevel@tonic-gate	    );
2741*0Sstevel@tonic-gate	if (! $netrcfile) {
2742*0Sstevel@tonic-gate	    CPAN->debug("No ~/.netrc file found") if $CPAN::DEBUG;
2743*0Sstevel@tonic-gate	} elsif ($netrc->hasdefault || $netrc->contains($host)) {
2744*0Sstevel@tonic-gate	    CPAN->debug(sprintf("hasdef[%d]cont($host)[%d]",
2745*0Sstevel@tonic-gate				$netrc->hasdefault,
2746*0Sstevel@tonic-gate				$netrc->contains($host))) if $CPAN::DEBUG;
2747*0Sstevel@tonic-gate	    if ($netrc->protected) {
2748*0Sstevel@tonic-gate		$CPAN::Frontend->myprint(qq{
2749*0Sstevel@tonic-gate  Trying with external ftp to get
2750*0Sstevel@tonic-gate    $url
2751*0Sstevel@tonic-gate  As this requires some features that are not thoroughly tested, we\'re
2752*0Sstevel@tonic-gate  not sure, that we get it right....
2753*0Sstevel@tonic-gate
2754*0Sstevel@tonic-gate}
2755*0Sstevel@tonic-gate		     );
2756*0Sstevel@tonic-gate		$self->talk_ftp("$ftpbin$verbose $host",
2757*0Sstevel@tonic-gate				@dialog);
2758*0Sstevel@tonic-gate		($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
2759*0Sstevel@tonic-gate		 $atime,$mtime,$ctime,$blksize,$blocks) = stat($aslocal);
2760*0Sstevel@tonic-gate		$mtime ||= 0;
2761*0Sstevel@tonic-gate		if ($mtime > $timestamp) {
2762*0Sstevel@tonic-gate		    $CPAN::Frontend->myprint("GOT $aslocal\n");
2763*0Sstevel@tonic-gate		    $Thesite = $i;
2764*0Sstevel@tonic-gate		    return $aslocal;
2765*0Sstevel@tonic-gate		} else {
2766*0Sstevel@tonic-gate		    $CPAN::Frontend->myprint("Hmm... Still failed!\n");
2767*0Sstevel@tonic-gate		}
2768*0Sstevel@tonic-gate                return if $CPAN::Signal;
2769*0Sstevel@tonic-gate	    } else {
2770*0Sstevel@tonic-gate		$CPAN::Frontend->mywarn(qq{Your $netrcfile is not }.
2771*0Sstevel@tonic-gate					qq{correctly protected.\n});
2772*0Sstevel@tonic-gate	    }
2773*0Sstevel@tonic-gate	} else {
2774*0Sstevel@tonic-gate	    $CPAN::Frontend->mywarn("Your ~/.netrc neither contains $host
2775*0Sstevel@tonic-gate  nor does it have a default entry\n");
2776*0Sstevel@tonic-gate	}
2777*0Sstevel@tonic-gate
2778*0Sstevel@tonic-gate	# OK, they don't have a valid ~/.netrc. Use 'ftp -n'
2779*0Sstevel@tonic-gate	# then and login manually to host, using e-mail as
2780*0Sstevel@tonic-gate	# password.
2781*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(qq{Issuing "$ftpbin$verbose -n"\n});
2782*0Sstevel@tonic-gate	unshift(
2783*0Sstevel@tonic-gate		@dialog,
2784*0Sstevel@tonic-gate		"open $host",
2785*0Sstevel@tonic-gate		"user anonymous $Config::Config{'cf_email'}"
2786*0Sstevel@tonic-gate	       );
2787*0Sstevel@tonic-gate	$self->talk_ftp("$ftpbin$verbose -n", @dialog);
2788*0Sstevel@tonic-gate	($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
2789*0Sstevel@tonic-gate	 $atime,$mtime,$ctime,$blksize,$blocks) = stat($aslocal);
2790*0Sstevel@tonic-gate	$mtime ||= 0;
2791*0Sstevel@tonic-gate	if ($mtime > $timestamp) {
2792*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("GOT $aslocal\n");
2793*0Sstevel@tonic-gate	    $Thesite = $i;
2794*0Sstevel@tonic-gate	    return $aslocal;
2795*0Sstevel@tonic-gate	} else {
2796*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("Bad luck... Still failed!\n");
2797*0Sstevel@tonic-gate	}
2798*0Sstevel@tonic-gate        return if $CPAN::Signal;
2799*0Sstevel@tonic-gate	$CPAN::Frontend->myprint("Can't access URL $url.\n\n");
2800*0Sstevel@tonic-gate	sleep 2;
2801*0Sstevel@tonic-gate    } # host
2802*0Sstevel@tonic-gate}
2803*0Sstevel@tonic-gate
2804*0Sstevel@tonic-gatesub talk_ftp {
2805*0Sstevel@tonic-gate    my($self,$command,@dialog) = @_;
2806*0Sstevel@tonic-gate    my $fh = FileHandle->new;
2807*0Sstevel@tonic-gate    $fh->open("|$command") or die "Couldn't open ftp: $!";
2808*0Sstevel@tonic-gate    foreach (@dialog) { $fh->print("$_\n") }
2809*0Sstevel@tonic-gate    $fh->close;		# Wait for process to complete
2810*0Sstevel@tonic-gate    my $wstatus = $?;
2811*0Sstevel@tonic-gate    my $estatus = $wstatus >> 8;
2812*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(qq{
2813*0Sstevel@tonic-gateSubprocess "|$command"
2814*0Sstevel@tonic-gate  returned status $estatus (wstat $wstatus)
2815*0Sstevel@tonic-gate}) if $wstatus;
2816*0Sstevel@tonic-gate}
2817*0Sstevel@tonic-gate
2818*0Sstevel@tonic-gate# find2perl needs modularization, too, all the following is stolen
2819*0Sstevel@tonic-gate# from there
2820*0Sstevel@tonic-gate# CPAN::FTP::ls
2821*0Sstevel@tonic-gatesub ls {
2822*0Sstevel@tonic-gate    my($self,$name) = @_;
2823*0Sstevel@tonic-gate    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$sizemm,
2824*0Sstevel@tonic-gate     $atime,$mtime,$ctime,$blksize,$blocks) = lstat($name);
2825*0Sstevel@tonic-gate
2826*0Sstevel@tonic-gate    my($perms,%user,%group);
2827*0Sstevel@tonic-gate    my $pname = $name;
2828*0Sstevel@tonic-gate
2829*0Sstevel@tonic-gate    if ($blocks) {
2830*0Sstevel@tonic-gate	$blocks = int(($blocks + 1) / 2);
2831*0Sstevel@tonic-gate    }
2832*0Sstevel@tonic-gate    else {
2833*0Sstevel@tonic-gate	$blocks = int(($sizemm + 1023) / 1024);
2834*0Sstevel@tonic-gate    }
2835*0Sstevel@tonic-gate
2836*0Sstevel@tonic-gate    if    (-f _) { $perms = '-'; }
2837*0Sstevel@tonic-gate    elsif (-d _) { $perms = 'd'; }
2838*0Sstevel@tonic-gate    elsif (-c _) { $perms = 'c'; $sizemm = &sizemm; }
2839*0Sstevel@tonic-gate    elsif (-b _) { $perms = 'b'; $sizemm = &sizemm; }
2840*0Sstevel@tonic-gate    elsif (-p _) { $perms = 'p'; }
2841*0Sstevel@tonic-gate    elsif (-S _) { $perms = 's'; }
2842*0Sstevel@tonic-gate    else         { $perms = 'l'; $pname .= ' -> ' . readlink($_); }
2843*0Sstevel@tonic-gate
2844*0Sstevel@tonic-gate    my(@rwx) = ('---','--x','-w-','-wx','r--','r-x','rw-','rwx');
2845*0Sstevel@tonic-gate    my(@moname) = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
2846*0Sstevel@tonic-gate    my $tmpmode = $mode;
2847*0Sstevel@tonic-gate    my $tmp = $rwx[$tmpmode & 7];
2848*0Sstevel@tonic-gate    $tmpmode >>= 3;
2849*0Sstevel@tonic-gate    $tmp = $rwx[$tmpmode & 7] . $tmp;
2850*0Sstevel@tonic-gate    $tmpmode >>= 3;
2851*0Sstevel@tonic-gate    $tmp = $rwx[$tmpmode & 7] . $tmp;
2852*0Sstevel@tonic-gate    substr($tmp,2,1) =~ tr/-x/Ss/ if -u _;
2853*0Sstevel@tonic-gate    substr($tmp,5,1) =~ tr/-x/Ss/ if -g _;
2854*0Sstevel@tonic-gate    substr($tmp,8,1) =~ tr/-x/Tt/ if -k _;
2855*0Sstevel@tonic-gate    $perms .= $tmp;
2856*0Sstevel@tonic-gate
2857*0Sstevel@tonic-gate    my $user = $user{$uid} || $uid;   # too lazy to implement lookup
2858*0Sstevel@tonic-gate    my $group = $group{$gid} || $gid;
2859*0Sstevel@tonic-gate
2860*0Sstevel@tonic-gate    my($sec,$min,$hour,$mday,$mon,$year) = localtime($mtime);
2861*0Sstevel@tonic-gate    my($timeyear);
2862*0Sstevel@tonic-gate    my($moname) = $moname[$mon];
2863*0Sstevel@tonic-gate    if (-M _ > 365.25 / 2) {
2864*0Sstevel@tonic-gate	$timeyear = $year + 1900;
2865*0Sstevel@tonic-gate    }
2866*0Sstevel@tonic-gate    else {
2867*0Sstevel@tonic-gate	$timeyear = sprintf("%02d:%02d", $hour, $min);
2868*0Sstevel@tonic-gate    }
2869*0Sstevel@tonic-gate
2870*0Sstevel@tonic-gate    sprintf "%5lu %4ld %-10s %2d %-8s %-8s %8s %s %2d %5s %s\n",
2871*0Sstevel@tonic-gate	    $ino,
2872*0Sstevel@tonic-gate		 $blocks,
2873*0Sstevel@tonic-gate		      $perms,
2874*0Sstevel@tonic-gate			    $nlink,
2875*0Sstevel@tonic-gate				$user,
2876*0Sstevel@tonic-gate				     $group,
2877*0Sstevel@tonic-gate					  $sizemm,
2878*0Sstevel@tonic-gate					      $moname,
2879*0Sstevel@tonic-gate						 $mday,
2880*0Sstevel@tonic-gate						     $timeyear,
2881*0Sstevel@tonic-gate							 $pname;
2882*0Sstevel@tonic-gate}
2883*0Sstevel@tonic-gate
2884*0Sstevel@tonic-gatepackage CPAN::FTP::netrc;
2885*0Sstevel@tonic-gate
2886*0Sstevel@tonic-gatesub new {
2887*0Sstevel@tonic-gate    my($class) = @_;
2888*0Sstevel@tonic-gate    my $file = File::Spec->catfile($ENV{HOME},".netrc");
2889*0Sstevel@tonic-gate
2890*0Sstevel@tonic-gate    my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
2891*0Sstevel@tonic-gate       $atime,$mtime,$ctime,$blksize,$blocks)
2892*0Sstevel@tonic-gate	= stat($file);
2893*0Sstevel@tonic-gate    $mode ||= 0;
2894*0Sstevel@tonic-gate    my $protected = 0;
2895*0Sstevel@tonic-gate
2896*0Sstevel@tonic-gate    my($fh,@machines,$hasdefault);
2897*0Sstevel@tonic-gate    $hasdefault = 0;
2898*0Sstevel@tonic-gate    $fh = FileHandle->new or die "Could not create a filehandle";
2899*0Sstevel@tonic-gate
2900*0Sstevel@tonic-gate    if($fh->open($file)){
2901*0Sstevel@tonic-gate	$protected = ($mode & 077) == 0;
2902*0Sstevel@tonic-gate	local($/) = "";
2903*0Sstevel@tonic-gate      NETRC: while (<$fh>) {
2904*0Sstevel@tonic-gate	    my(@tokens) = split " ", $_;
2905*0Sstevel@tonic-gate	  TOKEN: while (@tokens) {
2906*0Sstevel@tonic-gate		my($t) = shift @tokens;
2907*0Sstevel@tonic-gate		if ($t eq "default"){
2908*0Sstevel@tonic-gate		    $hasdefault++;
2909*0Sstevel@tonic-gate		    last NETRC;
2910*0Sstevel@tonic-gate		}
2911*0Sstevel@tonic-gate		last TOKEN if $t eq "macdef";
2912*0Sstevel@tonic-gate		if ($t eq "machine") {
2913*0Sstevel@tonic-gate		    push @machines, shift @tokens;
2914*0Sstevel@tonic-gate		}
2915*0Sstevel@tonic-gate	    }
2916*0Sstevel@tonic-gate	}
2917*0Sstevel@tonic-gate    } else {
2918*0Sstevel@tonic-gate	$file = $hasdefault = $protected = "";
2919*0Sstevel@tonic-gate    }
2920*0Sstevel@tonic-gate
2921*0Sstevel@tonic-gate    bless {
2922*0Sstevel@tonic-gate	   'mach' => [@machines],
2923*0Sstevel@tonic-gate	   'netrc' => $file,
2924*0Sstevel@tonic-gate	   'hasdefault' => $hasdefault,
2925*0Sstevel@tonic-gate	   'protected' => $protected,
2926*0Sstevel@tonic-gate	  }, $class;
2927*0Sstevel@tonic-gate}
2928*0Sstevel@tonic-gate
2929*0Sstevel@tonic-gate# CPAN::FTP::hasdefault;
2930*0Sstevel@tonic-gatesub hasdefault { shift->{'hasdefault'} }
2931*0Sstevel@tonic-gatesub netrc      { shift->{'netrc'}      }
2932*0Sstevel@tonic-gatesub protected  { shift->{'protected'}  }
2933*0Sstevel@tonic-gatesub contains {
2934*0Sstevel@tonic-gate    my($self,$mach) = @_;
2935*0Sstevel@tonic-gate    for ( @{$self->{'mach'}} ) {
2936*0Sstevel@tonic-gate	return 1 if $_ eq $mach;
2937*0Sstevel@tonic-gate    }
2938*0Sstevel@tonic-gate    return 0;
2939*0Sstevel@tonic-gate}
2940*0Sstevel@tonic-gate
2941*0Sstevel@tonic-gatepackage CPAN::Complete;
2942*0Sstevel@tonic-gate
2943*0Sstevel@tonic-gatesub gnu_cpl {
2944*0Sstevel@tonic-gate    my($text, $line, $start, $end) = @_;
2945*0Sstevel@tonic-gate    my(@perlret) = cpl($text, $line, $start);
2946*0Sstevel@tonic-gate    # find longest common match. Can anybody show me how to peruse
2947*0Sstevel@tonic-gate    # T::R::Gnu to have this done automatically? Seems expensive.
2948*0Sstevel@tonic-gate    return () unless @perlret;
2949*0Sstevel@tonic-gate    my($newtext) = $text;
2950*0Sstevel@tonic-gate    for (my $i = length($text)+1;;$i++) {
2951*0Sstevel@tonic-gate	last unless length($perlret[0]) && length($perlret[0]) >= $i;
2952*0Sstevel@tonic-gate	my $try = substr($perlret[0],0,$i);
2953*0Sstevel@tonic-gate	my @tries = grep {substr($_,0,$i) eq $try} @perlret;
2954*0Sstevel@tonic-gate	# warn "try[$try]tries[@tries]";
2955*0Sstevel@tonic-gate	if (@tries == @perlret) {
2956*0Sstevel@tonic-gate	    $newtext = $try;
2957*0Sstevel@tonic-gate	} else {
2958*0Sstevel@tonic-gate	    last;
2959*0Sstevel@tonic-gate	}
2960*0Sstevel@tonic-gate    }
2961*0Sstevel@tonic-gate    ($newtext,@perlret);
2962*0Sstevel@tonic-gate}
2963*0Sstevel@tonic-gate
2964*0Sstevel@tonic-gate#-> sub CPAN::Complete::cpl ;
2965*0Sstevel@tonic-gatesub cpl {
2966*0Sstevel@tonic-gate    my($word,$line,$pos) = @_;
2967*0Sstevel@tonic-gate    $word ||= "";
2968*0Sstevel@tonic-gate    $line ||= "";
2969*0Sstevel@tonic-gate    $pos ||= 0;
2970*0Sstevel@tonic-gate    CPAN->debug("word [$word] line[$line] pos[$pos]") if $CPAN::DEBUG;
2971*0Sstevel@tonic-gate    $line =~ s/^\s*//;
2972*0Sstevel@tonic-gate    if ($line =~ s/^(force\s*)//) {
2973*0Sstevel@tonic-gate	$pos -= length($1);
2974*0Sstevel@tonic-gate    }
2975*0Sstevel@tonic-gate    my @return;
2976*0Sstevel@tonic-gate    if ($pos == 0) {
2977*0Sstevel@tonic-gate	@return = grep /^$word/, @CPAN::Complete::COMMANDS;
2978*0Sstevel@tonic-gate    } elsif ( $line !~ /^[\!abcdghimorutl]/ ) {
2979*0Sstevel@tonic-gate	@return = ();
2980*0Sstevel@tonic-gate    } elsif ($line =~ /^(a|ls)\s/) {
2981*0Sstevel@tonic-gate	@return = cplx('CPAN::Author',uc($word));
2982*0Sstevel@tonic-gate    } elsif ($line =~ /^b\s/) {
2983*0Sstevel@tonic-gate        CPAN::Shell->local_bundles;
2984*0Sstevel@tonic-gate	@return = cplx('CPAN::Bundle',$word);
2985*0Sstevel@tonic-gate    } elsif ($line =~ /^d\s/) {
2986*0Sstevel@tonic-gate	@return = cplx('CPAN::Distribution',$word);
2987*0Sstevel@tonic-gate    } elsif ($line =~ m/^(
2988*0Sstevel@tonic-gate                          [mru]|make|clean|dump|get|test|install|readme|look|cvs_import
2989*0Sstevel@tonic-gate                         )\s/x ) {
2990*0Sstevel@tonic-gate        if ($word =~ /^Bundle::/) {
2991*0Sstevel@tonic-gate            CPAN::Shell->local_bundles;
2992*0Sstevel@tonic-gate        }
2993*0Sstevel@tonic-gate	@return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word));
2994*0Sstevel@tonic-gate    } elsif ($line =~ /^i\s/) {
2995*0Sstevel@tonic-gate	@return = cpl_any($word);
2996*0Sstevel@tonic-gate    } elsif ($line =~ /^reload\s/) {
2997*0Sstevel@tonic-gate	@return = cpl_reload($word,$line,$pos);
2998*0Sstevel@tonic-gate    } elsif ($line =~ /^o\s/) {
2999*0Sstevel@tonic-gate	@return = cpl_option($word,$line,$pos);
3000*0Sstevel@tonic-gate    } elsif ($line =~ m/^\S+\s/ ) {
3001*0Sstevel@tonic-gate        # fallback for future commands and what we have forgotten above
3002*0Sstevel@tonic-gate	@return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word));
3003*0Sstevel@tonic-gate    } else {
3004*0Sstevel@tonic-gate	@return = ();
3005*0Sstevel@tonic-gate    }
3006*0Sstevel@tonic-gate    return @return;
3007*0Sstevel@tonic-gate}
3008*0Sstevel@tonic-gate
3009*0Sstevel@tonic-gate#-> sub CPAN::Complete::cplx ;
3010*0Sstevel@tonic-gatesub cplx {
3011*0Sstevel@tonic-gate    my($class, $word) = @_;
3012*0Sstevel@tonic-gate    # I believed for many years that this was sorted, today I
3013*0Sstevel@tonic-gate    # realized, it wasn't sorted anymore. Now (rev 1.301 / v 1.55) I
3014*0Sstevel@tonic-gate    # make it sorted again. Maybe sort was dropped when GNU-readline
3015*0Sstevel@tonic-gate    # support came in? The RCS file is difficult to read on that:-(
3016*0Sstevel@tonic-gate    sort grep /^\Q$word\E/, map { $_->id } $CPAN::META->all_objects($class);
3017*0Sstevel@tonic-gate}
3018*0Sstevel@tonic-gate
3019*0Sstevel@tonic-gate#-> sub CPAN::Complete::cpl_any ;
3020*0Sstevel@tonic-gatesub cpl_any {
3021*0Sstevel@tonic-gate    my($word) = shift;
3022*0Sstevel@tonic-gate    return (
3023*0Sstevel@tonic-gate	    cplx('CPAN::Author',$word),
3024*0Sstevel@tonic-gate	    cplx('CPAN::Bundle',$word),
3025*0Sstevel@tonic-gate	    cplx('CPAN::Distribution',$word),
3026*0Sstevel@tonic-gate	    cplx('CPAN::Module',$word),
3027*0Sstevel@tonic-gate	   );
3028*0Sstevel@tonic-gate}
3029*0Sstevel@tonic-gate
3030*0Sstevel@tonic-gate#-> sub CPAN::Complete::cpl_reload ;
3031*0Sstevel@tonic-gatesub cpl_reload {
3032*0Sstevel@tonic-gate    my($word,$line,$pos) = @_;
3033*0Sstevel@tonic-gate    $word ||= "";
3034*0Sstevel@tonic-gate    my(@words) = split " ", $line;
3035*0Sstevel@tonic-gate    CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG;
3036*0Sstevel@tonic-gate    my(@ok) = qw(cpan index);
3037*0Sstevel@tonic-gate    return @ok if @words == 1;
3038*0Sstevel@tonic-gate    return grep /^\Q$word\E/, @ok if @words == 2 && $word;
3039*0Sstevel@tonic-gate}
3040*0Sstevel@tonic-gate
3041*0Sstevel@tonic-gate#-> sub CPAN::Complete::cpl_option ;
3042*0Sstevel@tonic-gatesub cpl_option {
3043*0Sstevel@tonic-gate    my($word,$line,$pos) = @_;
3044*0Sstevel@tonic-gate    $word ||= "";
3045*0Sstevel@tonic-gate    my(@words) = split " ", $line;
3046*0Sstevel@tonic-gate    CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG;
3047*0Sstevel@tonic-gate    my(@ok) = qw(conf debug);
3048*0Sstevel@tonic-gate    return @ok if @words == 1;
3049*0Sstevel@tonic-gate    return grep /^\Q$word\E/, @ok if @words == 2 && length($word);
3050*0Sstevel@tonic-gate    if (0) {
3051*0Sstevel@tonic-gate    } elsif ($words[1] eq 'index') {
3052*0Sstevel@tonic-gate	return ();
3053*0Sstevel@tonic-gate    } elsif ($words[1] eq 'conf') {
3054*0Sstevel@tonic-gate	return CPAN::Config::cpl(@_);
3055*0Sstevel@tonic-gate    } elsif ($words[1] eq 'debug') {
3056*0Sstevel@tonic-gate	return sort grep /^\Q$word\E/, sort keys %CPAN::DEBUG, 'all';
3057*0Sstevel@tonic-gate    }
3058*0Sstevel@tonic-gate}
3059*0Sstevel@tonic-gate
3060*0Sstevel@tonic-gatepackage CPAN::Index;
3061*0Sstevel@tonic-gate
3062*0Sstevel@tonic-gate#-> sub CPAN::Index::force_reload ;
3063*0Sstevel@tonic-gatesub force_reload {
3064*0Sstevel@tonic-gate    my($class) = @_;
3065*0Sstevel@tonic-gate    $CPAN::Index::LAST_TIME = 0;
3066*0Sstevel@tonic-gate    $class->reload(1);
3067*0Sstevel@tonic-gate}
3068*0Sstevel@tonic-gate
3069*0Sstevel@tonic-gate#-> sub CPAN::Index::reload ;
3070*0Sstevel@tonic-gatesub reload {
3071*0Sstevel@tonic-gate    my($cl,$force) = @_;
3072*0Sstevel@tonic-gate    my $time = time;
3073*0Sstevel@tonic-gate
3074*0Sstevel@tonic-gate    # XXX check if a newer one is available. (We currently read it
3075*0Sstevel@tonic-gate    # from time to time)
3076*0Sstevel@tonic-gate    for ($CPAN::Config->{index_expire}) {
3077*0Sstevel@tonic-gate	$_ = 0.001 unless $_ && $_ > 0.001;
3078*0Sstevel@tonic-gate    }
3079*0Sstevel@tonic-gate    unless (1 || $CPAN::Have_warned->{readmetadatacache}++) {
3080*0Sstevel@tonic-gate        # debug here when CPAN doesn't seem to read the Metadata
3081*0Sstevel@tonic-gate        require Carp;
3082*0Sstevel@tonic-gate        Carp::cluck("META-PROTOCOL[$CPAN::META->{PROTOCOL}]");
3083*0Sstevel@tonic-gate    }
3084*0Sstevel@tonic-gate    unless ($CPAN::META->{PROTOCOL}) {
3085*0Sstevel@tonic-gate        $cl->read_metadata_cache;
3086*0Sstevel@tonic-gate        $CPAN::META->{PROTOCOL} ||= "1.0";
3087*0Sstevel@tonic-gate    }
3088*0Sstevel@tonic-gate    if ( $CPAN::META->{PROTOCOL} < PROTOCOL  ) {
3089*0Sstevel@tonic-gate        # warn "Setting last_time to 0";
3090*0Sstevel@tonic-gate        $LAST_TIME = 0; # No warning necessary
3091*0Sstevel@tonic-gate    }
3092*0Sstevel@tonic-gate    return if $LAST_TIME + $CPAN::Config->{index_expire}*86400 > $time
3093*0Sstevel@tonic-gate	and ! $force;
3094*0Sstevel@tonic-gate    if (0) {
3095*0Sstevel@tonic-gate        # IFF we are developing, it helps to wipe out the memory
3096*0Sstevel@tonic-gate        # between reloads, otherwise it is not what a user expects.
3097*0Sstevel@tonic-gate        undef $CPAN::META; # Neue Gruendlichkeit since v1.52(r1.274)
3098*0Sstevel@tonic-gate        $CPAN::META = CPAN->new;
3099*0Sstevel@tonic-gate    }
3100*0Sstevel@tonic-gate    {
3101*0Sstevel@tonic-gate        my($debug,$t2);
3102*0Sstevel@tonic-gate        local $LAST_TIME = $time;
3103*0Sstevel@tonic-gate        local $CPAN::META->{PROTOCOL} = PROTOCOL;
3104*0Sstevel@tonic-gate
3105*0Sstevel@tonic-gate        my $needshort = $^O eq "dos";
3106*0Sstevel@tonic-gate
3107*0Sstevel@tonic-gate        $cl->rd_authindex($cl
3108*0Sstevel@tonic-gate                          ->reload_x(
3109*0Sstevel@tonic-gate                                     "authors/01mailrc.txt.gz",
3110*0Sstevel@tonic-gate                                     $needshort ?
3111*0Sstevel@tonic-gate                                     File::Spec->catfile('authors', '01mailrc.gz') :
3112*0Sstevel@tonic-gate                                     File::Spec->catfile('authors', '01mailrc.txt.gz'),
3113*0Sstevel@tonic-gate                                     $force));
3114*0Sstevel@tonic-gate        $t2 = time;
3115*0Sstevel@tonic-gate        $debug = "timing reading 01[".($t2 - $time)."]";
3116*0Sstevel@tonic-gate        $time = $t2;
3117*0Sstevel@tonic-gate        return if $CPAN::Signal; # this is sometimes lengthy
3118*0Sstevel@tonic-gate        $cl->rd_modpacks($cl
3119*0Sstevel@tonic-gate                         ->reload_x(
3120*0Sstevel@tonic-gate                                    "modules/02packages.details.txt.gz",
3121*0Sstevel@tonic-gate                                    $needshort ?
3122*0Sstevel@tonic-gate                                    File::Spec->catfile('modules', '02packag.gz') :
3123*0Sstevel@tonic-gate                                    File::Spec->catfile('modules', '02packages.details.txt.gz'),
3124*0Sstevel@tonic-gate                                    $force));
3125*0Sstevel@tonic-gate        $t2 = time;
3126*0Sstevel@tonic-gate        $debug .= "02[".($t2 - $time)."]";
3127*0Sstevel@tonic-gate        $time = $t2;
3128*0Sstevel@tonic-gate        return if $CPAN::Signal; # this is sometimes lengthy
3129*0Sstevel@tonic-gate        $cl->rd_modlist($cl
3130*0Sstevel@tonic-gate                        ->reload_x(
3131*0Sstevel@tonic-gate                                   "modules/03modlist.data.gz",
3132*0Sstevel@tonic-gate                                   $needshort ?
3133*0Sstevel@tonic-gate                                   File::Spec->catfile('modules', '03mlist.gz') :
3134*0Sstevel@tonic-gate                                   File::Spec->catfile('modules', '03modlist.data.gz'),
3135*0Sstevel@tonic-gate                                   $force));
3136*0Sstevel@tonic-gate        $cl->write_metadata_cache;
3137*0Sstevel@tonic-gate        $t2 = time;
3138*0Sstevel@tonic-gate        $debug .= "03[".($t2 - $time)."]";
3139*0Sstevel@tonic-gate        $time = $t2;
3140*0Sstevel@tonic-gate        CPAN->debug($debug) if $CPAN::DEBUG;
3141*0Sstevel@tonic-gate    }
3142*0Sstevel@tonic-gate    $LAST_TIME = $time;
3143*0Sstevel@tonic-gate    $CPAN::META->{PROTOCOL} = PROTOCOL;
3144*0Sstevel@tonic-gate}
3145*0Sstevel@tonic-gate
3146*0Sstevel@tonic-gate#-> sub CPAN::Index::reload_x ;
3147*0Sstevel@tonic-gatesub reload_x {
3148*0Sstevel@tonic-gate    my($cl,$wanted,$localname,$force) = @_;
3149*0Sstevel@tonic-gate    $force |= 2; # means we're dealing with an index here
3150*0Sstevel@tonic-gate    CPAN::Config->load; # we should guarantee loading wherever we rely
3151*0Sstevel@tonic-gate                        # on Config XXX
3152*0Sstevel@tonic-gate    $localname ||= $wanted;
3153*0Sstevel@tonic-gate    my $abs_wanted = File::Spec->catfile($CPAN::Config->{'keep_source_where'},
3154*0Sstevel@tonic-gate					 $localname);
3155*0Sstevel@tonic-gate    if (
3156*0Sstevel@tonic-gate	-f $abs_wanted &&
3157*0Sstevel@tonic-gate	-M $abs_wanted < $CPAN::Config->{'index_expire'} &&
3158*0Sstevel@tonic-gate	!($force & 1)
3159*0Sstevel@tonic-gate       ) {
3160*0Sstevel@tonic-gate	my $s = $CPAN::Config->{'index_expire'} == 1 ? "" : "s";
3161*0Sstevel@tonic-gate	$cl->debug(qq{$abs_wanted younger than $CPAN::Config->{'index_expire'} }.
3162*0Sstevel@tonic-gate		   qq{day$s. I\'ll use that.});
3163*0Sstevel@tonic-gate	return $abs_wanted;
3164*0Sstevel@tonic-gate    } else {
3165*0Sstevel@tonic-gate	$force |= 1; # means we're quite serious about it.
3166*0Sstevel@tonic-gate    }
3167*0Sstevel@tonic-gate    return CPAN::FTP->localize($wanted,$abs_wanted,$force);
3168*0Sstevel@tonic-gate}
3169*0Sstevel@tonic-gate
3170*0Sstevel@tonic-gate#-> sub CPAN::Index::rd_authindex ;
3171*0Sstevel@tonic-gatesub rd_authindex {
3172*0Sstevel@tonic-gate    my($cl, $index_target) = @_;
3173*0Sstevel@tonic-gate    my @lines;
3174*0Sstevel@tonic-gate    return unless defined $index_target;
3175*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Going to read $index_target\n");
3176*0Sstevel@tonic-gate    local(*FH);
3177*0Sstevel@tonic-gate    tie *FH, CPAN::Tarzip, $index_target;
3178*0Sstevel@tonic-gate    local($/) = "\n";
3179*0Sstevel@tonic-gate    push @lines, split /\012/ while <FH>;
3180*0Sstevel@tonic-gate    foreach (@lines) {
3181*0Sstevel@tonic-gate	my($userid,$fullname,$email) =
3182*0Sstevel@tonic-gate	    m/alias\s+(\S+)\s+\"([^\"\<]+)\s+\<([^\>]+)\>\"/;
3183*0Sstevel@tonic-gate	next unless $userid && $fullname && $email;
3184*0Sstevel@tonic-gate
3185*0Sstevel@tonic-gate	# instantiate an author object
3186*0Sstevel@tonic-gate 	my $userobj = $CPAN::META->instance('CPAN::Author',$userid);
3187*0Sstevel@tonic-gate	$userobj->set('FULLNAME' => $fullname, 'EMAIL' => $email);
3188*0Sstevel@tonic-gate	return if $CPAN::Signal;
3189*0Sstevel@tonic-gate    }
3190*0Sstevel@tonic-gate}
3191*0Sstevel@tonic-gate
3192*0Sstevel@tonic-gatesub userid {
3193*0Sstevel@tonic-gate  my($self,$dist) = @_;
3194*0Sstevel@tonic-gate  $dist = $self->{'id'} unless defined $dist;
3195*0Sstevel@tonic-gate  my($ret) = $dist =~ m|(?:\w/\w\w/)?([^/]+)/|;
3196*0Sstevel@tonic-gate  $ret;
3197*0Sstevel@tonic-gate}
3198*0Sstevel@tonic-gate
3199*0Sstevel@tonic-gate#-> sub CPAN::Index::rd_modpacks ;
3200*0Sstevel@tonic-gatesub rd_modpacks {
3201*0Sstevel@tonic-gate    my($self, $index_target) = @_;
3202*0Sstevel@tonic-gate    my @lines;
3203*0Sstevel@tonic-gate    return unless defined $index_target;
3204*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Going to read $index_target\n");
3205*0Sstevel@tonic-gate    my $fh = CPAN::Tarzip->TIEHANDLE($index_target);
3206*0Sstevel@tonic-gate    local($/) = "\n";
3207*0Sstevel@tonic-gate    while ($_ = $fh->READLINE) {
3208*0Sstevel@tonic-gate	s/\012/\n/g;
3209*0Sstevel@tonic-gate	my @ls = map {"$_\n"} split /\n/, $_;
3210*0Sstevel@tonic-gate	unshift @ls, "\n" x length($1) if /^(\n+)/;
3211*0Sstevel@tonic-gate	push @lines, @ls;
3212*0Sstevel@tonic-gate    }
3213*0Sstevel@tonic-gate    # read header
3214*0Sstevel@tonic-gate    my($line_count,$last_updated);
3215*0Sstevel@tonic-gate    while (@lines) {
3216*0Sstevel@tonic-gate	my $shift = shift(@lines);
3217*0Sstevel@tonic-gate	last if $shift =~ /^\s*$/;
3218*0Sstevel@tonic-gate	$shift =~ /^Line-Count:\s+(\d+)/ and $line_count = $1;
3219*0Sstevel@tonic-gate        $shift =~ /^Last-Updated:\s+(.+)/ and $last_updated = $1;
3220*0Sstevel@tonic-gate    }
3221*0Sstevel@tonic-gate    if (not defined $line_count) {
3222*0Sstevel@tonic-gate
3223*0Sstevel@tonic-gate	warn qq{Warning: Your $index_target does not contain a Line-Count header.
3224*0Sstevel@tonic-gatePlease check the validity of the index file by comparing it to more
3225*0Sstevel@tonic-gatethan one CPAN mirror. I'll continue but problems seem likely to
3226*0Sstevel@tonic-gatehappen.\a
3227*0Sstevel@tonic-gate};
3228*0Sstevel@tonic-gate
3229*0Sstevel@tonic-gate	sleep 5;
3230*0Sstevel@tonic-gate    } elsif ($line_count != scalar @lines) {
3231*0Sstevel@tonic-gate
3232*0Sstevel@tonic-gate	warn sprintf qq{Warning: Your %s
3233*0Sstevel@tonic-gatecontains a Line-Count header of %d but I see %d lines there. Please
3234*0Sstevel@tonic-gatecheck the validity of the index file by comparing it to more than one
3235*0Sstevel@tonic-gateCPAN mirror. I'll continue but problems seem likely to happen.\a\n},
3236*0Sstevel@tonic-gate$index_target, $line_count, scalar(@lines);
3237*0Sstevel@tonic-gate
3238*0Sstevel@tonic-gate    }
3239*0Sstevel@tonic-gate    if (not defined $last_updated) {
3240*0Sstevel@tonic-gate
3241*0Sstevel@tonic-gate	warn qq{Warning: Your $index_target does not contain a Last-Updated header.
3242*0Sstevel@tonic-gatePlease check the validity of the index file by comparing it to more
3243*0Sstevel@tonic-gatethan one CPAN mirror. I'll continue but problems seem likely to
3244*0Sstevel@tonic-gatehappen.\a
3245*0Sstevel@tonic-gate};
3246*0Sstevel@tonic-gate
3247*0Sstevel@tonic-gate	sleep 5;
3248*0Sstevel@tonic-gate    } else {
3249*0Sstevel@tonic-gate
3250*0Sstevel@tonic-gate	$CPAN::Frontend
3251*0Sstevel@tonic-gate            ->myprint(sprintf qq{  Database was generated on %s\n},
3252*0Sstevel@tonic-gate                      $last_updated);
3253*0Sstevel@tonic-gate        $DATE_OF_02 = $last_updated;
3254*0Sstevel@tonic-gate
3255*0Sstevel@tonic-gate        if ($CPAN::META->has_inst(HTTP::Date)) {
3256*0Sstevel@tonic-gate            require HTTP::Date;
3257*0Sstevel@tonic-gate            my($age) = (time - HTTP::Date::str2time($last_updated))/3600/24;
3258*0Sstevel@tonic-gate            if ($age > 30) {
3259*0Sstevel@tonic-gate
3260*0Sstevel@tonic-gate                $CPAN::Frontend
3261*0Sstevel@tonic-gate                    ->mywarn(sprintf
3262*0Sstevel@tonic-gate                             qq{Warning: This index file is %d days old.
3263*0Sstevel@tonic-gate  Please check the host you chose as your CPAN mirror for staleness.
3264*0Sstevel@tonic-gate  I'll continue but problems seem likely to happen.\a\n},
3265*0Sstevel@tonic-gate                             $age);
3266*0Sstevel@tonic-gate
3267*0Sstevel@tonic-gate            }
3268*0Sstevel@tonic-gate        } else {
3269*0Sstevel@tonic-gate            $CPAN::Frontend->myprint("  HTTP::Date not available\n");
3270*0Sstevel@tonic-gate        }
3271*0Sstevel@tonic-gate    }
3272*0Sstevel@tonic-gate
3273*0Sstevel@tonic-gate
3274*0Sstevel@tonic-gate    # A necessity since we have metadata_cache: delete what isn't
3275*0Sstevel@tonic-gate    # there anymore
3276*0Sstevel@tonic-gate    my $secondtime = $CPAN::META->exists("CPAN::Module","CPAN");
3277*0Sstevel@tonic-gate    CPAN->debug("secondtime[$secondtime]") if $CPAN::DEBUG;
3278*0Sstevel@tonic-gate    my(%exists);
3279*0Sstevel@tonic-gate    foreach (@lines) {
3280*0Sstevel@tonic-gate	chomp;
3281*0Sstevel@tonic-gate        # before 1.56 we split into 3 and discarded the rest. From
3282*0Sstevel@tonic-gate        # 1.57 we assign remaining text to $comment thus allowing to
3283*0Sstevel@tonic-gate        # influence isa_perl
3284*0Sstevel@tonic-gate	my($mod,$version,$dist,$comment) = split " ", $_, 4;
3285*0Sstevel@tonic-gate	my($bundle,$id,$userid);
3286*0Sstevel@tonic-gate
3287*0Sstevel@tonic-gate	if ($mod eq 'CPAN' &&
3288*0Sstevel@tonic-gate	    ! (
3289*0Sstevel@tonic-gate	       CPAN::Queue->exists('Bundle::CPAN') ||
3290*0Sstevel@tonic-gate	       CPAN::Queue->exists('CPAN')
3291*0Sstevel@tonic-gate	      )
3292*0Sstevel@tonic-gate	   ) {
3293*0Sstevel@tonic-gate            local($^W)= 0;
3294*0Sstevel@tonic-gate            if ($version > $CPAN::VERSION){
3295*0Sstevel@tonic-gate                $CPAN::Frontend->myprint(qq{
3296*0Sstevel@tonic-gate  There's a new CPAN.pm version (v$version) available!
3297*0Sstevel@tonic-gate  [Current version is v$CPAN::VERSION]
3298*0Sstevel@tonic-gate  You might want to try
3299*0Sstevel@tonic-gate    install Bundle::CPAN
3300*0Sstevel@tonic-gate    reload cpan
3301*0Sstevel@tonic-gate  without quitting the current session. It should be a seamless upgrade
3302*0Sstevel@tonic-gate  while we are running...
3303*0Sstevel@tonic-gate}); #});
3304*0Sstevel@tonic-gate                sleep 2;
3305*0Sstevel@tonic-gate		$CPAN::Frontend->myprint(qq{\n});
3306*0Sstevel@tonic-gate	    }
3307*0Sstevel@tonic-gate	    last if $CPAN::Signal;
3308*0Sstevel@tonic-gate	} elsif ($mod =~ /^Bundle::(.*)/) {
3309*0Sstevel@tonic-gate	    $bundle = $1;
3310*0Sstevel@tonic-gate	}
3311*0Sstevel@tonic-gate
3312*0Sstevel@tonic-gate	if ($bundle){
3313*0Sstevel@tonic-gate	    $id =  $CPAN::META->instance('CPAN::Bundle',$mod);
3314*0Sstevel@tonic-gate	    # Let's make it a module too, because bundles have so much
3315*0Sstevel@tonic-gate	    # in common with modules.
3316*0Sstevel@tonic-gate
3317*0Sstevel@tonic-gate            # Changed in 1.57_63: seems like memory bloat now without
3318*0Sstevel@tonic-gate            # any value, so commented out
3319*0Sstevel@tonic-gate
3320*0Sstevel@tonic-gate	    # $CPAN::META->instance('CPAN::Module',$mod);
3321*0Sstevel@tonic-gate
3322*0Sstevel@tonic-gate	} else {
3323*0Sstevel@tonic-gate
3324*0Sstevel@tonic-gate	    # instantiate a module object
3325*0Sstevel@tonic-gate	    $id = $CPAN::META->instance('CPAN::Module',$mod);
3326*0Sstevel@tonic-gate
3327*0Sstevel@tonic-gate	}
3328*0Sstevel@tonic-gate
3329*0Sstevel@tonic-gate	if ($id->cpan_file ne $dist){ # update only if file is
3330*0Sstevel@tonic-gate                                      # different. CPAN prohibits same
3331*0Sstevel@tonic-gate                                      # name with different version
3332*0Sstevel@tonic-gate	    $userid = $id->userid || $self->userid($dist);
3333*0Sstevel@tonic-gate	    $id->set(
3334*0Sstevel@tonic-gate		     'CPAN_USERID' => $userid,
3335*0Sstevel@tonic-gate		     'CPAN_VERSION' => $version,
3336*0Sstevel@tonic-gate		     'CPAN_FILE' => $dist,
3337*0Sstevel@tonic-gate		    );
3338*0Sstevel@tonic-gate	}
3339*0Sstevel@tonic-gate
3340*0Sstevel@tonic-gate	# instantiate a distribution object
3341*0Sstevel@tonic-gate	if ($CPAN::META->exists('CPAN::Distribution',$dist)) {
3342*0Sstevel@tonic-gate	  # we do not need CONTAINSMODS unless we do something with
3343*0Sstevel@tonic-gate	  # this dist, so we better produce it on demand.
3344*0Sstevel@tonic-gate
3345*0Sstevel@tonic-gate	  ## my $obj = $CPAN::META->instance(
3346*0Sstevel@tonic-gate	  ## 				  'CPAN::Distribution' => $dist
3347*0Sstevel@tonic-gate	  ## 				 );
3348*0Sstevel@tonic-gate	  ## $obj->{CONTAINSMODS}{$mod} = undef; # experimental
3349*0Sstevel@tonic-gate	} else {
3350*0Sstevel@tonic-gate	  $CPAN::META->instance(
3351*0Sstevel@tonic-gate				'CPAN::Distribution' => $dist
3352*0Sstevel@tonic-gate			       )->set(
3353*0Sstevel@tonic-gate				      'CPAN_USERID' => $userid,
3354*0Sstevel@tonic-gate                                      'CPAN_COMMENT' => $comment,
3355*0Sstevel@tonic-gate				     );
3356*0Sstevel@tonic-gate	}
3357*0Sstevel@tonic-gate        if ($secondtime) {
3358*0Sstevel@tonic-gate            for my $name ($mod,$dist) {
3359*0Sstevel@tonic-gate                CPAN->debug("exists name[$name]") if $CPAN::DEBUG;
3360*0Sstevel@tonic-gate                $exists{$name} = undef;
3361*0Sstevel@tonic-gate            }
3362*0Sstevel@tonic-gate        }
3363*0Sstevel@tonic-gate	return if $CPAN::Signal;
3364*0Sstevel@tonic-gate    }
3365*0Sstevel@tonic-gate    undef $fh;
3366*0Sstevel@tonic-gate    if ($secondtime) {
3367*0Sstevel@tonic-gate        for my $class (qw(CPAN::Module CPAN::Bundle CPAN::Distribution)) {
3368*0Sstevel@tonic-gate            for my $o ($CPAN::META->all_objects($class)) {
3369*0Sstevel@tonic-gate                next if exists $exists{$o->{ID}};
3370*0Sstevel@tonic-gate                $CPAN::META->delete($class,$o->{ID});
3371*0Sstevel@tonic-gate                CPAN->debug("deleting ID[$o->{ID}] in class[$class]")
3372*0Sstevel@tonic-gate                    if $CPAN::DEBUG;
3373*0Sstevel@tonic-gate            }
3374*0Sstevel@tonic-gate        }
3375*0Sstevel@tonic-gate    }
3376*0Sstevel@tonic-gate}
3377*0Sstevel@tonic-gate
3378*0Sstevel@tonic-gate#-> sub CPAN::Index::rd_modlist ;
3379*0Sstevel@tonic-gatesub rd_modlist {
3380*0Sstevel@tonic-gate    my($cl,$index_target) = @_;
3381*0Sstevel@tonic-gate    return unless defined $index_target;
3382*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Going to read $index_target\n");
3383*0Sstevel@tonic-gate    my $fh = CPAN::Tarzip->TIEHANDLE($index_target);
3384*0Sstevel@tonic-gate    my @eval;
3385*0Sstevel@tonic-gate    local($/) = "\n";
3386*0Sstevel@tonic-gate    while ($_ = $fh->READLINE) {
3387*0Sstevel@tonic-gate	s/\012/\n/g;
3388*0Sstevel@tonic-gate	my @ls = map {"$_\n"} split /\n/, $_;
3389*0Sstevel@tonic-gate	unshift @ls, "\n" x length($1) if /^(\n+)/;
3390*0Sstevel@tonic-gate	push @eval, @ls;
3391*0Sstevel@tonic-gate    }
3392*0Sstevel@tonic-gate    while (@eval) {
3393*0Sstevel@tonic-gate	my $shift = shift(@eval);
3394*0Sstevel@tonic-gate	if ($shift =~ /^Date:\s+(.*)/){
3395*0Sstevel@tonic-gate	    return if $DATE_OF_03 eq $1;
3396*0Sstevel@tonic-gate	    ($DATE_OF_03) = $1;
3397*0Sstevel@tonic-gate	}
3398*0Sstevel@tonic-gate	last if $shift =~ /^\s*$/;
3399*0Sstevel@tonic-gate    }
3400*0Sstevel@tonic-gate    undef $fh;
3401*0Sstevel@tonic-gate    push @eval, q{CPAN::Modulelist->data;};
3402*0Sstevel@tonic-gate    local($^W) = 0;
3403*0Sstevel@tonic-gate    my($comp) = Safe->new("CPAN::Safe1");
3404*0Sstevel@tonic-gate    my($eval) = join("", @eval);
3405*0Sstevel@tonic-gate    my $ret = $comp->reval($eval);
3406*0Sstevel@tonic-gate    Carp::confess($@) if $@;
3407*0Sstevel@tonic-gate    return if $CPAN::Signal;
3408*0Sstevel@tonic-gate    for (keys %$ret) {
3409*0Sstevel@tonic-gate	my $obj = $CPAN::META->instance("CPAN::Module",$_);
3410*0Sstevel@tonic-gate        delete $ret->{$_}{modid}; # not needed here, maybe elsewhere
3411*0Sstevel@tonic-gate	$obj->set(%{$ret->{$_}});
3412*0Sstevel@tonic-gate	return if $CPAN::Signal;
3413*0Sstevel@tonic-gate    }
3414*0Sstevel@tonic-gate}
3415*0Sstevel@tonic-gate
3416*0Sstevel@tonic-gate#-> sub CPAN::Index::write_metadata_cache ;
3417*0Sstevel@tonic-gatesub write_metadata_cache {
3418*0Sstevel@tonic-gate    my($self) = @_;
3419*0Sstevel@tonic-gate    return unless $CPAN::Config->{'cache_metadata'};
3420*0Sstevel@tonic-gate    return unless $CPAN::META->has_usable("Storable");
3421*0Sstevel@tonic-gate    my $cache;
3422*0Sstevel@tonic-gate    foreach my $k (qw(CPAN::Bundle CPAN::Author CPAN::Module
3423*0Sstevel@tonic-gate		      CPAN::Distribution)) {
3424*0Sstevel@tonic-gate	$cache->{$k} = $CPAN::META->{readonly}{$k}; # unsafe meta access, ok
3425*0Sstevel@tonic-gate    }
3426*0Sstevel@tonic-gate    my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata");
3427*0Sstevel@tonic-gate    $cache->{last_time} = $LAST_TIME;
3428*0Sstevel@tonic-gate    $cache->{DATE_OF_02} = $DATE_OF_02;
3429*0Sstevel@tonic-gate    $cache->{PROTOCOL} = PROTOCOL;
3430*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Going to write $metadata_file\n");
3431*0Sstevel@tonic-gate    eval { Storable::nstore($cache, $metadata_file) };
3432*0Sstevel@tonic-gate    $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ??
3433*0Sstevel@tonic-gate}
3434*0Sstevel@tonic-gate
3435*0Sstevel@tonic-gate#-> sub CPAN::Index::read_metadata_cache ;
3436*0Sstevel@tonic-gatesub read_metadata_cache {
3437*0Sstevel@tonic-gate    my($self) = @_;
3438*0Sstevel@tonic-gate    return unless $CPAN::Config->{'cache_metadata'};
3439*0Sstevel@tonic-gate    return unless $CPAN::META->has_usable("Storable");
3440*0Sstevel@tonic-gate    my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata");
3441*0Sstevel@tonic-gate    return unless -r $metadata_file and -f $metadata_file;
3442*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Going to read $metadata_file\n");
3443*0Sstevel@tonic-gate    my $cache;
3444*0Sstevel@tonic-gate    eval { $cache = Storable::retrieve($metadata_file) };
3445*0Sstevel@tonic-gate    $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ??
3446*0Sstevel@tonic-gate    if (!$cache || ref $cache ne 'HASH'){
3447*0Sstevel@tonic-gate        $LAST_TIME = 0;
3448*0Sstevel@tonic-gate        return;
3449*0Sstevel@tonic-gate    }
3450*0Sstevel@tonic-gate    if (exists $cache->{PROTOCOL}) {
3451*0Sstevel@tonic-gate        if (PROTOCOL > $cache->{PROTOCOL}) {
3452*0Sstevel@tonic-gate            $CPAN::Frontend->mywarn(sprintf("Ignoring Metadata cache written ".
3453*0Sstevel@tonic-gate                                            "with protocol v%s, requiring v%s\n",
3454*0Sstevel@tonic-gate                                            $cache->{PROTOCOL},
3455*0Sstevel@tonic-gate                                            PROTOCOL)
3456*0Sstevel@tonic-gate                                   );
3457*0Sstevel@tonic-gate            return;
3458*0Sstevel@tonic-gate        }
3459*0Sstevel@tonic-gate    } else {
3460*0Sstevel@tonic-gate        $CPAN::Frontend->mywarn("Ignoring Metadata cache written ".
3461*0Sstevel@tonic-gate                                "with protocol v1.0\n");
3462*0Sstevel@tonic-gate        return;
3463*0Sstevel@tonic-gate    }
3464*0Sstevel@tonic-gate    my $clcnt = 0;
3465*0Sstevel@tonic-gate    my $idcnt = 0;
3466*0Sstevel@tonic-gate    while(my($class,$v) = each %$cache) {
3467*0Sstevel@tonic-gate	next unless $class =~ /^CPAN::/;
3468*0Sstevel@tonic-gate	$CPAN::META->{readonly}{$class} = $v; # unsafe meta access, ok
3469*0Sstevel@tonic-gate        while (my($id,$ro) = each %$v) {
3470*0Sstevel@tonic-gate            $CPAN::META->{readwrite}{$class}{$id} ||=
3471*0Sstevel@tonic-gate                $class->new(ID=>$id, RO=>$ro);
3472*0Sstevel@tonic-gate            $idcnt++;
3473*0Sstevel@tonic-gate        }
3474*0Sstevel@tonic-gate        $clcnt++;
3475*0Sstevel@tonic-gate    }
3476*0Sstevel@tonic-gate    unless ($clcnt) { # sanity check
3477*0Sstevel@tonic-gate        $CPAN::Frontend->myprint("Warning: Found no data in $metadata_file\n");
3478*0Sstevel@tonic-gate        return;
3479*0Sstevel@tonic-gate    }
3480*0Sstevel@tonic-gate    if ($idcnt < 1000) {
3481*0Sstevel@tonic-gate        $CPAN::Frontend->myprint("Warning: Found only $idcnt objects ".
3482*0Sstevel@tonic-gate                                 "in $metadata_file\n");
3483*0Sstevel@tonic-gate        return;
3484*0Sstevel@tonic-gate    }
3485*0Sstevel@tonic-gate    $CPAN::META->{PROTOCOL} ||=
3486*0Sstevel@tonic-gate        $cache->{PROTOCOL}; # reading does not up or downgrade, but it
3487*0Sstevel@tonic-gate                            # does initialize to some protocol
3488*0Sstevel@tonic-gate    $LAST_TIME = $cache->{last_time};
3489*0Sstevel@tonic-gate    $DATE_OF_02 = $cache->{DATE_OF_02};
3490*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("  Database was generated on $DATE_OF_02\n")
3491*0Sstevel@tonic-gate	if defined $DATE_OF_02; # An old cache may not contain DATE_OF_02
3492*0Sstevel@tonic-gate    return;
3493*0Sstevel@tonic-gate}
3494*0Sstevel@tonic-gate
3495*0Sstevel@tonic-gatepackage CPAN::InfoObj;
3496*0Sstevel@tonic-gate
3497*0Sstevel@tonic-gate# Accessors
3498*0Sstevel@tonic-gatesub cpan_userid {
3499*0Sstevel@tonic-gate    my $self = shift;
3500*0Sstevel@tonic-gate    $self->{RO}{CPAN_USERID}
3501*0Sstevel@tonic-gate}
3502*0Sstevel@tonic-gate
3503*0Sstevel@tonic-gatesub id { shift->{ID}; }
3504*0Sstevel@tonic-gate
3505*0Sstevel@tonic-gate#-> sub CPAN::InfoObj::new ;
3506*0Sstevel@tonic-gatesub new {
3507*0Sstevel@tonic-gate    my $this = bless {}, shift;
3508*0Sstevel@tonic-gate    %$this = @_;
3509*0Sstevel@tonic-gate    $this
3510*0Sstevel@tonic-gate}
3511*0Sstevel@tonic-gate
3512*0Sstevel@tonic-gate# The set method may only be used by code that reads index data or
3513*0Sstevel@tonic-gate# otherwise "objective" data from the outside world. All session
3514*0Sstevel@tonic-gate# related material may do anything else with instance variables but
3515*0Sstevel@tonic-gate# must not touch the hash under the RO attribute. The reason is that
3516*0Sstevel@tonic-gate# the RO hash gets written to Metadata file and is thus persistent.
3517*0Sstevel@tonic-gate
3518*0Sstevel@tonic-gate#-> sub CPAN::InfoObj::set ;
3519*0Sstevel@tonic-gatesub set {
3520*0Sstevel@tonic-gate    my($self,%att) = @_;
3521*0Sstevel@tonic-gate    my $class = ref $self;
3522*0Sstevel@tonic-gate
3523*0Sstevel@tonic-gate    # This must be ||=, not ||, because only if we write an empty
3524*0Sstevel@tonic-gate    # reference, only then the set method will write into the readonly
3525*0Sstevel@tonic-gate    # area. But for Distributions that spring into existence, maybe
3526*0Sstevel@tonic-gate    # because of a typo, we do not like it that they are written into
3527*0Sstevel@tonic-gate    # the readonly area and made permanent (at least for a while) and
3528*0Sstevel@tonic-gate    # that is why we do not "allow" other places to call ->set.
3529*0Sstevel@tonic-gate    unless ($self->id) {
3530*0Sstevel@tonic-gate        CPAN->debug("Bug? Empty ID, rejecting");
3531*0Sstevel@tonic-gate        return;
3532*0Sstevel@tonic-gate    }
3533*0Sstevel@tonic-gate    my $ro = $self->{RO} =
3534*0Sstevel@tonic-gate        $CPAN::META->{readonly}{$class}{$self->id} ||= {};
3535*0Sstevel@tonic-gate
3536*0Sstevel@tonic-gate    while (my($k,$v) = each %att) {
3537*0Sstevel@tonic-gate        $ro->{$k} = $v;
3538*0Sstevel@tonic-gate    }
3539*0Sstevel@tonic-gate}
3540*0Sstevel@tonic-gate
3541*0Sstevel@tonic-gate#-> sub CPAN::InfoObj::as_glimpse ;
3542*0Sstevel@tonic-gatesub as_glimpse {
3543*0Sstevel@tonic-gate    my($self) = @_;
3544*0Sstevel@tonic-gate    my(@m);
3545*0Sstevel@tonic-gate    my $class = ref($self);
3546*0Sstevel@tonic-gate    $class =~ s/^CPAN:://;
3547*0Sstevel@tonic-gate    push @m, sprintf "%-15s %s\n", $class, $self->{ID};
3548*0Sstevel@tonic-gate    join "", @m;
3549*0Sstevel@tonic-gate}
3550*0Sstevel@tonic-gate
3551*0Sstevel@tonic-gate#-> sub CPAN::InfoObj::as_string ;
3552*0Sstevel@tonic-gatesub as_string {
3553*0Sstevel@tonic-gate    my($self) = @_;
3554*0Sstevel@tonic-gate    my(@m);
3555*0Sstevel@tonic-gate    my $class = ref($self);
3556*0Sstevel@tonic-gate    $class =~ s/^CPAN:://;
3557*0Sstevel@tonic-gate    push @m, $class, " id = $self->{ID}\n";
3558*0Sstevel@tonic-gate    for (sort keys %{$self->{RO}}) {
3559*0Sstevel@tonic-gate	# next if m/^(ID|RO)$/;
3560*0Sstevel@tonic-gate	my $extra = "";
3561*0Sstevel@tonic-gate	if ($_ eq "CPAN_USERID") {
3562*0Sstevel@tonic-gate            $extra .= " (".$self->author;
3563*0Sstevel@tonic-gate            my $email; # old perls!
3564*0Sstevel@tonic-gate            if ($email = $CPAN::META->instance("CPAN::Author",
3565*0Sstevel@tonic-gate                                               $self->cpan_userid
3566*0Sstevel@tonic-gate                                              )->email) {
3567*0Sstevel@tonic-gate                $extra .= " <$email>";
3568*0Sstevel@tonic-gate            } else {
3569*0Sstevel@tonic-gate                $extra .= " <no email>";
3570*0Sstevel@tonic-gate            }
3571*0Sstevel@tonic-gate            $extra .= ")";
3572*0Sstevel@tonic-gate        } elsif ($_ eq "FULLNAME") { # potential UTF-8 conversion
3573*0Sstevel@tonic-gate            push @m, sprintf "    %-12s %s\n", $_, $self->fullname;
3574*0Sstevel@tonic-gate            next;
3575*0Sstevel@tonic-gate        }
3576*0Sstevel@tonic-gate        next unless defined $self->{RO}{$_};
3577*0Sstevel@tonic-gate        push @m, sprintf "    %-12s %s%s\n", $_, $self->{RO}{$_}, $extra;
3578*0Sstevel@tonic-gate    }
3579*0Sstevel@tonic-gate    for (sort keys %$self) {
3580*0Sstevel@tonic-gate	next if m/^(ID|RO)$/;
3581*0Sstevel@tonic-gate	if (ref($self->{$_}) eq "ARRAY") {
3582*0Sstevel@tonic-gate	  push @m, sprintf "    %-12s %s\n", $_, "@{$self->{$_}}";
3583*0Sstevel@tonic-gate	} elsif (ref($self->{$_}) eq "HASH") {
3584*0Sstevel@tonic-gate	  push @m, sprintf(
3585*0Sstevel@tonic-gate			   "    %-12s %s\n",
3586*0Sstevel@tonic-gate			   $_,
3587*0Sstevel@tonic-gate			   join(" ",keys %{$self->{$_}}),
3588*0Sstevel@tonic-gate                          );
3589*0Sstevel@tonic-gate	} else {
3590*0Sstevel@tonic-gate	  push @m, sprintf "    %-12s %s\n", $_, $self->{$_};
3591*0Sstevel@tonic-gate	}
3592*0Sstevel@tonic-gate    }
3593*0Sstevel@tonic-gate    join "", @m, "\n";
3594*0Sstevel@tonic-gate}
3595*0Sstevel@tonic-gate
3596*0Sstevel@tonic-gate#-> sub CPAN::InfoObj::author ;
3597*0Sstevel@tonic-gatesub author {
3598*0Sstevel@tonic-gate    my($self) = @_;
3599*0Sstevel@tonic-gate    $CPAN::META->instance("CPAN::Author",$self->cpan_userid)->fullname;
3600*0Sstevel@tonic-gate}
3601*0Sstevel@tonic-gate
3602*0Sstevel@tonic-gate#-> sub CPAN::InfoObj::dump ;
3603*0Sstevel@tonic-gatesub dump {
3604*0Sstevel@tonic-gate  my($self) = @_;
3605*0Sstevel@tonic-gate  require Data::Dumper;
3606*0Sstevel@tonic-gate  print Data::Dumper::Dumper($self);
3607*0Sstevel@tonic-gate}
3608*0Sstevel@tonic-gate
3609*0Sstevel@tonic-gatepackage CPAN::Author;
3610*0Sstevel@tonic-gate
3611*0Sstevel@tonic-gate#-> sub CPAN::Author::id
3612*0Sstevel@tonic-gatesub id {
3613*0Sstevel@tonic-gate    my $self = shift;
3614*0Sstevel@tonic-gate    my $id = $self->{ID};
3615*0Sstevel@tonic-gate    $CPAN::Frontend->mydie("Illegal author id[$id]") unless $id =~ /^[A-Z]/;
3616*0Sstevel@tonic-gate    $id;
3617*0Sstevel@tonic-gate}
3618*0Sstevel@tonic-gate
3619*0Sstevel@tonic-gate#-> sub CPAN::Author::as_glimpse ;
3620*0Sstevel@tonic-gatesub as_glimpse {
3621*0Sstevel@tonic-gate    my($self) = @_;
3622*0Sstevel@tonic-gate    my(@m);
3623*0Sstevel@tonic-gate    my $class = ref($self);
3624*0Sstevel@tonic-gate    $class =~ s/^CPAN:://;
3625*0Sstevel@tonic-gate    push @m, sprintf(qq{%-15s %s ("%s" <%s>)\n},
3626*0Sstevel@tonic-gate                     $class,
3627*0Sstevel@tonic-gate                     $self->{ID},
3628*0Sstevel@tonic-gate                     $self->fullname,
3629*0Sstevel@tonic-gate                     $self->email);
3630*0Sstevel@tonic-gate    join "", @m;
3631*0Sstevel@tonic-gate}
3632*0Sstevel@tonic-gate
3633*0Sstevel@tonic-gate#-> sub CPAN::Author::fullname ;
3634*0Sstevel@tonic-gatesub fullname {
3635*0Sstevel@tonic-gate    shift->{RO}{FULLNAME};
3636*0Sstevel@tonic-gate}
3637*0Sstevel@tonic-gate*name = \&fullname;
3638*0Sstevel@tonic-gate
3639*0Sstevel@tonic-gate#-> sub CPAN::Author::email ;
3640*0Sstevel@tonic-gatesub email    { shift->{RO}{EMAIL}; }
3641*0Sstevel@tonic-gate
3642*0Sstevel@tonic-gate#-> sub CPAN::Author::ls ;
3643*0Sstevel@tonic-gatesub ls {
3644*0Sstevel@tonic-gate    my $self = shift;
3645*0Sstevel@tonic-gate    my $id = $self->id;
3646*0Sstevel@tonic-gate
3647*0Sstevel@tonic-gate    # adapted from CPAN::Distribution::verifyMD5 ;
3648*0Sstevel@tonic-gate    my(@csf); # chksumfile
3649*0Sstevel@tonic-gate    @csf = $self->id =~ /(.)(.)(.*)/;
3650*0Sstevel@tonic-gate    $csf[1] = join "", @csf[0,1];
3651*0Sstevel@tonic-gate    $csf[2] = join "", @csf[1,2];
3652*0Sstevel@tonic-gate    my(@dl);
3653*0Sstevel@tonic-gate    @dl = $self->dir_listing([$csf[0],"CHECKSUMS"], 0);
3654*0Sstevel@tonic-gate    unless (grep {$_->[2] eq $csf[1]} @dl) {
3655*0Sstevel@tonic-gate        $CPAN::Frontend->myprint("No files in the directory of $id\n");
3656*0Sstevel@tonic-gate        return;
3657*0Sstevel@tonic-gate    }
3658*0Sstevel@tonic-gate    @dl = $self->dir_listing([@csf[0,1],"CHECKSUMS"], 0);
3659*0Sstevel@tonic-gate    unless (grep {$_->[2] eq $csf[2]} @dl) {
3660*0Sstevel@tonic-gate        $CPAN::Frontend->myprint("No files in the directory of $id\n");
3661*0Sstevel@tonic-gate        return;
3662*0Sstevel@tonic-gate    }
3663*0Sstevel@tonic-gate    @dl = $self->dir_listing([@csf,"CHECKSUMS"], 1);
3664*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(join "", map {
3665*0Sstevel@tonic-gate        sprintf("%8d %10s %s/%s\n", $_->[0], $_->[1], $id, $_->[2])
3666*0Sstevel@tonic-gate    } sort { $a->[2] cmp $b->[2] } @dl);
3667*0Sstevel@tonic-gate}
3668*0Sstevel@tonic-gate
3669*0Sstevel@tonic-gate# returns an array of arrays, the latter contain (size,mtime,filename)
3670*0Sstevel@tonic-gate#-> sub CPAN::Author::dir_listing ;
3671*0Sstevel@tonic-gatesub dir_listing {
3672*0Sstevel@tonic-gate    my $self = shift;
3673*0Sstevel@tonic-gate    my $chksumfile = shift;
3674*0Sstevel@tonic-gate    my $recursive = shift;
3675*0Sstevel@tonic-gate    my $lc_want =
3676*0Sstevel@tonic-gate	File::Spec->catfile($CPAN::Config->{keep_source_where},
3677*0Sstevel@tonic-gate			    "authors", "id", @$chksumfile);
3678*0Sstevel@tonic-gate    local($") = "/";
3679*0Sstevel@tonic-gate    # connect "force" argument with "index_expire".
3680*0Sstevel@tonic-gate    my $force = 0;
3681*0Sstevel@tonic-gate    if (my @stat = stat $lc_want) {
3682*0Sstevel@tonic-gate        $force = $stat[9] + $CPAN::Config->{index_expire}*86400 <= time;
3683*0Sstevel@tonic-gate    }
3684*0Sstevel@tonic-gate    my $lc_file = CPAN::FTP->localize("authors/id/@$chksumfile",
3685*0Sstevel@tonic-gate                                      $lc_want,$force);
3686*0Sstevel@tonic-gate    unless ($lc_file) {
3687*0Sstevel@tonic-gate        $CPAN::Frontend->myprint("Trying $lc_want.gz\n");
3688*0Sstevel@tonic-gate	$chksumfile->[-1] .= ".gz";
3689*0Sstevel@tonic-gate	$lc_file = CPAN::FTP->localize("authors/id/@$chksumfile",
3690*0Sstevel@tonic-gate                                       "$lc_want.gz",1);
3691*0Sstevel@tonic-gate	if ($lc_file) {
3692*0Sstevel@tonic-gate	    $lc_file =~ s{\.gz(?!\n)\Z}{}; #};
3693*0Sstevel@tonic-gate	    CPAN::Tarzip->gunzip("$lc_file.gz",$lc_file);
3694*0Sstevel@tonic-gate	} else {
3695*0Sstevel@tonic-gate	    return;
3696*0Sstevel@tonic-gate	}
3697*0Sstevel@tonic-gate    }
3698*0Sstevel@tonic-gate
3699*0Sstevel@tonic-gate    # adapted from CPAN::Distribution::MD5_check_file ;
3700*0Sstevel@tonic-gate    my $fh = FileHandle->new;
3701*0Sstevel@tonic-gate    my($cksum);
3702*0Sstevel@tonic-gate    if (open $fh, $lc_file){
3703*0Sstevel@tonic-gate	local($/);
3704*0Sstevel@tonic-gate	my $eval = <$fh>;
3705*0Sstevel@tonic-gate	$eval =~ s/\015?\012/\n/g;
3706*0Sstevel@tonic-gate	close $fh;
3707*0Sstevel@tonic-gate	my($comp) = Safe->new();
3708*0Sstevel@tonic-gate	$cksum = $comp->reval($eval);
3709*0Sstevel@tonic-gate	if ($@) {
3710*0Sstevel@tonic-gate	    rename $lc_file, "$lc_file.bad";
3711*0Sstevel@tonic-gate	    Carp::confess($@) if $@;
3712*0Sstevel@tonic-gate	}
3713*0Sstevel@tonic-gate    } else {
3714*0Sstevel@tonic-gate	Carp::carp "Could not open $lc_file for reading";
3715*0Sstevel@tonic-gate    }
3716*0Sstevel@tonic-gate    my(@result,$f);
3717*0Sstevel@tonic-gate    for $f (sort keys %$cksum) {
3718*0Sstevel@tonic-gate        if (exists $cksum->{$f}{isdir}) {
3719*0Sstevel@tonic-gate            if ($recursive) {
3720*0Sstevel@tonic-gate                my(@dir) = @$chksumfile;
3721*0Sstevel@tonic-gate                pop @dir;
3722*0Sstevel@tonic-gate                push @dir, $f, "CHECKSUMS";
3723*0Sstevel@tonic-gate                push @result, map {
3724*0Sstevel@tonic-gate                    [$_->[0], $_->[1], "$f/$_->[2]"]
3725*0Sstevel@tonic-gate                } $self->dir_listing(\@dir,1);
3726*0Sstevel@tonic-gate            } else {
3727*0Sstevel@tonic-gate                push @result, [ 0, "-", $f ];
3728*0Sstevel@tonic-gate            }
3729*0Sstevel@tonic-gate        } else {
3730*0Sstevel@tonic-gate            push @result, [
3731*0Sstevel@tonic-gate                           ($cksum->{$f}{"size"}||0),
3732*0Sstevel@tonic-gate                           $cksum->{$f}{"mtime"}||"---",
3733*0Sstevel@tonic-gate                           $f
3734*0Sstevel@tonic-gate                          ];
3735*0Sstevel@tonic-gate        }
3736*0Sstevel@tonic-gate    }
3737*0Sstevel@tonic-gate    @result;
3738*0Sstevel@tonic-gate}
3739*0Sstevel@tonic-gate
3740*0Sstevel@tonic-gatepackage CPAN::Distribution;
3741*0Sstevel@tonic-gate
3742*0Sstevel@tonic-gate# Accessors
3743*0Sstevel@tonic-gatesub cpan_comment { shift->{RO}{CPAN_COMMENT} }
3744*0Sstevel@tonic-gate
3745*0Sstevel@tonic-gatesub undelay {
3746*0Sstevel@tonic-gate    my $self = shift;
3747*0Sstevel@tonic-gate    delete $self->{later};
3748*0Sstevel@tonic-gate}
3749*0Sstevel@tonic-gate
3750*0Sstevel@tonic-gate# CPAN::Distribution::normalize
3751*0Sstevel@tonic-gatesub normalize {
3752*0Sstevel@tonic-gate    my($self,$s) = @_;
3753*0Sstevel@tonic-gate    $s = $self->id unless defined $s;
3754*0Sstevel@tonic-gate    if (
3755*0Sstevel@tonic-gate        $s =~ tr|/|| == 1
3756*0Sstevel@tonic-gate        or
3757*0Sstevel@tonic-gate        $s !~ m|[A-Z]/[A-Z-]{2}/[A-Z-]{2,}/|
3758*0Sstevel@tonic-gate       ) {
3759*0Sstevel@tonic-gate        return $s if $s =~ m:^N/A|^Contact Author: ;
3760*0Sstevel@tonic-gate        $s =~ s|^(.)(.)([^/]*/)(.+)$|$1/$1$2/$1$2$3$4| or
3761*0Sstevel@tonic-gate            $CPAN::Frontend->mywarn("Strange distribution name [$s]\n");
3762*0Sstevel@tonic-gate        CPAN->debug("s[$s]") if $CPAN::DEBUG;
3763*0Sstevel@tonic-gate    }
3764*0Sstevel@tonic-gate    $s;
3765*0Sstevel@tonic-gate}
3766*0Sstevel@tonic-gate
3767*0Sstevel@tonic-gate#-> sub CPAN::Distribution::color_cmd_tmps ;
3768*0Sstevel@tonic-gatesub color_cmd_tmps {
3769*0Sstevel@tonic-gate    my($self) = shift;
3770*0Sstevel@tonic-gate    my($depth) = shift || 0;
3771*0Sstevel@tonic-gate    my($color) = shift || 0;
3772*0Sstevel@tonic-gate    my($ancestors) = shift || [];
3773*0Sstevel@tonic-gate    # a distribution needs to recurse into its prereq_pms
3774*0Sstevel@tonic-gate
3775*0Sstevel@tonic-gate    return if exists $self->{incommandcolor}
3776*0Sstevel@tonic-gate        && $self->{incommandcolor}==$color;
3777*0Sstevel@tonic-gate    if ($depth>=100){
3778*0Sstevel@tonic-gate        $CPAN::Frontend->mydie(CPAN::Exception::RecursiveDependency->new($ancestors));
3779*0Sstevel@tonic-gate    }
3780*0Sstevel@tonic-gate    # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1;
3781*0Sstevel@tonic-gate    my $prereq_pm = $self->prereq_pm;
3782*0Sstevel@tonic-gate    if (defined $prereq_pm) {
3783*0Sstevel@tonic-gate        for my $pre (keys %$prereq_pm) {
3784*0Sstevel@tonic-gate            my $premo = CPAN::Shell->expand("Module",$pre);
3785*0Sstevel@tonic-gate            $premo->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]);
3786*0Sstevel@tonic-gate        }
3787*0Sstevel@tonic-gate    }
3788*0Sstevel@tonic-gate    if ($color==0) {
3789*0Sstevel@tonic-gate        delete $self->{sponsored_mods};
3790*0Sstevel@tonic-gate        delete $self->{badtestcnt};
3791*0Sstevel@tonic-gate    }
3792*0Sstevel@tonic-gate    $self->{incommandcolor} = $color;
3793*0Sstevel@tonic-gate}
3794*0Sstevel@tonic-gate
3795*0Sstevel@tonic-gate#-> sub CPAN::Distribution::as_string ;
3796*0Sstevel@tonic-gatesub as_string {
3797*0Sstevel@tonic-gate  my $self = shift;
3798*0Sstevel@tonic-gate  $self->containsmods;
3799*0Sstevel@tonic-gate  $self->SUPER::as_string(@_);
3800*0Sstevel@tonic-gate}
3801*0Sstevel@tonic-gate
3802*0Sstevel@tonic-gate#-> sub CPAN::Distribution::containsmods ;
3803*0Sstevel@tonic-gatesub containsmods {
3804*0Sstevel@tonic-gate  my $self = shift;
3805*0Sstevel@tonic-gate  return keys %{$self->{CONTAINSMODS}} if exists $self->{CONTAINSMODS};
3806*0Sstevel@tonic-gate  my $dist_id = $self->{ID};
3807*0Sstevel@tonic-gate  for my $mod ($CPAN::META->all_objects("CPAN::Module")) {
3808*0Sstevel@tonic-gate    my $mod_file = $mod->cpan_file or next;
3809*0Sstevel@tonic-gate    my $mod_id = $mod->{ID} or next;
3810*0Sstevel@tonic-gate    # warn "mod_file[$mod_file] dist_id[$dist_id] mod_id[$mod_id]";
3811*0Sstevel@tonic-gate    # sleep 1;
3812*0Sstevel@tonic-gate    $self->{CONTAINSMODS}{$mod_id} = undef if $mod_file eq $dist_id;
3813*0Sstevel@tonic-gate  }
3814*0Sstevel@tonic-gate  keys %{$self->{CONTAINSMODS}};
3815*0Sstevel@tonic-gate}
3816*0Sstevel@tonic-gate
3817*0Sstevel@tonic-gate#-> sub CPAN::Distribution::uptodate ;
3818*0Sstevel@tonic-gatesub uptodate {
3819*0Sstevel@tonic-gate    my($self) = @_;
3820*0Sstevel@tonic-gate    my $c;
3821*0Sstevel@tonic-gate    foreach $c ($self->containsmods) {
3822*0Sstevel@tonic-gate        my $obj = CPAN::Shell->expandany($c);
3823*0Sstevel@tonic-gate        return 0 unless $obj->uptodate;
3824*0Sstevel@tonic-gate    }
3825*0Sstevel@tonic-gate    return 1;
3826*0Sstevel@tonic-gate}
3827*0Sstevel@tonic-gate
3828*0Sstevel@tonic-gate#-> sub CPAN::Distribution::called_for ;
3829*0Sstevel@tonic-gatesub called_for {
3830*0Sstevel@tonic-gate    my($self,$id) = @_;
3831*0Sstevel@tonic-gate    $self->{CALLED_FOR} = $id if defined $id;
3832*0Sstevel@tonic-gate    return $self->{CALLED_FOR};
3833*0Sstevel@tonic-gate}
3834*0Sstevel@tonic-gate
3835*0Sstevel@tonic-gate#-> sub CPAN::Distribution::safe_chdir ;
3836*0Sstevel@tonic-gatesub safe_chdir {
3837*0Sstevel@tonic-gate    my($self,$todir) = @_;
3838*0Sstevel@tonic-gate    # we die if we cannot chdir and we are debuggable
3839*0Sstevel@tonic-gate    Carp::confess("safe_chdir called without todir argument")
3840*0Sstevel@tonic-gate          unless defined $todir and length $todir;
3841*0Sstevel@tonic-gate    if (chdir $todir) {
3842*0Sstevel@tonic-gate        $self->debug(sprintf "changed directory to %s", CPAN::anycwd())
3843*0Sstevel@tonic-gate            if $CPAN::DEBUG;
3844*0Sstevel@tonic-gate    } else {
3845*0Sstevel@tonic-gate        my $cwd = CPAN::anycwd();
3846*0Sstevel@tonic-gate        $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] }.
3847*0Sstevel@tonic-gate                               qq{to todir[$todir]: $!});
3848*0Sstevel@tonic-gate    }
3849*0Sstevel@tonic-gate}
3850*0Sstevel@tonic-gate
3851*0Sstevel@tonic-gate#-> sub CPAN::Distribution::get ;
3852*0Sstevel@tonic-gatesub get {
3853*0Sstevel@tonic-gate    my($self) = @_;
3854*0Sstevel@tonic-gate  EXCUSE: {
3855*0Sstevel@tonic-gate	my @e;
3856*0Sstevel@tonic-gate	exists $self->{'build_dir'} and push @e,
3857*0Sstevel@tonic-gate	    "Is already unwrapped into directory $self->{'build_dir'}";
3858*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(join "", map {"  $_\n"} @e) and return if @e;
3859*0Sstevel@tonic-gate    }
3860*0Sstevel@tonic-gate    my $sub_wd = CPAN::anycwd(); # for cleaning up as good as possible
3861*0Sstevel@tonic-gate
3862*0Sstevel@tonic-gate    #
3863*0Sstevel@tonic-gate    # Get the file on local disk
3864*0Sstevel@tonic-gate    #
3865*0Sstevel@tonic-gate
3866*0Sstevel@tonic-gate    my($local_file);
3867*0Sstevel@tonic-gate    my($local_wanted) =
3868*0Sstevel@tonic-gate        File::Spec->catfile(
3869*0Sstevel@tonic-gate			    $CPAN::Config->{keep_source_where},
3870*0Sstevel@tonic-gate			    "authors",
3871*0Sstevel@tonic-gate			    "id",
3872*0Sstevel@tonic-gate			    split(/\//,$self->id)
3873*0Sstevel@tonic-gate			   );
3874*0Sstevel@tonic-gate
3875*0Sstevel@tonic-gate    $self->debug("Doing localize") if $CPAN::DEBUG;
3876*0Sstevel@tonic-gate    unless ($local_file =
3877*0Sstevel@tonic-gate            CPAN::FTP->localize("authors/id/$self->{ID}",
3878*0Sstevel@tonic-gate                                $local_wanted)) {
3879*0Sstevel@tonic-gate        my $note = "";
3880*0Sstevel@tonic-gate        if ($CPAN::Index::DATE_OF_02) {
3881*0Sstevel@tonic-gate            $note = "Note: Current database in memory was generated ".
3882*0Sstevel@tonic-gate                "on $CPAN::Index::DATE_OF_02\n";
3883*0Sstevel@tonic-gate        }
3884*0Sstevel@tonic-gate        $CPAN::Frontend->mydie("Giving up on '$local_wanted'\n$note");
3885*0Sstevel@tonic-gate    }
3886*0Sstevel@tonic-gate    $self->debug("local_file[$local_file]") if $CPAN::DEBUG;
3887*0Sstevel@tonic-gate    $self->{localfile} = $local_file;
3888*0Sstevel@tonic-gate    return if $CPAN::Signal;
3889*0Sstevel@tonic-gate
3890*0Sstevel@tonic-gate    #
3891*0Sstevel@tonic-gate    # Check integrity
3892*0Sstevel@tonic-gate    #
3893*0Sstevel@tonic-gate    if ($CPAN::META->has_inst("Digest::MD5")) {
3894*0Sstevel@tonic-gate	$self->debug("Digest::MD5 is installed, verifying");
3895*0Sstevel@tonic-gate	$self->verifyMD5;
3896*0Sstevel@tonic-gate    } else {
3897*0Sstevel@tonic-gate	$self->debug("Digest::MD5 is NOT installed");
3898*0Sstevel@tonic-gate    }
3899*0Sstevel@tonic-gate    return if $CPAN::Signal;
3900*0Sstevel@tonic-gate
3901*0Sstevel@tonic-gate    #
3902*0Sstevel@tonic-gate    # Create a clean room and go there
3903*0Sstevel@tonic-gate    #
3904*0Sstevel@tonic-gate    $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); # unsafe meta access, ok
3905*0Sstevel@tonic-gate    my $builddir = $CPAN::META->{cachemgr}->dir; # unsafe meta access, ok
3906*0Sstevel@tonic-gate    $self->safe_chdir($builddir);
3907*0Sstevel@tonic-gate    $self->debug("Removing tmp") if $CPAN::DEBUG;
3908*0Sstevel@tonic-gate    File::Path::rmtree("tmp");
3909*0Sstevel@tonic-gate    mkdir "tmp", 0755 or Carp::croak "Couldn't mkdir tmp: $!";
3910*0Sstevel@tonic-gate    if ($CPAN::Signal){
3911*0Sstevel@tonic-gate        $self->safe_chdir($sub_wd);
3912*0Sstevel@tonic-gate        return;
3913*0Sstevel@tonic-gate    }
3914*0Sstevel@tonic-gate    $self->safe_chdir("tmp");
3915*0Sstevel@tonic-gate
3916*0Sstevel@tonic-gate    #
3917*0Sstevel@tonic-gate    # Unpack the goods
3918*0Sstevel@tonic-gate    #
3919*0Sstevel@tonic-gate    if ($local_file =~ /(\.tar\.(gz|Z)|\.tgz)(?!\n)\Z/i){
3920*0Sstevel@tonic-gate        $self->{was_uncompressed}++ unless CPAN::Tarzip->gtest($local_file);
3921*0Sstevel@tonic-gate	$self->untar_me($local_file);
3922*0Sstevel@tonic-gate    } elsif ( $local_file =~ /\.zip(?!\n)\Z/i ) {
3923*0Sstevel@tonic-gate	$self->unzip_me($local_file);
3924*0Sstevel@tonic-gate    } elsif ( $local_file =~ /\.pm\.(gz|Z)(?!\n)\Z/) {
3925*0Sstevel@tonic-gate        $self->{was_uncompressed}++ unless CPAN::Tarzip->gtest($local_file);
3926*0Sstevel@tonic-gate	$self->pm2dir_me($local_file);
3927*0Sstevel@tonic-gate    } else {
3928*0Sstevel@tonic-gate	$self->{archived} = "NO";
3929*0Sstevel@tonic-gate        $self->safe_chdir($sub_wd);
3930*0Sstevel@tonic-gate        return;
3931*0Sstevel@tonic-gate    }
3932*0Sstevel@tonic-gate
3933*0Sstevel@tonic-gate    # we are still in the tmp directory!
3934*0Sstevel@tonic-gate    # Let's check if the package has its own directory.
3935*0Sstevel@tonic-gate    my $dh = DirHandle->new(File::Spec->curdir)
3936*0Sstevel@tonic-gate        or Carp::croak("Couldn't opendir .: $!");
3937*0Sstevel@tonic-gate    my @readdir = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh->read; ### MAC??
3938*0Sstevel@tonic-gate    $dh->close;
3939*0Sstevel@tonic-gate    my ($distdir,$packagedir);
3940*0Sstevel@tonic-gate    if (@readdir == 1 && -d $readdir[0]) {
3941*0Sstevel@tonic-gate        $distdir = $readdir[0];
3942*0Sstevel@tonic-gate        $packagedir = File::Spec->catdir($builddir,$distdir);
3943*0Sstevel@tonic-gate        $self->debug("packagedir[$packagedir]builddir[$builddir]distdir[$distdir]")
3944*0Sstevel@tonic-gate            if $CPAN::DEBUG;
3945*0Sstevel@tonic-gate        -d $packagedir and $CPAN::Frontend->myprint("Removing previously used ".
3946*0Sstevel@tonic-gate                                                    "$packagedir\n");
3947*0Sstevel@tonic-gate        File::Path::rmtree($packagedir);
3948*0Sstevel@tonic-gate        rename($distdir,$packagedir) or
3949*0Sstevel@tonic-gate            Carp::confess("Couldn't rename $distdir to $packagedir: $!");
3950*0Sstevel@tonic-gate        $self->debug(sprintf("renamed distdir[%s] to packagedir[%s] -e[%s]-d[%s]",
3951*0Sstevel@tonic-gate                             $distdir,
3952*0Sstevel@tonic-gate                             $packagedir,
3953*0Sstevel@tonic-gate                             -e $packagedir,
3954*0Sstevel@tonic-gate                             -d $packagedir,
3955*0Sstevel@tonic-gate                            )) if $CPAN::DEBUG;
3956*0Sstevel@tonic-gate    } else {
3957*0Sstevel@tonic-gate        my $userid = $self->cpan_userid;
3958*0Sstevel@tonic-gate        unless ($userid) {
3959*0Sstevel@tonic-gate            CPAN->debug("no userid? self[$self]");
3960*0Sstevel@tonic-gate            $userid = "anon";
3961*0Sstevel@tonic-gate        }
3962*0Sstevel@tonic-gate        my $pragmatic_dir = $userid . '000';
3963*0Sstevel@tonic-gate        $pragmatic_dir =~ s/\W_//g;
3964*0Sstevel@tonic-gate        $pragmatic_dir++ while -d "../$pragmatic_dir";
3965*0Sstevel@tonic-gate        $packagedir = File::Spec->catdir($builddir,$pragmatic_dir);
3966*0Sstevel@tonic-gate        $self->debug("packagedir[$packagedir]") if $CPAN::DEBUG;
3967*0Sstevel@tonic-gate        File::Path::mkpath($packagedir);
3968*0Sstevel@tonic-gate        my($f);
3969*0Sstevel@tonic-gate        for $f (@readdir) { # is already without "." and ".."
3970*0Sstevel@tonic-gate            my $to = File::Spec->catdir($packagedir,$f);
3971*0Sstevel@tonic-gate            rename($f,$to) or Carp::confess("Couldn't rename $f to $to: $!");
3972*0Sstevel@tonic-gate        }
3973*0Sstevel@tonic-gate    }
3974*0Sstevel@tonic-gate    if ($CPAN::Signal){
3975*0Sstevel@tonic-gate        $self->safe_chdir($sub_wd);
3976*0Sstevel@tonic-gate        return;
3977*0Sstevel@tonic-gate    }
3978*0Sstevel@tonic-gate
3979*0Sstevel@tonic-gate    $self->{'build_dir'} = $packagedir;
3980*0Sstevel@tonic-gate    $self->safe_chdir($builddir);
3981*0Sstevel@tonic-gate    File::Path::rmtree("tmp");
3982*0Sstevel@tonic-gate
3983*0Sstevel@tonic-gate    my($mpl) = File::Spec->catfile($packagedir,"Makefile.PL");
3984*0Sstevel@tonic-gate    my($mpl_exists) = -f $mpl;
3985*0Sstevel@tonic-gate    unless ($mpl_exists) {
3986*0Sstevel@tonic-gate        # NFS has been reported to have racing problems after the
3987*0Sstevel@tonic-gate        # renaming of a directory in some environments.
3988*0Sstevel@tonic-gate        # This trick helps.
3989*0Sstevel@tonic-gate        sleep 1;
3990*0Sstevel@tonic-gate        my $mpldh = DirHandle->new($packagedir)
3991*0Sstevel@tonic-gate            or Carp::croak("Couldn't opendir $packagedir: $!");
3992*0Sstevel@tonic-gate        $mpl_exists = grep /^Makefile\.PL$/, $mpldh->read;
3993*0Sstevel@tonic-gate        $mpldh->close;
3994*0Sstevel@tonic-gate    }
3995*0Sstevel@tonic-gate    unless ($mpl_exists) {
3996*0Sstevel@tonic-gate        $self->debug(sprintf("makefilepl[%s]anycwd[%s]",
3997*0Sstevel@tonic-gate                             $mpl,
3998*0Sstevel@tonic-gate                             CPAN::anycwd(),
3999*0Sstevel@tonic-gate                            )) if $CPAN::DEBUG;
4000*0Sstevel@tonic-gate        my($configure) = File::Spec->catfile($packagedir,"Configure");
4001*0Sstevel@tonic-gate        if (-f $configure) {
4002*0Sstevel@tonic-gate            # do we have anything to do?
4003*0Sstevel@tonic-gate            $self->{'configure'} = $configure;
4004*0Sstevel@tonic-gate        } elsif (-f File::Spec->catfile($packagedir,"Makefile")) {
4005*0Sstevel@tonic-gate            $CPAN::Frontend->myprint(qq{
4006*0Sstevel@tonic-gatePackage comes with a Makefile and without a Makefile.PL.
4007*0Sstevel@tonic-gateWe\'ll try to build it with that Makefile then.
4008*0Sstevel@tonic-gate});
4009*0Sstevel@tonic-gate            $self->{writemakefile} = "YES";
4010*0Sstevel@tonic-gate            sleep 2;
4011*0Sstevel@tonic-gate        } else {
4012*0Sstevel@tonic-gate            my $cf = $self->called_for || "unknown";
4013*0Sstevel@tonic-gate            if ($cf =~ m|/|) {
4014*0Sstevel@tonic-gate                $cf =~ s|.*/||;
4015*0Sstevel@tonic-gate                $cf =~ s|\W.*||;
4016*0Sstevel@tonic-gate            }
4017*0Sstevel@tonic-gate            $cf =~ s|[/\\:]||g; # risk of filesystem damage
4018*0Sstevel@tonic-gate            $cf = "unknown" unless length($cf);
4019*0Sstevel@tonic-gate            $CPAN::Frontend->myprint(qq{Package seems to come without Makefile.PL.
4020*0Sstevel@tonic-gate  (The test -f "$mpl" returned false.)
4021*0Sstevel@tonic-gate  Writing one on our own (setting NAME to $cf)\a\n});
4022*0Sstevel@tonic-gate            $self->{had_no_makefile_pl}++;
4023*0Sstevel@tonic-gate            sleep 3;
4024*0Sstevel@tonic-gate
4025*0Sstevel@tonic-gate            # Writing our own Makefile.PL
4026*0Sstevel@tonic-gate
4027*0Sstevel@tonic-gate            my $fh = FileHandle->new;
4028*0Sstevel@tonic-gate            $fh->open(">$mpl")
4029*0Sstevel@tonic-gate                or Carp::croak("Could not open >$mpl: $!");
4030*0Sstevel@tonic-gate            $fh->print(
4031*0Sstevel@tonic-gateqq{# This Makefile.PL has been autogenerated by the module CPAN.pm
4032*0Sstevel@tonic-gate# because there was no Makefile.PL supplied.
4033*0Sstevel@tonic-gate# Autogenerated on: }.scalar localtime().qq{
4034*0Sstevel@tonic-gate
4035*0Sstevel@tonic-gateuse ExtUtils::MakeMaker;
4036*0Sstevel@tonic-gateWriteMakefile(NAME => q[$cf]);
4037*0Sstevel@tonic-gate
4038*0Sstevel@tonic-gate});
4039*0Sstevel@tonic-gate            $fh->close;
4040*0Sstevel@tonic-gate        }
4041*0Sstevel@tonic-gate    }
4042*0Sstevel@tonic-gate
4043*0Sstevel@tonic-gate    return $self;
4044*0Sstevel@tonic-gate}
4045*0Sstevel@tonic-gate
4046*0Sstevel@tonic-gate# CPAN::Distribution::untar_me ;
4047*0Sstevel@tonic-gatesub untar_me {
4048*0Sstevel@tonic-gate    my($self,$local_file) = @_;
4049*0Sstevel@tonic-gate    $self->{archived} = "tar";
4050*0Sstevel@tonic-gate    if (CPAN::Tarzip->untar($local_file)) {
4051*0Sstevel@tonic-gate	$self->{unwrapped} = "YES";
4052*0Sstevel@tonic-gate    } else {
4053*0Sstevel@tonic-gate	$self->{unwrapped} = "NO";
4054*0Sstevel@tonic-gate    }
4055*0Sstevel@tonic-gate}
4056*0Sstevel@tonic-gate
4057*0Sstevel@tonic-gate# CPAN::Distribution::unzip_me ;
4058*0Sstevel@tonic-gatesub unzip_me {
4059*0Sstevel@tonic-gate    my($self,$local_file) = @_;
4060*0Sstevel@tonic-gate    $self->{archived} = "zip";
4061*0Sstevel@tonic-gate    if (CPAN::Tarzip->unzip($local_file)) {
4062*0Sstevel@tonic-gate	$self->{unwrapped} = "YES";
4063*0Sstevel@tonic-gate    } else {
4064*0Sstevel@tonic-gate	$self->{unwrapped} = "NO";
4065*0Sstevel@tonic-gate    }
4066*0Sstevel@tonic-gate    return;
4067*0Sstevel@tonic-gate}
4068*0Sstevel@tonic-gate
4069*0Sstevel@tonic-gatesub pm2dir_me {
4070*0Sstevel@tonic-gate    my($self,$local_file) = @_;
4071*0Sstevel@tonic-gate    $self->{archived} = "pm";
4072*0Sstevel@tonic-gate    my $to = File::Basename::basename($local_file);
4073*0Sstevel@tonic-gate    $to =~ s/\.(gz|Z)(?!\n)\Z//;
4074*0Sstevel@tonic-gate    if (CPAN::Tarzip->gunzip($local_file,$to)) {
4075*0Sstevel@tonic-gate	$self->{unwrapped} = "YES";
4076*0Sstevel@tonic-gate    } else {
4077*0Sstevel@tonic-gate	$self->{unwrapped} = "NO";
4078*0Sstevel@tonic-gate    }
4079*0Sstevel@tonic-gate}
4080*0Sstevel@tonic-gate
4081*0Sstevel@tonic-gate#-> sub CPAN::Distribution::new ;
4082*0Sstevel@tonic-gatesub new {
4083*0Sstevel@tonic-gate    my($class,%att) = @_;
4084*0Sstevel@tonic-gate
4085*0Sstevel@tonic-gate    # $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new();
4086*0Sstevel@tonic-gate
4087*0Sstevel@tonic-gate    my $this = { %att };
4088*0Sstevel@tonic-gate    return bless $this, $class;
4089*0Sstevel@tonic-gate}
4090*0Sstevel@tonic-gate
4091*0Sstevel@tonic-gate#-> sub CPAN::Distribution::look ;
4092*0Sstevel@tonic-gatesub look {
4093*0Sstevel@tonic-gate    my($self) = @_;
4094*0Sstevel@tonic-gate
4095*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
4096*0Sstevel@tonic-gate      $self->Mac::BuildTools::look;
4097*0Sstevel@tonic-gate      return;
4098*0Sstevel@tonic-gate    }
4099*0Sstevel@tonic-gate
4100*0Sstevel@tonic-gate    if (  $CPAN::Config->{'shell'} ) {
4101*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(qq{
4102*0Sstevel@tonic-gateTrying to open a subshell in the build directory...
4103*0Sstevel@tonic-gate});
4104*0Sstevel@tonic-gate    } else {
4105*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(qq{
4106*0Sstevel@tonic-gateYour configuration does not define a value for subshells.
4107*0Sstevel@tonic-gatePlease define it with "o conf shell <your shell>"
4108*0Sstevel@tonic-gate});
4109*0Sstevel@tonic-gate	return;
4110*0Sstevel@tonic-gate    }
4111*0Sstevel@tonic-gate    my $dist = $self->id;
4112*0Sstevel@tonic-gate    my $dir;
4113*0Sstevel@tonic-gate    unless ($dir = $self->dir) {
4114*0Sstevel@tonic-gate        $self->get;
4115*0Sstevel@tonic-gate    }
4116*0Sstevel@tonic-gate    unless ($dir ||= $self->dir) {
4117*0Sstevel@tonic-gate	$CPAN::Frontend->mywarn(qq{
4118*0Sstevel@tonic-gateCould not determine which directory to use for looking at $dist.
4119*0Sstevel@tonic-gate});
4120*0Sstevel@tonic-gate	return;
4121*0Sstevel@tonic-gate    }
4122*0Sstevel@tonic-gate    my $pwd  = CPAN::anycwd();
4123*0Sstevel@tonic-gate    $self->safe_chdir($dir);
4124*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(qq{Working directory is $dir\n});
4125*0Sstevel@tonic-gate    unless (system($CPAN::Config->{'shell'}) == 0) {
4126*0Sstevel@tonic-gate        my $code = $? >> 8;
4127*0Sstevel@tonic-gate        $CPAN::Frontend->mywarn("Subprocess shell exit code $code\n");
4128*0Sstevel@tonic-gate    }
4129*0Sstevel@tonic-gate    $self->safe_chdir($pwd);
4130*0Sstevel@tonic-gate}
4131*0Sstevel@tonic-gate
4132*0Sstevel@tonic-gate# CPAN::Distribution::cvs_import ;
4133*0Sstevel@tonic-gatesub cvs_import {
4134*0Sstevel@tonic-gate    my($self) = @_;
4135*0Sstevel@tonic-gate    $self->get;
4136*0Sstevel@tonic-gate    my $dir = $self->dir;
4137*0Sstevel@tonic-gate
4138*0Sstevel@tonic-gate    my $package = $self->called_for;
4139*0Sstevel@tonic-gate    my $module = $CPAN::META->instance('CPAN::Module', $package);
4140*0Sstevel@tonic-gate    my $version = $module->cpan_version;
4141*0Sstevel@tonic-gate
4142*0Sstevel@tonic-gate    my $userid = $self->cpan_userid;
4143*0Sstevel@tonic-gate
4144*0Sstevel@tonic-gate    my $cvs_dir = (split /\//, $dir)[-1];
4145*0Sstevel@tonic-gate    $cvs_dir =~ s/-\d+[^-]+(?!\n)\Z//;
4146*0Sstevel@tonic-gate    my $cvs_root =
4147*0Sstevel@tonic-gate      $CPAN::Config->{cvsroot} || $ENV{CVSROOT};
4148*0Sstevel@tonic-gate    my $cvs_site_perl =
4149*0Sstevel@tonic-gate      $CPAN::Config->{cvs_site_perl} || $ENV{CVS_SITE_PERL};
4150*0Sstevel@tonic-gate    if ($cvs_site_perl) {
4151*0Sstevel@tonic-gate	$cvs_dir = "$cvs_site_perl/$cvs_dir";
4152*0Sstevel@tonic-gate    }
4153*0Sstevel@tonic-gate    my $cvs_log = qq{"imported $package $version sources"};
4154*0Sstevel@tonic-gate    $version =~ s/\./_/g;
4155*0Sstevel@tonic-gate    my @cmd = ('cvs', '-d', $cvs_root, 'import', '-m', $cvs_log,
4156*0Sstevel@tonic-gate	       "$cvs_dir", $userid, "v$version");
4157*0Sstevel@tonic-gate
4158*0Sstevel@tonic-gate    my $pwd  = CPAN::anycwd();
4159*0Sstevel@tonic-gate    chdir($dir) or $CPAN::Frontend->mydie(qq{Could not chdir to "$dir": $!});
4160*0Sstevel@tonic-gate
4161*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(qq{Working directory is $dir\n});
4162*0Sstevel@tonic-gate
4163*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(qq{@cmd\n});
4164*0Sstevel@tonic-gate    system(@cmd) == 0 or
4165*0Sstevel@tonic-gate	$CPAN::Frontend->mydie("cvs import failed");
4166*0Sstevel@tonic-gate    chdir($pwd) or $CPAN::Frontend->mydie(qq{Could not chdir to "$pwd": $!});
4167*0Sstevel@tonic-gate}
4168*0Sstevel@tonic-gate
4169*0Sstevel@tonic-gate#-> sub CPAN::Distribution::readme ;
4170*0Sstevel@tonic-gatesub readme {
4171*0Sstevel@tonic-gate    my($self) = @_;
4172*0Sstevel@tonic-gate    my($dist) = $self->id;
4173*0Sstevel@tonic-gate    my($sans,$suffix) = $dist =~ /(.+)\.(tgz|tar[\._-]gz|tar\.Z|zip)$/;
4174*0Sstevel@tonic-gate    $self->debug("sans[$sans] suffix[$suffix]\n") if $CPAN::DEBUG;
4175*0Sstevel@tonic-gate    my($local_file);
4176*0Sstevel@tonic-gate    my($local_wanted) =
4177*0Sstevel@tonic-gate	 File::Spec->catfile(
4178*0Sstevel@tonic-gate			     $CPAN::Config->{keep_source_where},
4179*0Sstevel@tonic-gate			     "authors",
4180*0Sstevel@tonic-gate			     "id",
4181*0Sstevel@tonic-gate			     split(/\//,"$sans.readme"),
4182*0Sstevel@tonic-gate			    );
4183*0Sstevel@tonic-gate    $self->debug("Doing localize") if $CPAN::DEBUG;
4184*0Sstevel@tonic-gate    $local_file = CPAN::FTP->localize("authors/id/$sans.readme",
4185*0Sstevel@tonic-gate				      $local_wanted)
4186*0Sstevel@tonic-gate	or $CPAN::Frontend->mydie(qq{No $sans.readme found});;
4187*0Sstevel@tonic-gate
4188*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
4189*0Sstevel@tonic-gate        Mac::BuildTools::launch_file($local_file);
4190*0Sstevel@tonic-gate        return;
4191*0Sstevel@tonic-gate    }
4192*0Sstevel@tonic-gate
4193*0Sstevel@tonic-gate    my $fh_pager = FileHandle->new;
4194*0Sstevel@tonic-gate    local($SIG{PIPE}) = "IGNORE";
4195*0Sstevel@tonic-gate    $fh_pager->open("|$CPAN::Config->{'pager'}")
4196*0Sstevel@tonic-gate	or die "Could not open pager $CPAN::Config->{'pager'}: $!";
4197*0Sstevel@tonic-gate    my $fh_readme = FileHandle->new;
4198*0Sstevel@tonic-gate    $fh_readme->open($local_file)
4199*0Sstevel@tonic-gate	or $CPAN::Frontend->mydie(qq{Could not open "$local_file": $!});
4200*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(qq{
4201*0Sstevel@tonic-gateDisplaying file
4202*0Sstevel@tonic-gate  $local_file
4203*0Sstevel@tonic-gatewith pager "$CPAN::Config->{'pager'}"
4204*0Sstevel@tonic-gate});
4205*0Sstevel@tonic-gate    sleep 2;
4206*0Sstevel@tonic-gate    $fh_pager->print(<$fh_readme>);
4207*0Sstevel@tonic-gate}
4208*0Sstevel@tonic-gate
4209*0Sstevel@tonic-gate#-> sub CPAN::Distribution::verifyMD5 ;
4210*0Sstevel@tonic-gatesub verifyMD5 {
4211*0Sstevel@tonic-gate    my($self) = @_;
4212*0Sstevel@tonic-gate  EXCUSE: {
4213*0Sstevel@tonic-gate	my @e;
4214*0Sstevel@tonic-gate	$self->{MD5_STATUS} ||= "";
4215*0Sstevel@tonic-gate	$self->{MD5_STATUS} eq "OK" and push @e, "MD5 Checksum was ok";
4216*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(join "", map {"  $_\n"} @e) and return if @e;
4217*0Sstevel@tonic-gate    }
4218*0Sstevel@tonic-gate    my($lc_want,$lc_file,@local,$basename);
4219*0Sstevel@tonic-gate    @local = split(/\//,$self->id);
4220*0Sstevel@tonic-gate    pop @local;
4221*0Sstevel@tonic-gate    push @local, "CHECKSUMS";
4222*0Sstevel@tonic-gate    $lc_want =
4223*0Sstevel@tonic-gate	File::Spec->catfile($CPAN::Config->{keep_source_where},
4224*0Sstevel@tonic-gate			    "authors", "id", @local);
4225*0Sstevel@tonic-gate    local($") = "/";
4226*0Sstevel@tonic-gate    if (
4227*0Sstevel@tonic-gate	-s $lc_want
4228*0Sstevel@tonic-gate	&&
4229*0Sstevel@tonic-gate	$self->MD5_check_file($lc_want)
4230*0Sstevel@tonic-gate       ) {
4231*0Sstevel@tonic-gate	return $self->{MD5_STATUS} = "OK";
4232*0Sstevel@tonic-gate    }
4233*0Sstevel@tonic-gate    $lc_file = CPAN::FTP->localize("authors/id/@local",
4234*0Sstevel@tonic-gate				   $lc_want,1);
4235*0Sstevel@tonic-gate    unless ($lc_file) {
4236*0Sstevel@tonic-gate        $CPAN::Frontend->myprint("Trying $lc_want.gz\n");
4237*0Sstevel@tonic-gate	$local[-1] .= ".gz";
4238*0Sstevel@tonic-gate	$lc_file = CPAN::FTP->localize("authors/id/@local",
4239*0Sstevel@tonic-gate				       "$lc_want.gz",1);
4240*0Sstevel@tonic-gate	if ($lc_file) {
4241*0Sstevel@tonic-gate	    $lc_file =~ s/\.gz(?!\n)\Z//;
4242*0Sstevel@tonic-gate	    CPAN::Tarzip->gunzip("$lc_file.gz",$lc_file);
4243*0Sstevel@tonic-gate	} else {
4244*0Sstevel@tonic-gate	    return;
4245*0Sstevel@tonic-gate	}
4246*0Sstevel@tonic-gate    }
4247*0Sstevel@tonic-gate    $self->MD5_check_file($lc_file);
4248*0Sstevel@tonic-gate}
4249*0Sstevel@tonic-gate
4250*0Sstevel@tonic-gate#-> sub CPAN::Distribution::MD5_check_file ;
4251*0Sstevel@tonic-gatesub MD5_check_file {
4252*0Sstevel@tonic-gate    my($self,$chk_file) = @_;
4253*0Sstevel@tonic-gate    my($cksum,$file,$basename);
4254*0Sstevel@tonic-gate    $file = $self->{localfile};
4255*0Sstevel@tonic-gate    $basename = File::Basename::basename($file);
4256*0Sstevel@tonic-gate    my $fh = FileHandle->new;
4257*0Sstevel@tonic-gate    if (open $fh, $chk_file){
4258*0Sstevel@tonic-gate	local($/);
4259*0Sstevel@tonic-gate	my $eval = <$fh>;
4260*0Sstevel@tonic-gate	$eval =~ s/\015?\012/\n/g;
4261*0Sstevel@tonic-gate	close $fh;
4262*0Sstevel@tonic-gate	my($comp) = Safe->new();
4263*0Sstevel@tonic-gate	$cksum = $comp->reval($eval);
4264*0Sstevel@tonic-gate	if ($@) {
4265*0Sstevel@tonic-gate	    rename $chk_file, "$chk_file.bad";
4266*0Sstevel@tonic-gate	    Carp::confess($@) if $@;
4267*0Sstevel@tonic-gate	}
4268*0Sstevel@tonic-gate    } else {
4269*0Sstevel@tonic-gate	Carp::carp "Could not open $chk_file for reading";
4270*0Sstevel@tonic-gate    }
4271*0Sstevel@tonic-gate
4272*0Sstevel@tonic-gate    if (exists $cksum->{$basename}{md5}) {
4273*0Sstevel@tonic-gate	$self->debug("Found checksum for $basename:" .
4274*0Sstevel@tonic-gate		     "$cksum->{$basename}{md5}\n") if $CPAN::DEBUG;
4275*0Sstevel@tonic-gate
4276*0Sstevel@tonic-gate	open($fh, $file);
4277*0Sstevel@tonic-gate	binmode $fh;
4278*0Sstevel@tonic-gate	my $eq = $self->eq_MD5($fh,$cksum->{$basename}{'md5'});
4279*0Sstevel@tonic-gate	$fh->close;
4280*0Sstevel@tonic-gate	$fh = CPAN::Tarzip->TIEHANDLE($file);
4281*0Sstevel@tonic-gate
4282*0Sstevel@tonic-gate	unless ($eq) {
4283*0Sstevel@tonic-gate	  # had to inline it, when I tied it, the tiedness got lost on
4284*0Sstevel@tonic-gate	  # the call to eq_MD5. (Jan 1998)
4285*0Sstevel@tonic-gate	  my $md5 = Digest::MD5->new;
4286*0Sstevel@tonic-gate	  my($data,$ref);
4287*0Sstevel@tonic-gate	  $ref = \$data;
4288*0Sstevel@tonic-gate	  while ($fh->READ($ref, 4096) > 0){
4289*0Sstevel@tonic-gate	    $md5->add($data);
4290*0Sstevel@tonic-gate	  }
4291*0Sstevel@tonic-gate	  my $hexdigest = $md5->hexdigest;
4292*0Sstevel@tonic-gate	  $eq += $hexdigest eq $cksum->{$basename}{'md5-ungz'};
4293*0Sstevel@tonic-gate	}
4294*0Sstevel@tonic-gate
4295*0Sstevel@tonic-gate	if ($eq) {
4296*0Sstevel@tonic-gate	  $CPAN::Frontend->myprint("Checksum for $file ok\n");
4297*0Sstevel@tonic-gate	  return $self->{MD5_STATUS} = "OK";
4298*0Sstevel@tonic-gate	} else {
4299*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(qq{\nChecksum mismatch for }.
4300*0Sstevel@tonic-gate				     qq{distribution file. }.
4301*0Sstevel@tonic-gate				     qq{Please investigate.\n\n}.
4302*0Sstevel@tonic-gate				     $self->as_string,
4303*0Sstevel@tonic-gate				     $CPAN::META->instance(
4304*0Sstevel@tonic-gate							   'CPAN::Author',
4305*0Sstevel@tonic-gate							   $self->cpan_userid
4306*0Sstevel@tonic-gate							  )->as_string);
4307*0Sstevel@tonic-gate
4308*0Sstevel@tonic-gate	    my $wrap = qq{I\'d recommend removing $file. Its MD5
4309*0Sstevel@tonic-gatechecksum is incorrect. Maybe you have configured your 'urllist' with
4310*0Sstevel@tonic-gatea bad URL. Please check this array with 'o conf urllist', and
4311*0Sstevel@tonic-gateretry.};
4312*0Sstevel@tonic-gate
4313*0Sstevel@tonic-gate            $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap));
4314*0Sstevel@tonic-gate
4315*0Sstevel@tonic-gate            # former versions just returned here but this seems a
4316*0Sstevel@tonic-gate            # serious threat that deserves a die
4317*0Sstevel@tonic-gate
4318*0Sstevel@tonic-gate	    # $CPAN::Frontend->myprint("\n\n");
4319*0Sstevel@tonic-gate	    # sleep 3;
4320*0Sstevel@tonic-gate	    # return;
4321*0Sstevel@tonic-gate	}
4322*0Sstevel@tonic-gate	# close $fh if fileno($fh);
4323*0Sstevel@tonic-gate    } else {
4324*0Sstevel@tonic-gate	$self->{MD5_STATUS} ||= "";
4325*0Sstevel@tonic-gate	if ($self->{MD5_STATUS} eq "NIL") {
4326*0Sstevel@tonic-gate	    $CPAN::Frontend->mywarn(qq{
4327*0Sstevel@tonic-gateWarning: No md5 checksum for $basename in $chk_file.
4328*0Sstevel@tonic-gate
4329*0Sstevel@tonic-gateThe cause for this may be that the file is very new and the checksum
4330*0Sstevel@tonic-gatehas not yet been calculated, but it may also be that something is
4331*0Sstevel@tonic-gategoing awry right now.
4332*0Sstevel@tonic-gate});
4333*0Sstevel@tonic-gate            my $answer = ExtUtils::MakeMaker::prompt("Proceed?", "yes");
4334*0Sstevel@tonic-gate            $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.");
4335*0Sstevel@tonic-gate	}
4336*0Sstevel@tonic-gate	$self->{MD5_STATUS} = "NIL";
4337*0Sstevel@tonic-gate	return;
4338*0Sstevel@tonic-gate    }
4339*0Sstevel@tonic-gate}
4340*0Sstevel@tonic-gate
4341*0Sstevel@tonic-gate#-> sub CPAN::Distribution::eq_MD5 ;
4342*0Sstevel@tonic-gatesub eq_MD5 {
4343*0Sstevel@tonic-gate    my($self,$fh,$expectMD5) = @_;
4344*0Sstevel@tonic-gate    my $md5 = Digest::MD5->new;
4345*0Sstevel@tonic-gate    my($data);
4346*0Sstevel@tonic-gate    while (read($fh, $data, 4096)){
4347*0Sstevel@tonic-gate      $md5->add($data);
4348*0Sstevel@tonic-gate    }
4349*0Sstevel@tonic-gate    # $md5->addfile($fh);
4350*0Sstevel@tonic-gate    my $hexdigest = $md5->hexdigest;
4351*0Sstevel@tonic-gate    # warn "fh[$fh] hex[$hexdigest] aexp[$expectMD5]";
4352*0Sstevel@tonic-gate    $hexdigest eq $expectMD5;
4353*0Sstevel@tonic-gate}
4354*0Sstevel@tonic-gate
4355*0Sstevel@tonic-gate#-> sub CPAN::Distribution::force ;
4356*0Sstevel@tonic-gate
4357*0Sstevel@tonic-gate# Both modules and distributions know if "force" is in effect by
4358*0Sstevel@tonic-gate# autoinspection, not by inspecting a global variable. One of the
4359*0Sstevel@tonic-gate# reason why this was chosen to work that way was the treatment of
4360*0Sstevel@tonic-gate# dependencies. They should not autpomatically inherit the force
4361*0Sstevel@tonic-gate# status. But this has the downside that ^C and die() will return to
4362*0Sstevel@tonic-gate# the prompt but will not be able to reset the force_update
4363*0Sstevel@tonic-gate# attributes. We try to correct for it currently in the read_metadata
4364*0Sstevel@tonic-gate# routine, and immediately before we check for a Signal. I hope this
4365*0Sstevel@tonic-gate# works out in one of v1.57_53ff
4366*0Sstevel@tonic-gate
4367*0Sstevel@tonic-gatesub force {
4368*0Sstevel@tonic-gate  my($self, $method) = @_;
4369*0Sstevel@tonic-gate  for my $att (qw(
4370*0Sstevel@tonic-gate  MD5_STATUS archived build_dir localfile make install unwrapped
4371*0Sstevel@tonic-gate  writemakefile
4372*0Sstevel@tonic-gate )) {
4373*0Sstevel@tonic-gate    delete $self->{$att};
4374*0Sstevel@tonic-gate  }
4375*0Sstevel@tonic-gate  if ($method && $method eq "install") {
4376*0Sstevel@tonic-gate    $self->{"force_update"}++; # name should probably have been force_install
4377*0Sstevel@tonic-gate  }
4378*0Sstevel@tonic-gate}
4379*0Sstevel@tonic-gate
4380*0Sstevel@tonic-gate#-> sub CPAN::Distribution::unforce ;
4381*0Sstevel@tonic-gatesub unforce {
4382*0Sstevel@tonic-gate  my($self) = @_;
4383*0Sstevel@tonic-gate  delete $self->{'force_update'};
4384*0Sstevel@tonic-gate}
4385*0Sstevel@tonic-gate
4386*0Sstevel@tonic-gate#-> sub CPAN::Distribution::isa_perl ;
4387*0Sstevel@tonic-gatesub isa_perl {
4388*0Sstevel@tonic-gate  my($self) = @_;
4389*0Sstevel@tonic-gate  my $file = File::Basename::basename($self->id);
4390*0Sstevel@tonic-gate  if ($file =~ m{ ^ perl
4391*0Sstevel@tonic-gate                  -?
4392*0Sstevel@tonic-gate		  (5)
4393*0Sstevel@tonic-gate		  ([._-])
4394*0Sstevel@tonic-gate		  (
4395*0Sstevel@tonic-gate                   \d{3}(_[0-4][0-9])?
4396*0Sstevel@tonic-gate                   |
4397*0Sstevel@tonic-gate                   \d*[24680]\.\d+
4398*0Sstevel@tonic-gate                  )
4399*0Sstevel@tonic-gate		  \.tar[._-]gz
4400*0Sstevel@tonic-gate		  (?!\n)\Z
4401*0Sstevel@tonic-gate		}xs){
4402*0Sstevel@tonic-gate    return "$1.$3";
4403*0Sstevel@tonic-gate  } elsif ($self->cpan_comment
4404*0Sstevel@tonic-gate           &&
4405*0Sstevel@tonic-gate           $self->cpan_comment =~ /isa_perl\(.+?\)/){
4406*0Sstevel@tonic-gate    return $1;
4407*0Sstevel@tonic-gate  }
4408*0Sstevel@tonic-gate}
4409*0Sstevel@tonic-gate
4410*0Sstevel@tonic-gate#-> sub CPAN::Distribution::perl ;
4411*0Sstevel@tonic-gatesub perl {
4412*0Sstevel@tonic-gate    my($self) = @_;
4413*0Sstevel@tonic-gate    my($perl) = File::Spec->file_name_is_absolute($^X) ? $^X : "";
4414*0Sstevel@tonic-gate    my $pwd  = CPAN::anycwd();
4415*0Sstevel@tonic-gate    my $candidate = File::Spec->catfile($pwd,$^X);
4416*0Sstevel@tonic-gate    $perl ||= $candidate if MM->maybe_command($candidate);
4417*0Sstevel@tonic-gate    unless ($perl) {
4418*0Sstevel@tonic-gate	my ($component,$perl_name);
4419*0Sstevel@tonic-gate      DIST_PERLNAME: foreach $perl_name ($^X, 'perl', 'perl5', "perl$]") {
4420*0Sstevel@tonic-gate	    PATH_COMPONENT: foreach $component (File::Spec->path(),
4421*0Sstevel@tonic-gate						$Config::Config{'binexp'}) {
4422*0Sstevel@tonic-gate		  next unless defined($component) && $component;
4423*0Sstevel@tonic-gate		  my($abs) = File::Spec->catfile($component,$perl_name);
4424*0Sstevel@tonic-gate		  if (MM->maybe_command($abs)) {
4425*0Sstevel@tonic-gate		      $perl = $abs;
4426*0Sstevel@tonic-gate		      last DIST_PERLNAME;
4427*0Sstevel@tonic-gate		  }
4428*0Sstevel@tonic-gate	      }
4429*0Sstevel@tonic-gate	  }
4430*0Sstevel@tonic-gate    }
4431*0Sstevel@tonic-gate    $perl;
4432*0Sstevel@tonic-gate}
4433*0Sstevel@tonic-gate
4434*0Sstevel@tonic-gate#-> sub CPAN::Distribution::make ;
4435*0Sstevel@tonic-gatesub make {
4436*0Sstevel@tonic-gate    my($self) = @_;
4437*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(sprintf "Running make for %s\n", $self->id);
4438*0Sstevel@tonic-gate    # Emergency brake if they said install Pippi and get newest perl
4439*0Sstevel@tonic-gate    if ($self->isa_perl) {
4440*0Sstevel@tonic-gate      if (
4441*0Sstevel@tonic-gate	  $self->called_for ne $self->id &&
4442*0Sstevel@tonic-gate          ! $self->{force_update}
4443*0Sstevel@tonic-gate	 ) {
4444*0Sstevel@tonic-gate        # if we die here, we break bundles
4445*0Sstevel@tonic-gate	$CPAN::Frontend->mywarn(sprintf qq{
4446*0Sstevel@tonic-gateThe most recent version "%s" of the module "%s"
4447*0Sstevel@tonic-gatecomes with the current version of perl (%s).
4448*0Sstevel@tonic-gateI\'ll build that only if you ask for something like
4449*0Sstevel@tonic-gate    force install %s
4450*0Sstevel@tonic-gateor
4451*0Sstevel@tonic-gate    install %s
4452*0Sstevel@tonic-gate},
4453*0Sstevel@tonic-gate			       $CPAN::META->instance(
4454*0Sstevel@tonic-gate						     'CPAN::Module',
4455*0Sstevel@tonic-gate						     $self->called_for
4456*0Sstevel@tonic-gate						    )->cpan_version,
4457*0Sstevel@tonic-gate			       $self->called_for,
4458*0Sstevel@tonic-gate			       $self->isa_perl,
4459*0Sstevel@tonic-gate			       $self->called_for,
4460*0Sstevel@tonic-gate			       $self->id);
4461*0Sstevel@tonic-gate        sleep 5; return;
4462*0Sstevel@tonic-gate      }
4463*0Sstevel@tonic-gate    }
4464*0Sstevel@tonic-gate    $self->get;
4465*0Sstevel@tonic-gate  EXCUSE: {
4466*0Sstevel@tonic-gate	my @e;
4467*0Sstevel@tonic-gate	$self->{archived} eq "NO" and push @e,
4468*0Sstevel@tonic-gate	"Is neither a tar nor a zip archive.";
4469*0Sstevel@tonic-gate
4470*0Sstevel@tonic-gate	$self->{unwrapped} eq "NO" and push @e,
4471*0Sstevel@tonic-gate	"had problems unarchiving. Please build manually";
4472*0Sstevel@tonic-gate
4473*0Sstevel@tonic-gate	exists $self->{writemakefile} &&
4474*0Sstevel@tonic-gate	    $self->{writemakefile} =~ m/ ^ NO\s* ( .* ) /sx and push @e,
4475*0Sstevel@tonic-gate		$1 || "Had some problem writing Makefile";
4476*0Sstevel@tonic-gate
4477*0Sstevel@tonic-gate	defined $self->{'make'} and push @e,
4478*0Sstevel@tonic-gate            "Has already been processed within this session";
4479*0Sstevel@tonic-gate
4480*0Sstevel@tonic-gate        exists $self->{later} and length($self->{later}) and
4481*0Sstevel@tonic-gate            push @e, $self->{later};
4482*0Sstevel@tonic-gate
4483*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(join "", map {"  $_\n"} @e) and return if @e;
4484*0Sstevel@tonic-gate    }
4485*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("\n  CPAN.pm: Going to build ".$self->id."\n\n");
4486*0Sstevel@tonic-gate    my $builddir = $self->dir;
4487*0Sstevel@tonic-gate    chdir $builddir or Carp::croak("Couldn't chdir $builddir: $!");
4488*0Sstevel@tonic-gate    $self->debug("Changed directory to $builddir") if $CPAN::DEBUG;
4489*0Sstevel@tonic-gate
4490*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
4491*0Sstevel@tonic-gate        Mac::BuildTools::make($self);
4492*0Sstevel@tonic-gate        return;
4493*0Sstevel@tonic-gate    }
4494*0Sstevel@tonic-gate
4495*0Sstevel@tonic-gate    my $system;
4496*0Sstevel@tonic-gate    if ($self->{'configure'}) {
4497*0Sstevel@tonic-gate      $system = $self->{'configure'};
4498*0Sstevel@tonic-gate    } else {
4499*0Sstevel@tonic-gate	my($perl) = $self->perl or die "Couldn\'t find executable perl\n";
4500*0Sstevel@tonic-gate	my $switch = "";
4501*0Sstevel@tonic-gate# This needs a handler that can be turned on or off:
4502*0Sstevel@tonic-gate#	$switch = "-MExtUtils::MakeMaker ".
4503*0Sstevel@tonic-gate#	    "-Mops=:default,:filesys_read,:filesys_open,require,chdir"
4504*0Sstevel@tonic-gate#	    if $] > 5.00310;
4505*0Sstevel@tonic-gate	$system = "$perl $switch Makefile.PL $CPAN::Config->{makepl_arg}";
4506*0Sstevel@tonic-gate    }
4507*0Sstevel@tonic-gate    unless (exists $self->{writemakefile}) {
4508*0Sstevel@tonic-gate	local($SIG{ALRM}) = sub { die "inactivity_timeout reached\n" };
4509*0Sstevel@tonic-gate	my($ret,$pid);
4510*0Sstevel@tonic-gate	$@ = "";
4511*0Sstevel@tonic-gate	if ($CPAN::Config->{inactivity_timeout}) {
4512*0Sstevel@tonic-gate	    eval {
4513*0Sstevel@tonic-gate		alarm $CPAN::Config->{inactivity_timeout};
4514*0Sstevel@tonic-gate		local $SIG{CHLD}; # = sub { wait };
4515*0Sstevel@tonic-gate		if (defined($pid = fork)) {
4516*0Sstevel@tonic-gate		    if ($pid) { #parent
4517*0Sstevel@tonic-gate			# wait;
4518*0Sstevel@tonic-gate			waitpid $pid, 0;
4519*0Sstevel@tonic-gate		    } else {    #child
4520*0Sstevel@tonic-gate		      # note, this exec isn't necessary if
4521*0Sstevel@tonic-gate		      # inactivity_timeout is 0. On the Mac I'd
4522*0Sstevel@tonic-gate		      # suggest, we set it always to 0.
4523*0Sstevel@tonic-gate		      exec $system;
4524*0Sstevel@tonic-gate		    }
4525*0Sstevel@tonic-gate		} else {
4526*0Sstevel@tonic-gate		    $CPAN::Frontend->myprint("Cannot fork: $!");
4527*0Sstevel@tonic-gate		    return;
4528*0Sstevel@tonic-gate		}
4529*0Sstevel@tonic-gate	    };
4530*0Sstevel@tonic-gate	    alarm 0;
4531*0Sstevel@tonic-gate	    if ($@){
4532*0Sstevel@tonic-gate		kill 9, $pid;
4533*0Sstevel@tonic-gate		waitpid $pid, 0;
4534*0Sstevel@tonic-gate		$CPAN::Frontend->myprint($@);
4535*0Sstevel@tonic-gate		$self->{writemakefile} = "NO $@";
4536*0Sstevel@tonic-gate		$@ = "";
4537*0Sstevel@tonic-gate		return;
4538*0Sstevel@tonic-gate	    }
4539*0Sstevel@tonic-gate	} else {
4540*0Sstevel@tonic-gate	  $ret = system($system);
4541*0Sstevel@tonic-gate	  if ($ret != 0) {
4542*0Sstevel@tonic-gate	    $self->{writemakefile} = "NO Makefile.PL returned status $ret";
4543*0Sstevel@tonic-gate	    return;
4544*0Sstevel@tonic-gate	  }
4545*0Sstevel@tonic-gate	}
4546*0Sstevel@tonic-gate	if (-f "Makefile") {
4547*0Sstevel@tonic-gate	  $self->{writemakefile} = "YES";
4548*0Sstevel@tonic-gate          delete $self->{make_clean}; # if cleaned before, enable next
4549*0Sstevel@tonic-gate	} else {
4550*0Sstevel@tonic-gate	  $self->{writemakefile} =
4551*0Sstevel@tonic-gate	      qq{NO Makefile.PL refused to write a Makefile.};
4552*0Sstevel@tonic-gate	  # It's probably worth it to record the reason, so let's retry
4553*0Sstevel@tonic-gate	  # local $/;
4554*0Sstevel@tonic-gate	  # my $fh = IO::File->new("$system |"); # STDERR? STDIN?
4555*0Sstevel@tonic-gate	  # $self->{writemakefile} .= <$fh>;
4556*0Sstevel@tonic-gate	}
4557*0Sstevel@tonic-gate    }
4558*0Sstevel@tonic-gate    if ($CPAN::Signal){
4559*0Sstevel@tonic-gate      delete $self->{force_update};
4560*0Sstevel@tonic-gate      return;
4561*0Sstevel@tonic-gate    }
4562*0Sstevel@tonic-gate    if (my @prereq = $self->unsat_prereq){
4563*0Sstevel@tonic-gate      return 1 if $self->follow_prereqs(@prereq); # signal success to the queuerunner
4564*0Sstevel@tonic-gate    }
4565*0Sstevel@tonic-gate    $system = join " ", $CPAN::Config->{'make'}, $CPAN::Config->{make_arg};
4566*0Sstevel@tonic-gate    if (system($system) == 0) {
4567*0Sstevel@tonic-gate	 $CPAN::Frontend->myprint("  $system -- OK\n");
4568*0Sstevel@tonic-gate	 $self->{'make'} = "YES";
4569*0Sstevel@tonic-gate    } else {
4570*0Sstevel@tonic-gate	 $self->{writemakefile} ||= "YES";
4571*0Sstevel@tonic-gate	 $self->{'make'} = "NO";
4572*0Sstevel@tonic-gate	 $CPAN::Frontend->myprint("  $system -- NOT OK\n");
4573*0Sstevel@tonic-gate    }
4574*0Sstevel@tonic-gate}
4575*0Sstevel@tonic-gate
4576*0Sstevel@tonic-gatesub follow_prereqs {
4577*0Sstevel@tonic-gate    my($self) = shift;
4578*0Sstevel@tonic-gate    my(@prereq) = @_;
4579*0Sstevel@tonic-gate    my $id = $self->id;
4580*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("---- Unsatisfied dependencies detected ".
4581*0Sstevel@tonic-gate                             "during [$id] -----\n");
4582*0Sstevel@tonic-gate
4583*0Sstevel@tonic-gate    for my $p (@prereq) {
4584*0Sstevel@tonic-gate	$CPAN::Frontend->myprint("    $p\n");
4585*0Sstevel@tonic-gate    }
4586*0Sstevel@tonic-gate    my $follow = 0;
4587*0Sstevel@tonic-gate    if ($CPAN::Config->{prerequisites_policy} eq "follow") {
4588*0Sstevel@tonic-gate	$follow = 1;
4589*0Sstevel@tonic-gate    } elsif ($CPAN::Config->{prerequisites_policy} eq "ask") {
4590*0Sstevel@tonic-gate	require ExtUtils::MakeMaker;
4591*0Sstevel@tonic-gate	my $answer = ExtUtils::MakeMaker::prompt(
4592*0Sstevel@tonic-gate"Shall I follow them and prepend them to the queue
4593*0Sstevel@tonic-gateof modules we are processing right now?", "yes");
4594*0Sstevel@tonic-gate	$follow = $answer =~ /^\s*y/i;
4595*0Sstevel@tonic-gate    } else {
4596*0Sstevel@tonic-gate	local($") = ", ";
4597*0Sstevel@tonic-gate	$CPAN::Frontend->
4598*0Sstevel@tonic-gate            myprint("  Ignoring dependencies on modules @prereq\n");
4599*0Sstevel@tonic-gate    }
4600*0Sstevel@tonic-gate    if ($follow) {
4601*0Sstevel@tonic-gate        # color them as dirty
4602*0Sstevel@tonic-gate        for my $p (@prereq) {
4603*0Sstevel@tonic-gate            # warn "calling color_cmd_tmps(0,1)";
4604*0Sstevel@tonic-gate            CPAN::Shell->expandany($p)->color_cmd_tmps(0,1);
4605*0Sstevel@tonic-gate        }
4606*0Sstevel@tonic-gate        CPAN::Queue->jumpqueue(@prereq,$id); # queue them and requeue yourself
4607*0Sstevel@tonic-gate        $self->{later} = "Delayed until after prerequisites";
4608*0Sstevel@tonic-gate        return 1; # signal success to the queuerunner
4609*0Sstevel@tonic-gate    }
4610*0Sstevel@tonic-gate}
4611*0Sstevel@tonic-gate
4612*0Sstevel@tonic-gate#-> sub CPAN::Distribution::unsat_prereq ;
4613*0Sstevel@tonic-gatesub unsat_prereq {
4614*0Sstevel@tonic-gate    my($self) = @_;
4615*0Sstevel@tonic-gate    my $prereq_pm = $self->prereq_pm or return;
4616*0Sstevel@tonic-gate    my(@need);
4617*0Sstevel@tonic-gate  NEED: while (my($need_module, $need_version) = each %$prereq_pm) {
4618*0Sstevel@tonic-gate        my $nmo = $CPAN::META->instance("CPAN::Module",$need_module);
4619*0Sstevel@tonic-gate        # we were too demanding:
4620*0Sstevel@tonic-gate        next if $nmo->uptodate;
4621*0Sstevel@tonic-gate
4622*0Sstevel@tonic-gate        # if they have not specified a version, we accept any installed one
4623*0Sstevel@tonic-gate        if (not defined $need_version or
4624*0Sstevel@tonic-gate           $need_version == 0 or
4625*0Sstevel@tonic-gate           $need_version eq "undef") {
4626*0Sstevel@tonic-gate            next if defined $nmo->inst_file;
4627*0Sstevel@tonic-gate        }
4628*0Sstevel@tonic-gate
4629*0Sstevel@tonic-gate        # We only want to install prereqs if either they're not installed
4630*0Sstevel@tonic-gate        # or if the installed version is too old. We cannot omit this
4631*0Sstevel@tonic-gate        # check, because if 'force' is in effect, nobody else will check.
4632*0Sstevel@tonic-gate        {
4633*0Sstevel@tonic-gate            local($^W) = 0;
4634*0Sstevel@tonic-gate            if (
4635*0Sstevel@tonic-gate                defined $nmo->inst_file &&
4636*0Sstevel@tonic-gate                ! CPAN::Version->vgt($need_version, $nmo->inst_version)
4637*0Sstevel@tonic-gate               ){
4638*0Sstevel@tonic-gate                CPAN->debug(sprintf "id[%s]inst_file[%s]inst_version[%s]need_version[%s]",
4639*0Sstevel@tonic-gate                            $nmo->id,
4640*0Sstevel@tonic-gate                            $nmo->inst_file,
4641*0Sstevel@tonic-gate                            $nmo->inst_version,
4642*0Sstevel@tonic-gate                            CPAN::Version->readable($need_version)
4643*0Sstevel@tonic-gate                           );
4644*0Sstevel@tonic-gate                next NEED;
4645*0Sstevel@tonic-gate            }
4646*0Sstevel@tonic-gate        }
4647*0Sstevel@tonic-gate
4648*0Sstevel@tonic-gate        if ($self->{sponsored_mods}{$need_module}++){
4649*0Sstevel@tonic-gate            # We have already sponsored it and for some reason it's still
4650*0Sstevel@tonic-gate            # not available. So we do nothing. Or what should we do?
4651*0Sstevel@tonic-gate            # if we push it again, we have a potential infinite loop
4652*0Sstevel@tonic-gate            next;
4653*0Sstevel@tonic-gate        }
4654*0Sstevel@tonic-gate        push @need, $need_module;
4655*0Sstevel@tonic-gate    }
4656*0Sstevel@tonic-gate    @need;
4657*0Sstevel@tonic-gate}
4658*0Sstevel@tonic-gate
4659*0Sstevel@tonic-gate#-> sub CPAN::Distribution::prereq_pm ;
4660*0Sstevel@tonic-gatesub prereq_pm {
4661*0Sstevel@tonic-gate  my($self) = @_;
4662*0Sstevel@tonic-gate  return $self->{prereq_pm} if
4663*0Sstevel@tonic-gate      exists $self->{prereq_pm_detected} && $self->{prereq_pm_detected};
4664*0Sstevel@tonic-gate  return unless $self->{writemakefile}; # no need to have succeeded
4665*0Sstevel@tonic-gate                                        # but we must have run it
4666*0Sstevel@tonic-gate  my $build_dir = $self->{build_dir} or die "Panic: no build_dir?";
4667*0Sstevel@tonic-gate  my $makefile = File::Spec->catfile($build_dir,"Makefile");
4668*0Sstevel@tonic-gate  my(%p) = ();
4669*0Sstevel@tonic-gate  my $fh;
4670*0Sstevel@tonic-gate  if (-f $makefile
4671*0Sstevel@tonic-gate      and
4672*0Sstevel@tonic-gate      $fh = FileHandle->new("<$makefile\0")) {
4673*0Sstevel@tonic-gate
4674*0Sstevel@tonic-gate      local($/) = "\n";
4675*0Sstevel@tonic-gate
4676*0Sstevel@tonic-gate      #  A.Speer @p -> %p, where %p is $p{Module::Name}=Required_Version
4677*0Sstevel@tonic-gate      while (<$fh>) {
4678*0Sstevel@tonic-gate          last if /MakeMaker post_initialize section/;
4679*0Sstevel@tonic-gate          my($p) = m{^[\#]
4680*0Sstevel@tonic-gate		 \s+PREREQ_PM\s+=>\s+(.+)
4681*0Sstevel@tonic-gate		 }x;
4682*0Sstevel@tonic-gate          next unless $p;
4683*0Sstevel@tonic-gate          # warn "Found prereq expr[$p]";
4684*0Sstevel@tonic-gate
4685*0Sstevel@tonic-gate          #  Regexp modified by A.Speer to remember actual version of file
4686*0Sstevel@tonic-gate          #  PREREQ_PM hash key wants, then add to
4687*0Sstevel@tonic-gate          while ( $p =~ m/(?:\s)([\w\:]+)=>q\[(.*?)\],?/g ){
4688*0Sstevel@tonic-gate              # In case a prereq is mentioned twice, complain.
4689*0Sstevel@tonic-gate              if ( defined $p{$1} ) {
4690*0Sstevel@tonic-gate                  warn "Warning: PREREQ_PM mentions $1 more than once, last mention wins";
4691*0Sstevel@tonic-gate              }
4692*0Sstevel@tonic-gate              $p{$1} = $2;
4693*0Sstevel@tonic-gate          }
4694*0Sstevel@tonic-gate          last;
4695*0Sstevel@tonic-gate      }
4696*0Sstevel@tonic-gate  }
4697*0Sstevel@tonic-gate  $self->{prereq_pm_detected}++;
4698*0Sstevel@tonic-gate  return $self->{prereq_pm} = \%p;
4699*0Sstevel@tonic-gate}
4700*0Sstevel@tonic-gate
4701*0Sstevel@tonic-gate#-> sub CPAN::Distribution::test ;
4702*0Sstevel@tonic-gatesub test {
4703*0Sstevel@tonic-gate    my($self) = @_;
4704*0Sstevel@tonic-gate    $self->make;
4705*0Sstevel@tonic-gate    if ($CPAN::Signal){
4706*0Sstevel@tonic-gate      delete $self->{force_update};
4707*0Sstevel@tonic-gate      return;
4708*0Sstevel@tonic-gate    }
4709*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Running make test\n");
4710*0Sstevel@tonic-gate    if (my @prereq = $self->unsat_prereq){
4711*0Sstevel@tonic-gate      return 1 if $self->follow_prereqs(@prereq); # signal success to the queuerunner
4712*0Sstevel@tonic-gate    }
4713*0Sstevel@tonic-gate  EXCUSE: {
4714*0Sstevel@tonic-gate	my @e;
4715*0Sstevel@tonic-gate	exists $self->{make} or exists $self->{later} or push @e,
4716*0Sstevel@tonic-gate	"Make had some problems, maybe interrupted? Won't test";
4717*0Sstevel@tonic-gate
4718*0Sstevel@tonic-gate	exists $self->{'make'} and
4719*0Sstevel@tonic-gate	    $self->{'make'} eq 'NO' and
4720*0Sstevel@tonic-gate		push @e, "Can't test without successful make";
4721*0Sstevel@tonic-gate
4722*0Sstevel@tonic-gate	exists $self->{build_dir} or push @e, "Has no own directory";
4723*0Sstevel@tonic-gate        $self->{badtestcnt} ||= 0;
4724*0Sstevel@tonic-gate        $self->{badtestcnt} > 0 and
4725*0Sstevel@tonic-gate            push @e, "Won't repeat unsuccessful test during this command";
4726*0Sstevel@tonic-gate
4727*0Sstevel@tonic-gate        exists $self->{later} and length($self->{later}) and
4728*0Sstevel@tonic-gate            push @e, $self->{later};
4729*0Sstevel@tonic-gate
4730*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(join "", map {"  $_\n"} @e) and return if @e;
4731*0Sstevel@tonic-gate    }
4732*0Sstevel@tonic-gate    chdir $self->{'build_dir'} or
4733*0Sstevel@tonic-gate	Carp::croak("Couldn't chdir to $self->{'build_dir'}");
4734*0Sstevel@tonic-gate    $self->debug("Changed directory to $self->{'build_dir'}")
4735*0Sstevel@tonic-gate	if $CPAN::DEBUG;
4736*0Sstevel@tonic-gate
4737*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
4738*0Sstevel@tonic-gate        Mac::BuildTools::make_test($self);
4739*0Sstevel@tonic-gate        return;
4740*0Sstevel@tonic-gate    }
4741*0Sstevel@tonic-gate
4742*0Sstevel@tonic-gate    local $ENV{PERL5LIB} = $ENV{PERL5LIB} || "";
4743*0Sstevel@tonic-gate    $CPAN::META->set_perl5lib;
4744*0Sstevel@tonic-gate    my $system = join " ", $CPAN::Config->{'make'}, "test";
4745*0Sstevel@tonic-gate    if (system($system) == 0) {
4746*0Sstevel@tonic-gate	 $CPAN::Frontend->myprint("  $system -- OK\n");
4747*0Sstevel@tonic-gate	 $CPAN::META->is_tested($self->{'build_dir'});
4748*0Sstevel@tonic-gate	 $self->{make_test} = "YES";
4749*0Sstevel@tonic-gate    } else {
4750*0Sstevel@tonic-gate	 $self->{make_test} = "NO";
4751*0Sstevel@tonic-gate         $self->{badtestcnt}++;
4752*0Sstevel@tonic-gate	 $CPAN::Frontend->myprint("  $system -- NOT OK\n");
4753*0Sstevel@tonic-gate    }
4754*0Sstevel@tonic-gate}
4755*0Sstevel@tonic-gate
4756*0Sstevel@tonic-gate#-> sub CPAN::Distribution::clean ;
4757*0Sstevel@tonic-gatesub clean {
4758*0Sstevel@tonic-gate    my($self) = @_;
4759*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Running make clean\n");
4760*0Sstevel@tonic-gate  EXCUSE: {
4761*0Sstevel@tonic-gate	my @e;
4762*0Sstevel@tonic-gate        exists $self->{make_clean} and $self->{make_clean} eq "YES" and
4763*0Sstevel@tonic-gate            push @e, "make clean already called once";
4764*0Sstevel@tonic-gate	exists $self->{build_dir} or push @e, "Has no own directory";
4765*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(join "", map {"  $_\n"} @e) and return if @e;
4766*0Sstevel@tonic-gate    }
4767*0Sstevel@tonic-gate    chdir $self->{'build_dir'} or
4768*0Sstevel@tonic-gate	Carp::croak("Couldn't chdir to $self->{'build_dir'}");
4769*0Sstevel@tonic-gate    $self->debug("Changed directory to $self->{'build_dir'}") if $CPAN::DEBUG;
4770*0Sstevel@tonic-gate
4771*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
4772*0Sstevel@tonic-gate        Mac::BuildTools::make_clean($self);
4773*0Sstevel@tonic-gate        return;
4774*0Sstevel@tonic-gate    }
4775*0Sstevel@tonic-gate
4776*0Sstevel@tonic-gate    my $system = join " ", $CPAN::Config->{'make'}, "clean";
4777*0Sstevel@tonic-gate    if (system($system) == 0) {
4778*0Sstevel@tonic-gate      $CPAN::Frontend->myprint("  $system -- OK\n");
4779*0Sstevel@tonic-gate
4780*0Sstevel@tonic-gate      # $self->force;
4781*0Sstevel@tonic-gate
4782*0Sstevel@tonic-gate      # Jost Krieger pointed out that this "force" was wrong because
4783*0Sstevel@tonic-gate      # it has the effect that the next "install" on this distribution
4784*0Sstevel@tonic-gate      # will untar everything again. Instead we should bring the
4785*0Sstevel@tonic-gate      # object's state back to where it is after untarring.
4786*0Sstevel@tonic-gate
4787*0Sstevel@tonic-gate      delete $self->{force_update};
4788*0Sstevel@tonic-gate      delete $self->{install};
4789*0Sstevel@tonic-gate      delete $self->{writemakefile};
4790*0Sstevel@tonic-gate      delete $self->{make};
4791*0Sstevel@tonic-gate      delete $self->{make_test}; # no matter if yes or no, tests must be redone
4792*0Sstevel@tonic-gate      $self->{make_clean} = "YES";
4793*0Sstevel@tonic-gate
4794*0Sstevel@tonic-gate    } else {
4795*0Sstevel@tonic-gate      # Hmmm, what to do if make clean failed?
4796*0Sstevel@tonic-gate
4797*0Sstevel@tonic-gate      $CPAN::Frontend->myprint(qq{  $system -- NOT OK
4798*0Sstevel@tonic-gate
4799*0Sstevel@tonic-gatemake clean did not succeed, marking directory as unusable for further work.
4800*0Sstevel@tonic-gate});
4801*0Sstevel@tonic-gate      $self->force("make"); # so that this directory won't be used again
4802*0Sstevel@tonic-gate
4803*0Sstevel@tonic-gate    }
4804*0Sstevel@tonic-gate}
4805*0Sstevel@tonic-gate
4806*0Sstevel@tonic-gate#-> sub CPAN::Distribution::install ;
4807*0Sstevel@tonic-gatesub install {
4808*0Sstevel@tonic-gate    my($self) = @_;
4809*0Sstevel@tonic-gate    $self->test;
4810*0Sstevel@tonic-gate    if ($CPAN::Signal){
4811*0Sstevel@tonic-gate      delete $self->{force_update};
4812*0Sstevel@tonic-gate      return;
4813*0Sstevel@tonic-gate    }
4814*0Sstevel@tonic-gate    $CPAN::Frontend->myprint("Running make install\n");
4815*0Sstevel@tonic-gate  EXCUSE: {
4816*0Sstevel@tonic-gate	my @e;
4817*0Sstevel@tonic-gate	exists $self->{build_dir} or push @e, "Has no own directory";
4818*0Sstevel@tonic-gate
4819*0Sstevel@tonic-gate	exists $self->{make} or exists $self->{later} or push @e,
4820*0Sstevel@tonic-gate	"Make had some problems, maybe interrupted? Won't install";
4821*0Sstevel@tonic-gate
4822*0Sstevel@tonic-gate	exists $self->{'make'} and
4823*0Sstevel@tonic-gate	    $self->{'make'} eq 'NO' and
4824*0Sstevel@tonic-gate		push @e, "make had returned bad status, install seems impossible";
4825*0Sstevel@tonic-gate
4826*0Sstevel@tonic-gate	push @e, "make test had returned bad status, ".
4827*0Sstevel@tonic-gate	    "won't install without force"
4828*0Sstevel@tonic-gate	    if exists $self->{'make_test'} and
4829*0Sstevel@tonic-gate	    $self->{'make_test'} eq 'NO' and
4830*0Sstevel@tonic-gate	    ! $self->{'force_update'};
4831*0Sstevel@tonic-gate
4832*0Sstevel@tonic-gate	exists $self->{'install'} and push @e,
4833*0Sstevel@tonic-gate	$self->{'install'} eq "YES" ?
4834*0Sstevel@tonic-gate	    "Already done" : "Already tried without success";
4835*0Sstevel@tonic-gate
4836*0Sstevel@tonic-gate        exists $self->{later} and length($self->{later}) and
4837*0Sstevel@tonic-gate            push @e, $self->{later};
4838*0Sstevel@tonic-gate
4839*0Sstevel@tonic-gate	$CPAN::Frontend->myprint(join "", map {"  $_\n"} @e) and return if @e;
4840*0Sstevel@tonic-gate    }
4841*0Sstevel@tonic-gate    chdir $self->{'build_dir'} or
4842*0Sstevel@tonic-gate	Carp::croak("Couldn't chdir to $self->{'build_dir'}");
4843*0Sstevel@tonic-gate    $self->debug("Changed directory to $self->{'build_dir'}")
4844*0Sstevel@tonic-gate	if $CPAN::DEBUG;
4845*0Sstevel@tonic-gate
4846*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
4847*0Sstevel@tonic-gate        Mac::BuildTools::make_install($self);
4848*0Sstevel@tonic-gate        return;
4849*0Sstevel@tonic-gate    }
4850*0Sstevel@tonic-gate
4851*0Sstevel@tonic-gate    my $system = join(" ", $CPAN::Config->{'make'},
4852*0Sstevel@tonic-gate		      "install", $CPAN::Config->{make_install_arg});
4853*0Sstevel@tonic-gate    my($stderr) = $^O =~ /Win/i ? "" : " 2>&1 ";
4854*0Sstevel@tonic-gate    my($pipe) = FileHandle->new("$system $stderr |");
4855*0Sstevel@tonic-gate    my($makeout) = "";
4856*0Sstevel@tonic-gate    while (<$pipe>){
4857*0Sstevel@tonic-gate	$CPAN::Frontend->myprint($_);
4858*0Sstevel@tonic-gate	$makeout .= $_;
4859*0Sstevel@tonic-gate    }
4860*0Sstevel@tonic-gate    $pipe->close;
4861*0Sstevel@tonic-gate    if ($?==0) {
4862*0Sstevel@tonic-gate	 $CPAN::Frontend->myprint("  $system -- OK\n");
4863*0Sstevel@tonic-gate	 $CPAN::META->is_installed($self->{'build_dir'});
4864*0Sstevel@tonic-gate	 return $self->{'install'} = "YES";
4865*0Sstevel@tonic-gate    } else {
4866*0Sstevel@tonic-gate	 $self->{'install'} = "NO";
4867*0Sstevel@tonic-gate	 $CPAN::Frontend->myprint("  $system -- NOT OK\n");
4868*0Sstevel@tonic-gate	 if ($makeout =~ /permission/s && $> > 0) {
4869*0Sstevel@tonic-gate	     $CPAN::Frontend->myprint(qq{    You may have to su }.
4870*0Sstevel@tonic-gate				      qq{to root to install the package\n});
4871*0Sstevel@tonic-gate	 }
4872*0Sstevel@tonic-gate    }
4873*0Sstevel@tonic-gate    delete $self->{force_update};
4874*0Sstevel@tonic-gate}
4875*0Sstevel@tonic-gate
4876*0Sstevel@tonic-gate#-> sub CPAN::Distribution::dir ;
4877*0Sstevel@tonic-gatesub dir {
4878*0Sstevel@tonic-gate    shift->{'build_dir'};
4879*0Sstevel@tonic-gate}
4880*0Sstevel@tonic-gate
4881*0Sstevel@tonic-gatepackage CPAN::Bundle;
4882*0Sstevel@tonic-gate
4883*0Sstevel@tonic-gatesub look {
4884*0Sstevel@tonic-gate    my $self = shift;
4885*0Sstevel@tonic-gate    $CPAN::Frontend->myprint($self->as_string);
4886*0Sstevel@tonic-gate}
4887*0Sstevel@tonic-gate
4888*0Sstevel@tonic-gatesub undelay {
4889*0Sstevel@tonic-gate    my $self = shift;
4890*0Sstevel@tonic-gate    delete $self->{later};
4891*0Sstevel@tonic-gate    for my $c ( $self->contains ) {
4892*0Sstevel@tonic-gate        my $obj = CPAN::Shell->expandany($c) or next;
4893*0Sstevel@tonic-gate        $obj->undelay;
4894*0Sstevel@tonic-gate    }
4895*0Sstevel@tonic-gate}
4896*0Sstevel@tonic-gate
4897*0Sstevel@tonic-gate#-> sub CPAN::Bundle::color_cmd_tmps ;
4898*0Sstevel@tonic-gatesub color_cmd_tmps {
4899*0Sstevel@tonic-gate    my($self) = shift;
4900*0Sstevel@tonic-gate    my($depth) = shift || 0;
4901*0Sstevel@tonic-gate    my($color) = shift || 0;
4902*0Sstevel@tonic-gate    my($ancestors) = shift || [];
4903*0Sstevel@tonic-gate    # a module needs to recurse to its cpan_file, a distribution needs
4904*0Sstevel@tonic-gate    # to recurse into its prereq_pms, a bundle needs to recurse into its modules
4905*0Sstevel@tonic-gate
4906*0Sstevel@tonic-gate    return if exists $self->{incommandcolor}
4907*0Sstevel@tonic-gate        && $self->{incommandcolor}==$color;
4908*0Sstevel@tonic-gate    if ($depth>=100){
4909*0Sstevel@tonic-gate        $CPAN::Frontend->mydie(CPAN::Exception::RecursiveDependency->new($ancestors));
4910*0Sstevel@tonic-gate    }
4911*0Sstevel@tonic-gate    # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1;
4912*0Sstevel@tonic-gate
4913*0Sstevel@tonic-gate    for my $c ( $self->contains ) {
4914*0Sstevel@tonic-gate        my $obj = CPAN::Shell->expandany($c) or next;
4915*0Sstevel@tonic-gate        CPAN->debug("c[$c]obj[$obj]") if $CPAN::DEBUG;
4916*0Sstevel@tonic-gate        $obj->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]);
4917*0Sstevel@tonic-gate    }
4918*0Sstevel@tonic-gate    if ($color==0) {
4919*0Sstevel@tonic-gate        delete $self->{badtestcnt};
4920*0Sstevel@tonic-gate    }
4921*0Sstevel@tonic-gate    $self->{incommandcolor} = $color;
4922*0Sstevel@tonic-gate}
4923*0Sstevel@tonic-gate
4924*0Sstevel@tonic-gate#-> sub CPAN::Bundle::as_string ;
4925*0Sstevel@tonic-gatesub as_string {
4926*0Sstevel@tonic-gate    my($self) = @_;
4927*0Sstevel@tonic-gate    $self->contains;
4928*0Sstevel@tonic-gate    # following line must be "=", not "||=" because we have a moving target
4929*0Sstevel@tonic-gate    $self->{INST_VERSION} = $self->inst_version;
4930*0Sstevel@tonic-gate    return $self->SUPER::as_string;
4931*0Sstevel@tonic-gate}
4932*0Sstevel@tonic-gate
4933*0Sstevel@tonic-gate#-> sub CPAN::Bundle::contains ;
4934*0Sstevel@tonic-gatesub contains {
4935*0Sstevel@tonic-gate    my($self) = @_;
4936*0Sstevel@tonic-gate    my($inst_file) = $self->inst_file || "";
4937*0Sstevel@tonic-gate    my($id) = $self->id;
4938*0Sstevel@tonic-gate    $self->debug("inst_file[$inst_file]id[$id]") if $CPAN::DEBUG;
4939*0Sstevel@tonic-gate    unless ($inst_file) {
4940*0Sstevel@tonic-gate        # Try to get at it in the cpan directory
4941*0Sstevel@tonic-gate        $self->debug("no inst_file") if $CPAN::DEBUG;
4942*0Sstevel@tonic-gate        my $cpan_file;
4943*0Sstevel@tonic-gate        $CPAN::Frontend->mydie("I don't know a bundle with ID $id\n") unless
4944*0Sstevel@tonic-gate              $cpan_file = $self->cpan_file;
4945*0Sstevel@tonic-gate        if ($cpan_file eq "N/A") {
4946*0Sstevel@tonic-gate            $CPAN::Frontend->mydie("Bundle $id not found on disk and not on CPAN.
4947*0Sstevel@tonic-gate  Maybe stale symlink? Maybe removed during session? Giving up.\n");
4948*0Sstevel@tonic-gate        }
4949*0Sstevel@tonic-gate        my $dist = $CPAN::META->instance('CPAN::Distribution',
4950*0Sstevel@tonic-gate                                         $self->cpan_file);
4951*0Sstevel@tonic-gate        $dist->get;
4952*0Sstevel@tonic-gate        $self->debug($dist->as_string) if $CPAN::DEBUG;
4953*0Sstevel@tonic-gate        my($todir) = $CPAN::Config->{'cpan_home'};
4954*0Sstevel@tonic-gate        my(@me,$from,$to,$me);
4955*0Sstevel@tonic-gate        @me = split /::/, $self->id;
4956*0Sstevel@tonic-gate        $me[-1] .= ".pm";
4957*0Sstevel@tonic-gate        $me = File::Spec->catfile(@me);
4958*0Sstevel@tonic-gate        $from = $self->find_bundle_file($dist->{'build_dir'},$me);
4959*0Sstevel@tonic-gate        $to = File::Spec->catfile($todir,$me);
4960*0Sstevel@tonic-gate        File::Path::mkpath(File::Basename::dirname($to));
4961*0Sstevel@tonic-gate        File::Copy::copy($from, $to)
4962*0Sstevel@tonic-gate              or Carp::confess("Couldn't copy $from to $to: $!");
4963*0Sstevel@tonic-gate        $inst_file = $to;
4964*0Sstevel@tonic-gate    }
4965*0Sstevel@tonic-gate    my @result;
4966*0Sstevel@tonic-gate    my $fh = FileHandle->new;
4967*0Sstevel@tonic-gate    local $/ = "\n";
4968*0Sstevel@tonic-gate    open($fh,$inst_file) or die "Could not open '$inst_file': $!";
4969*0Sstevel@tonic-gate    my $in_cont = 0;
4970*0Sstevel@tonic-gate    $self->debug("inst_file[$inst_file]") if $CPAN::DEBUG;
4971*0Sstevel@tonic-gate    while (<$fh>) {
4972*0Sstevel@tonic-gate        $in_cont = m/^=(?!head1\s+CONTENTS)/ ? 0 :
4973*0Sstevel@tonic-gate            m/^=head1\s+CONTENTS/ ? 1 : $in_cont;
4974*0Sstevel@tonic-gate        next unless $in_cont;
4975*0Sstevel@tonic-gate        next if /^=/;
4976*0Sstevel@tonic-gate        s/\#.*//;
4977*0Sstevel@tonic-gate        next if /^\s+$/;
4978*0Sstevel@tonic-gate        chomp;
4979*0Sstevel@tonic-gate        push @result, (split " ", $_, 2)[0];
4980*0Sstevel@tonic-gate    }
4981*0Sstevel@tonic-gate    close $fh;
4982*0Sstevel@tonic-gate    delete $self->{STATUS};
4983*0Sstevel@tonic-gate    $self->{CONTAINS} = \@result;
4984*0Sstevel@tonic-gate    $self->debug("CONTAINS[@result]") if $CPAN::DEBUG;
4985*0Sstevel@tonic-gate    unless (@result) {
4986*0Sstevel@tonic-gate        $CPAN::Frontend->mywarn(qq{
4987*0Sstevel@tonic-gateThe bundle file "$inst_file" may be a broken
4988*0Sstevel@tonic-gatebundlefile. It seems not to contain any bundle definition.
4989*0Sstevel@tonic-gatePlease check the file and if it is bogus, please delete it.
4990*0Sstevel@tonic-gateSorry for the inconvenience.
4991*0Sstevel@tonic-gate});
4992*0Sstevel@tonic-gate    }
4993*0Sstevel@tonic-gate    @result;
4994*0Sstevel@tonic-gate}
4995*0Sstevel@tonic-gate
4996*0Sstevel@tonic-gate#-> sub CPAN::Bundle::find_bundle_file
4997*0Sstevel@tonic-gatesub find_bundle_file {
4998*0Sstevel@tonic-gate    my($self,$where,$what) = @_;
4999*0Sstevel@tonic-gate    $self->debug("where[$where]what[$what]") if $CPAN::DEBUG;
5000*0Sstevel@tonic-gate### The following two lines let CPAN.pm become Bundle/CPAN.pm :-(
5001*0Sstevel@tonic-gate###    my $bu = File::Spec->catfile($where,$what);
5002*0Sstevel@tonic-gate###    return $bu if -f $bu;
5003*0Sstevel@tonic-gate    my $manifest = File::Spec->catfile($where,"MANIFEST");
5004*0Sstevel@tonic-gate    unless (-f $manifest) {
5005*0Sstevel@tonic-gate	require ExtUtils::Manifest;
5006*0Sstevel@tonic-gate	my $cwd = CPAN::anycwd();
5007*0Sstevel@tonic-gate	chdir $where or $CPAN::Frontend->mydie(qq{Could not chdir to "$where": $!});
5008*0Sstevel@tonic-gate	ExtUtils::Manifest::mkmanifest();
5009*0Sstevel@tonic-gate	chdir $cwd or $CPAN::Frontend->mydie(qq{Could not chdir to "$cwd": $!});
5010*0Sstevel@tonic-gate    }
5011*0Sstevel@tonic-gate    my $fh = FileHandle->new($manifest)
5012*0Sstevel@tonic-gate	or Carp::croak("Couldn't open $manifest: $!");
5013*0Sstevel@tonic-gate    local($/) = "\n";
5014*0Sstevel@tonic-gate    my $what2 = $what;
5015*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
5016*0Sstevel@tonic-gate      $what =~ s/^://;
5017*0Sstevel@tonic-gate      $what =~ tr|:|/|;
5018*0Sstevel@tonic-gate      $what2 =~ s/:Bundle://;
5019*0Sstevel@tonic-gate      $what2 =~ tr|:|/|;
5020*0Sstevel@tonic-gate    } else {
5021*0Sstevel@tonic-gate	$what2 =~ s|Bundle[/\\]||;
5022*0Sstevel@tonic-gate    }
5023*0Sstevel@tonic-gate    my $bu;
5024*0Sstevel@tonic-gate    while (<$fh>) {
5025*0Sstevel@tonic-gate	next if /^\s*\#/;
5026*0Sstevel@tonic-gate	my($file) = /(\S+)/;
5027*0Sstevel@tonic-gate	if ($file =~ m|\Q$what\E$|) {
5028*0Sstevel@tonic-gate	    $bu = $file;
5029*0Sstevel@tonic-gate	    # return File::Spec->catfile($where,$bu); # bad
5030*0Sstevel@tonic-gate	    last;
5031*0Sstevel@tonic-gate	}
5032*0Sstevel@tonic-gate	# retry if she managed to
5033*0Sstevel@tonic-gate	# have no Bundle directory
5034*0Sstevel@tonic-gate	$bu = $file if $file =~ m|\Q$what2\E$|;
5035*0Sstevel@tonic-gate    }
5036*0Sstevel@tonic-gate    $bu =~ tr|/|:| if $^O eq 'MacOS';
5037*0Sstevel@tonic-gate    return File::Spec->catfile($where, $bu) if $bu;
5038*0Sstevel@tonic-gate    Carp::croak("Couldn't find a Bundle file in $where");
5039*0Sstevel@tonic-gate}
5040*0Sstevel@tonic-gate
5041*0Sstevel@tonic-gate# needs to work quite differently from Module::inst_file because of
5042*0Sstevel@tonic-gate# cpan_home/Bundle/ directory and the possibility that we have
5043*0Sstevel@tonic-gate# shadowing effect. As it makes no sense to take the first in @INC for
5044*0Sstevel@tonic-gate# Bundles, we parse them all for $VERSION and take the newest.
5045*0Sstevel@tonic-gate
5046*0Sstevel@tonic-gate#-> sub CPAN::Bundle::inst_file ;
5047*0Sstevel@tonic-gatesub inst_file {
5048*0Sstevel@tonic-gate    my($self) = @_;
5049*0Sstevel@tonic-gate    my($inst_file);
5050*0Sstevel@tonic-gate    my(@me);
5051*0Sstevel@tonic-gate    @me = split /::/, $self->id;
5052*0Sstevel@tonic-gate    $me[-1] .= ".pm";
5053*0Sstevel@tonic-gate    my($incdir,$bestv);
5054*0Sstevel@tonic-gate    foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) {
5055*0Sstevel@tonic-gate        my $bfile = File::Spec->catfile($incdir, @me);
5056*0Sstevel@tonic-gate        CPAN->debug("bfile[$bfile]") if $CPAN::DEBUG;
5057*0Sstevel@tonic-gate        next unless -f $bfile;
5058*0Sstevel@tonic-gate        my $foundv = MM->parse_version($bfile);
5059*0Sstevel@tonic-gate        if (!$bestv || CPAN::Version->vgt($foundv,$bestv)) {
5060*0Sstevel@tonic-gate            $self->{INST_FILE} = $bfile;
5061*0Sstevel@tonic-gate            $self->{INST_VERSION} = $bestv = $foundv;
5062*0Sstevel@tonic-gate        }
5063*0Sstevel@tonic-gate    }
5064*0Sstevel@tonic-gate    $self->{INST_FILE};
5065*0Sstevel@tonic-gate}
5066*0Sstevel@tonic-gate
5067*0Sstevel@tonic-gate#-> sub CPAN::Bundle::inst_version ;
5068*0Sstevel@tonic-gatesub inst_version {
5069*0Sstevel@tonic-gate    my($self) = @_;
5070*0Sstevel@tonic-gate    $self->inst_file; # finds INST_VERSION as side effect
5071*0Sstevel@tonic-gate    $self->{INST_VERSION};
5072*0Sstevel@tonic-gate}
5073*0Sstevel@tonic-gate
5074*0Sstevel@tonic-gate#-> sub CPAN::Bundle::rematein ;
5075*0Sstevel@tonic-gatesub rematein {
5076*0Sstevel@tonic-gate    my($self,$meth) = @_;
5077*0Sstevel@tonic-gate    $self->debug("self[$self] meth[$meth]") if $CPAN::DEBUG;
5078*0Sstevel@tonic-gate    my($id) = $self->id;
5079*0Sstevel@tonic-gate    Carp::croak "Can't $meth $id, don't have an associated bundle file. :-(\n"
5080*0Sstevel@tonic-gate	unless $self->inst_file || $self->cpan_file;
5081*0Sstevel@tonic-gate    my($s,%fail);
5082*0Sstevel@tonic-gate    for $s ($self->contains) {
5083*0Sstevel@tonic-gate	my($type) = $s =~ m|/| ? 'CPAN::Distribution' :
5084*0Sstevel@tonic-gate	    $s =~ m|^Bundle::| ? 'CPAN::Bundle' : 'CPAN::Module';
5085*0Sstevel@tonic-gate	if ($type eq 'CPAN::Distribution') {
5086*0Sstevel@tonic-gate	    $CPAN::Frontend->mywarn(qq{
5087*0Sstevel@tonic-gateThe Bundle }.$self->id.qq{ contains
5088*0Sstevel@tonic-gateexplicitly a file $s.
5089*0Sstevel@tonic-gate});
5090*0Sstevel@tonic-gate	    sleep 3;
5091*0Sstevel@tonic-gate	}
5092*0Sstevel@tonic-gate	# possibly noisy action:
5093*0Sstevel@tonic-gate        $self->debug("type[$type] s[$s]") if $CPAN::DEBUG;
5094*0Sstevel@tonic-gate	my $obj = $CPAN::META->instance($type,$s);
5095*0Sstevel@tonic-gate	$obj->$meth();
5096*0Sstevel@tonic-gate        if ($obj->isa(CPAN::Bundle)
5097*0Sstevel@tonic-gate            &&
5098*0Sstevel@tonic-gate            exists $obj->{install_failed}
5099*0Sstevel@tonic-gate            &&
5100*0Sstevel@tonic-gate            ref($obj->{install_failed}) eq "HASH"
5101*0Sstevel@tonic-gate           ) {
5102*0Sstevel@tonic-gate          for (keys %{$obj->{install_failed}}) {
5103*0Sstevel@tonic-gate            $self->{install_failed}{$_} = undef; # propagate faiure up
5104*0Sstevel@tonic-gate                                                 # to me in a
5105*0Sstevel@tonic-gate                                                 # recursive call
5106*0Sstevel@tonic-gate            $fail{$s} = 1; # the bundle itself may have succeeded but
5107*0Sstevel@tonic-gate                           # not all children
5108*0Sstevel@tonic-gate          }
5109*0Sstevel@tonic-gate        } else {
5110*0Sstevel@tonic-gate          my $success;
5111*0Sstevel@tonic-gate          $success = $obj->can("uptodate") ? $obj->uptodate : 0;
5112*0Sstevel@tonic-gate          $success ||= $obj->{'install'} && $obj->{'install'} eq "YES";
5113*0Sstevel@tonic-gate          if ($success) {
5114*0Sstevel@tonic-gate            delete $self->{install_failed}{$s};
5115*0Sstevel@tonic-gate          } else {
5116*0Sstevel@tonic-gate            $fail{$s} = 1;
5117*0Sstevel@tonic-gate          }
5118*0Sstevel@tonic-gate        }
5119*0Sstevel@tonic-gate    }
5120*0Sstevel@tonic-gate
5121*0Sstevel@tonic-gate    # recap with less noise
5122*0Sstevel@tonic-gate    if ( $meth eq "install" ) {
5123*0Sstevel@tonic-gate	if (%fail) {
5124*0Sstevel@tonic-gate	    require Text::Wrap;
5125*0Sstevel@tonic-gate	    my $raw = sprintf(qq{Bundle summary:
5126*0Sstevel@tonic-gateThe following items in bundle %s had installation problems:},
5127*0Sstevel@tonic-gate			      $self->id
5128*0Sstevel@tonic-gate			     );
5129*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(Text::Wrap::fill("","",$raw));
5130*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("\n");
5131*0Sstevel@tonic-gate	    my $paragraph = "";
5132*0Sstevel@tonic-gate            my %reported;
5133*0Sstevel@tonic-gate	    for $s ($self->contains) {
5134*0Sstevel@tonic-gate              if ($fail{$s}){
5135*0Sstevel@tonic-gate		$paragraph .= "$s ";
5136*0Sstevel@tonic-gate                $self->{install_failed}{$s} = undef;
5137*0Sstevel@tonic-gate                $reported{$s} = undef;
5138*0Sstevel@tonic-gate              }
5139*0Sstevel@tonic-gate	    }
5140*0Sstevel@tonic-gate            my $report_propagated;
5141*0Sstevel@tonic-gate            for $s (sort keys %{$self->{install_failed}}) {
5142*0Sstevel@tonic-gate              next if exists $reported{$s};
5143*0Sstevel@tonic-gate              $paragraph .= "and the following items had problems
5144*0Sstevel@tonic-gateduring recursive bundle calls: " unless $report_propagated++;
5145*0Sstevel@tonic-gate              $paragraph .= "$s ";
5146*0Sstevel@tonic-gate            }
5147*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint(Text::Wrap::fill("  ","  ",$paragraph));
5148*0Sstevel@tonic-gate	    $CPAN::Frontend->myprint("\n");
5149*0Sstevel@tonic-gate	} else {
5150*0Sstevel@tonic-gate	    $self->{'install'} = 'YES';
5151*0Sstevel@tonic-gate	}
5152*0Sstevel@tonic-gate    }
5153*0Sstevel@tonic-gate}
5154*0Sstevel@tonic-gate
5155*0Sstevel@tonic-gate#sub CPAN::Bundle::xs_file
5156*0Sstevel@tonic-gatesub xs_file {
5157*0Sstevel@tonic-gate    # If a bundle contains another that contains an xs_file we have
5158*0Sstevel@tonic-gate    # here, we just don't bother I suppose
5159*0Sstevel@tonic-gate    return 0;
5160*0Sstevel@tonic-gate}
5161*0Sstevel@tonic-gate
5162*0Sstevel@tonic-gate#-> sub CPAN::Bundle::force ;
5163*0Sstevel@tonic-gatesub force   { shift->rematein('force',@_); }
5164*0Sstevel@tonic-gate#-> sub CPAN::Bundle::get ;
5165*0Sstevel@tonic-gatesub get     { shift->rematein('get',@_); }
5166*0Sstevel@tonic-gate#-> sub CPAN::Bundle::make ;
5167*0Sstevel@tonic-gatesub make    { shift->rematein('make',@_); }
5168*0Sstevel@tonic-gate#-> sub CPAN::Bundle::test ;
5169*0Sstevel@tonic-gatesub test    {
5170*0Sstevel@tonic-gate    my $self = shift;
5171*0Sstevel@tonic-gate    $self->{badtestcnt} ||= 0;
5172*0Sstevel@tonic-gate    $self->rematein('test',@_);
5173*0Sstevel@tonic-gate}
5174*0Sstevel@tonic-gate#-> sub CPAN::Bundle::install ;
5175*0Sstevel@tonic-gatesub install {
5176*0Sstevel@tonic-gate  my $self = shift;
5177*0Sstevel@tonic-gate  $self->rematein('install',@_);
5178*0Sstevel@tonic-gate}
5179*0Sstevel@tonic-gate#-> sub CPAN::Bundle::clean ;
5180*0Sstevel@tonic-gatesub clean   { shift->rematein('clean',@_); }
5181*0Sstevel@tonic-gate
5182*0Sstevel@tonic-gate#-> sub CPAN::Bundle::uptodate ;
5183*0Sstevel@tonic-gatesub uptodate {
5184*0Sstevel@tonic-gate    my($self) = @_;
5185*0Sstevel@tonic-gate    return 0 unless $self->SUPER::uptodate; # we mut have the current Bundle def
5186*0Sstevel@tonic-gate    my $c;
5187*0Sstevel@tonic-gate    foreach $c ($self->contains) {
5188*0Sstevel@tonic-gate        my $obj = CPAN::Shell->expandany($c);
5189*0Sstevel@tonic-gate        return 0 unless $obj->uptodate;
5190*0Sstevel@tonic-gate    }
5191*0Sstevel@tonic-gate    return 1;
5192*0Sstevel@tonic-gate}
5193*0Sstevel@tonic-gate
5194*0Sstevel@tonic-gate#-> sub CPAN::Bundle::readme ;
5195*0Sstevel@tonic-gatesub readme  {
5196*0Sstevel@tonic-gate    my($self) = @_;
5197*0Sstevel@tonic-gate    my($file) = $self->cpan_file or $CPAN::Frontend->myprint(qq{
5198*0Sstevel@tonic-gateNo File found for bundle } . $self->id . qq{\n}), return;
5199*0Sstevel@tonic-gate    $self->debug("self[$self] file[$file]") if $CPAN::DEBUG;
5200*0Sstevel@tonic-gate    $CPAN::META->instance('CPAN::Distribution',$file)->readme;
5201*0Sstevel@tonic-gate}
5202*0Sstevel@tonic-gate
5203*0Sstevel@tonic-gatepackage CPAN::Module;
5204*0Sstevel@tonic-gate
5205*0Sstevel@tonic-gate# Accessors
5206*0Sstevel@tonic-gate# sub CPAN::Module::userid
5207*0Sstevel@tonic-gatesub userid {
5208*0Sstevel@tonic-gate    my $self = shift;
5209*0Sstevel@tonic-gate    return unless exists $self->{RO}; # should never happen
5210*0Sstevel@tonic-gate    return $self->{RO}{userid} || $self->{RO}{CPAN_USERID};
5211*0Sstevel@tonic-gate}
5212*0Sstevel@tonic-gate# sub CPAN::Module::description
5213*0Sstevel@tonic-gatesub description { shift->{RO}{description} }
5214*0Sstevel@tonic-gate
5215*0Sstevel@tonic-gatesub undelay {
5216*0Sstevel@tonic-gate    my $self = shift;
5217*0Sstevel@tonic-gate    delete $self->{later};
5218*0Sstevel@tonic-gate    if ( my $dist = CPAN::Shell->expand("Distribution", $self->cpan_file) ) {
5219*0Sstevel@tonic-gate        $dist->undelay;
5220*0Sstevel@tonic-gate    }
5221*0Sstevel@tonic-gate}
5222*0Sstevel@tonic-gate
5223*0Sstevel@tonic-gate#-> sub CPAN::Module::color_cmd_tmps ;
5224*0Sstevel@tonic-gatesub color_cmd_tmps {
5225*0Sstevel@tonic-gate    my($self) = shift;
5226*0Sstevel@tonic-gate    my($depth) = shift || 0;
5227*0Sstevel@tonic-gate    my($color) = shift || 0;
5228*0Sstevel@tonic-gate    my($ancestors) = shift || [];
5229*0Sstevel@tonic-gate    # a module needs to recurse to its cpan_file
5230*0Sstevel@tonic-gate
5231*0Sstevel@tonic-gate    return if exists $self->{incommandcolor}
5232*0Sstevel@tonic-gate        && $self->{incommandcolor}==$color;
5233*0Sstevel@tonic-gate    if ($depth>=100){
5234*0Sstevel@tonic-gate        $CPAN::Frontend->mydie(CPAN::Exception::RecursiveDependency->new($ancestors));
5235*0Sstevel@tonic-gate    }
5236*0Sstevel@tonic-gate    # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1;
5237*0Sstevel@tonic-gate
5238*0Sstevel@tonic-gate    if ( my $dist = CPAN::Shell->expand("Distribution", $self->cpan_file) ) {
5239*0Sstevel@tonic-gate        $dist->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]);
5240*0Sstevel@tonic-gate    }
5241*0Sstevel@tonic-gate    if ($color==0) {
5242*0Sstevel@tonic-gate        delete $self->{badtestcnt};
5243*0Sstevel@tonic-gate    }
5244*0Sstevel@tonic-gate    $self->{incommandcolor} = $color;
5245*0Sstevel@tonic-gate}
5246*0Sstevel@tonic-gate
5247*0Sstevel@tonic-gate#-> sub CPAN::Module::as_glimpse ;
5248*0Sstevel@tonic-gatesub as_glimpse {
5249*0Sstevel@tonic-gate    my($self) = @_;
5250*0Sstevel@tonic-gate    my(@m);
5251*0Sstevel@tonic-gate    my $class = ref($self);
5252*0Sstevel@tonic-gate    $class =~ s/^CPAN:://;
5253*0Sstevel@tonic-gate    my $color_on = "";
5254*0Sstevel@tonic-gate    my $color_off = "";
5255*0Sstevel@tonic-gate    if (
5256*0Sstevel@tonic-gate        $CPAN::Shell::COLOR_REGISTERED
5257*0Sstevel@tonic-gate        &&
5258*0Sstevel@tonic-gate        $CPAN::META->has_inst("Term::ANSIColor")
5259*0Sstevel@tonic-gate        &&
5260*0Sstevel@tonic-gate        $self->{RO}{description}
5261*0Sstevel@tonic-gate       ) {
5262*0Sstevel@tonic-gate        $color_on = Term::ANSIColor::color("green");
5263*0Sstevel@tonic-gate        $color_off = Term::ANSIColor::color("reset");
5264*0Sstevel@tonic-gate    }
5265*0Sstevel@tonic-gate    push @m, sprintf("%-15s %s%-15s%s (%s)\n",
5266*0Sstevel@tonic-gate                     $class,
5267*0Sstevel@tonic-gate                     $color_on,
5268*0Sstevel@tonic-gate                     $self->id,
5269*0Sstevel@tonic-gate                     $color_off,
5270*0Sstevel@tonic-gate		     $self->cpan_file);
5271*0Sstevel@tonic-gate    join "", @m;
5272*0Sstevel@tonic-gate}
5273*0Sstevel@tonic-gate
5274*0Sstevel@tonic-gate#-> sub CPAN::Module::as_string ;
5275*0Sstevel@tonic-gatesub as_string {
5276*0Sstevel@tonic-gate    my($self) = @_;
5277*0Sstevel@tonic-gate    my(@m);
5278*0Sstevel@tonic-gate    CPAN->debug("$self entering as_string") if $CPAN::DEBUG;
5279*0Sstevel@tonic-gate    my $class = ref($self);
5280*0Sstevel@tonic-gate    $class =~ s/^CPAN:://;
5281*0Sstevel@tonic-gate    local($^W) = 0;
5282*0Sstevel@tonic-gate    push @m, $class, " id = $self->{ID}\n";
5283*0Sstevel@tonic-gate    my $sprintf = "    %-12s %s\n";
5284*0Sstevel@tonic-gate    push @m, sprintf($sprintf, 'DESCRIPTION', $self->description)
5285*0Sstevel@tonic-gate	if $self->description;
5286*0Sstevel@tonic-gate    my $sprintf2 = "    %-12s %s (%s)\n";
5287*0Sstevel@tonic-gate    my($userid);
5288*0Sstevel@tonic-gate    $userid = $self->userid;
5289*0Sstevel@tonic-gate    if ( $userid ){
5290*0Sstevel@tonic-gate	my $author;
5291*0Sstevel@tonic-gate	if ($author = CPAN::Shell->expand('Author',$userid)) {
5292*0Sstevel@tonic-gate	  my $email = "";
5293*0Sstevel@tonic-gate	  my $m; # old perls
5294*0Sstevel@tonic-gate	  if ($m = $author->email) {
5295*0Sstevel@tonic-gate            $email = " <$m>";
5296*0Sstevel@tonic-gate          }
5297*0Sstevel@tonic-gate	  push @m, sprintf(
5298*0Sstevel@tonic-gate			   $sprintf2,
5299*0Sstevel@tonic-gate			   'CPAN_USERID',
5300*0Sstevel@tonic-gate			   $userid,
5301*0Sstevel@tonic-gate			   $author->fullname . $email
5302*0Sstevel@tonic-gate			  );
5303*0Sstevel@tonic-gate	}
5304*0Sstevel@tonic-gate    }
5305*0Sstevel@tonic-gate    push @m, sprintf($sprintf, 'CPAN_VERSION', $self->cpan_version)
5306*0Sstevel@tonic-gate	if $self->cpan_version;
5307*0Sstevel@tonic-gate    push @m, sprintf($sprintf, 'CPAN_FILE', $self->cpan_file)
5308*0Sstevel@tonic-gate	if $self->cpan_file;
5309*0Sstevel@tonic-gate    my $sprintf3 = "    %-12s %1s%1s%1s%1s (%s,%s,%s,%s)\n";
5310*0Sstevel@tonic-gate    my(%statd,%stats,%statl,%stati);
5311*0Sstevel@tonic-gate    @statd{qw,? i c a b R M S,} = qw,unknown idea
5312*0Sstevel@tonic-gate	pre-alpha alpha beta released mature standard,;
5313*0Sstevel@tonic-gate    @stats{qw,? m d u n a,}       = qw,unknown mailing-list
5314*0Sstevel@tonic-gate	developer comp.lang.perl.* none abandoned,;
5315*0Sstevel@tonic-gate    @statl{qw,? p c + o h,}       = qw,unknown perl C C++ other hybrid,;
5316*0Sstevel@tonic-gate    @stati{qw,? f r O h,}         = qw,unknown functions
5317*0Sstevel@tonic-gate	references+ties object-oriented hybrid,;
5318*0Sstevel@tonic-gate    $statd{' '} = 'unknown';
5319*0Sstevel@tonic-gate    $stats{' '} = 'unknown';
5320*0Sstevel@tonic-gate    $statl{' '} = 'unknown';
5321*0Sstevel@tonic-gate    $stati{' '} = 'unknown';
5322*0Sstevel@tonic-gate    push @m, sprintf(
5323*0Sstevel@tonic-gate		     $sprintf3,
5324*0Sstevel@tonic-gate		     'DSLI_STATUS',
5325*0Sstevel@tonic-gate		     $self->{RO}{statd},
5326*0Sstevel@tonic-gate		     $self->{RO}{stats},
5327*0Sstevel@tonic-gate		     $self->{RO}{statl},
5328*0Sstevel@tonic-gate		     $self->{RO}{stati},
5329*0Sstevel@tonic-gate		     $statd{$self->{RO}{statd}},
5330*0Sstevel@tonic-gate		     $stats{$self->{RO}{stats}},
5331*0Sstevel@tonic-gate		     $statl{$self->{RO}{statl}},
5332*0Sstevel@tonic-gate		     $stati{$self->{RO}{stati}}
5333*0Sstevel@tonic-gate		    ) if $self->{RO}{statd};
5334*0Sstevel@tonic-gate    my $local_file = $self->inst_file;
5335*0Sstevel@tonic-gate    unless ($self->{MANPAGE}) {
5336*0Sstevel@tonic-gate        if ($local_file) {
5337*0Sstevel@tonic-gate            $self->{MANPAGE} = $self->manpage_headline($local_file);
5338*0Sstevel@tonic-gate        } else {
5339*0Sstevel@tonic-gate            # If we have already untarred it, we should look there
5340*0Sstevel@tonic-gate            my $dist = $CPAN::META->instance('CPAN::Distribution',
5341*0Sstevel@tonic-gate                                             $self->cpan_file);
5342*0Sstevel@tonic-gate            # warn "dist[$dist]";
5343*0Sstevel@tonic-gate            # mff=manifest file; mfh=manifest handle
5344*0Sstevel@tonic-gate            my($mff,$mfh);
5345*0Sstevel@tonic-gate            if (
5346*0Sstevel@tonic-gate                $dist->{build_dir}
5347*0Sstevel@tonic-gate                and
5348*0Sstevel@tonic-gate                (-f  ($mff = File::Spec->catfile($dist->{build_dir}, "MANIFEST")))
5349*0Sstevel@tonic-gate                and
5350*0Sstevel@tonic-gate                $mfh = FileHandle->new($mff)
5351*0Sstevel@tonic-gate               ) {
5352*0Sstevel@tonic-gate                CPAN->debug("mff[$mff]") if $CPAN::DEBUG;
5353*0Sstevel@tonic-gate                my $lfre = $self->id; # local file RE
5354*0Sstevel@tonic-gate                $lfre =~ s/::/./g;
5355*0Sstevel@tonic-gate                $lfre .= "\\.pm\$";
5356*0Sstevel@tonic-gate                my($lfl); # local file file
5357*0Sstevel@tonic-gate                local $/ = "\n";
5358*0Sstevel@tonic-gate                my(@mflines) = <$mfh>;
5359*0Sstevel@tonic-gate                for (@mflines) {
5360*0Sstevel@tonic-gate                    s/^\s+//;
5361*0Sstevel@tonic-gate                    s/\s.*//s;
5362*0Sstevel@tonic-gate                }
5363*0Sstevel@tonic-gate                while (length($lfre)>5 and !$lfl) {
5364*0Sstevel@tonic-gate                    ($lfl) = grep /$lfre/, @mflines;
5365*0Sstevel@tonic-gate                    CPAN->debug("lfl[$lfl]lfre[$lfre]") if $CPAN::DEBUG;
5366*0Sstevel@tonic-gate                    $lfre =~ s/.+?\.//;
5367*0Sstevel@tonic-gate                }
5368*0Sstevel@tonic-gate                $lfl =~ s/\s.*//; # remove comments
5369*0Sstevel@tonic-gate                $lfl =~ s/\s+//g; # chomp would maybe be too system-specific
5370*0Sstevel@tonic-gate                my $lfl_abs = File::Spec->catfile($dist->{build_dir},$lfl);
5371*0Sstevel@tonic-gate                # warn "lfl_abs[$lfl_abs]";
5372*0Sstevel@tonic-gate                if (-f $lfl_abs) {
5373*0Sstevel@tonic-gate                    $self->{MANPAGE} = $self->manpage_headline($lfl_abs);
5374*0Sstevel@tonic-gate                }
5375*0Sstevel@tonic-gate            }
5376*0Sstevel@tonic-gate        }
5377*0Sstevel@tonic-gate    }
5378*0Sstevel@tonic-gate    my($item);
5379*0Sstevel@tonic-gate    for $item (qw/MANPAGE/) {
5380*0Sstevel@tonic-gate	push @m, sprintf($sprintf, $item, $self->{$item})
5381*0Sstevel@tonic-gate	    if exists $self->{$item};
5382*0Sstevel@tonic-gate    }
5383*0Sstevel@tonic-gate    for $item (qw/CONTAINS/) {
5384*0Sstevel@tonic-gate	push @m, sprintf($sprintf, $item, join(" ",@{$self->{$item}}))
5385*0Sstevel@tonic-gate	    if exists $self->{$item} && @{$self->{$item}};
5386*0Sstevel@tonic-gate    }
5387*0Sstevel@tonic-gate    push @m, sprintf($sprintf, 'INST_FILE',
5388*0Sstevel@tonic-gate		     $local_file || "(not installed)");
5389*0Sstevel@tonic-gate    push @m, sprintf($sprintf, 'INST_VERSION',
5390*0Sstevel@tonic-gate		     $self->inst_version) if $local_file;
5391*0Sstevel@tonic-gate    join "", @m, "\n";
5392*0Sstevel@tonic-gate}
5393*0Sstevel@tonic-gate
5394*0Sstevel@tonic-gatesub manpage_headline {
5395*0Sstevel@tonic-gate  my($self,$local_file) = @_;
5396*0Sstevel@tonic-gate  my(@local_file) = $local_file;
5397*0Sstevel@tonic-gate  $local_file =~ s/\.pm(?!\n)\Z/.pod/;
5398*0Sstevel@tonic-gate  push @local_file, $local_file;
5399*0Sstevel@tonic-gate  my(@result,$locf);
5400*0Sstevel@tonic-gate  for $locf (@local_file) {
5401*0Sstevel@tonic-gate    next unless -f $locf;
5402*0Sstevel@tonic-gate    my $fh = FileHandle->new($locf)
5403*0Sstevel@tonic-gate	or $Carp::Frontend->mydie("Couldn't open $locf: $!");
5404*0Sstevel@tonic-gate    my $inpod = 0;
5405*0Sstevel@tonic-gate    local $/ = "\n";
5406*0Sstevel@tonic-gate    while (<$fh>) {
5407*0Sstevel@tonic-gate      $inpod = m/^=(?!head1\s+NAME\s*$)/ ? 0 :
5408*0Sstevel@tonic-gate	  m/^=head1\s+NAME\s*$/ ? 1 : $inpod;
5409*0Sstevel@tonic-gate      next unless $inpod;
5410*0Sstevel@tonic-gate      next if /^=/;
5411*0Sstevel@tonic-gate      next if /^\s+$/;
5412*0Sstevel@tonic-gate      chomp;
5413*0Sstevel@tonic-gate      push @result, $_;
5414*0Sstevel@tonic-gate    }
5415*0Sstevel@tonic-gate    close $fh;
5416*0Sstevel@tonic-gate    last if @result;
5417*0Sstevel@tonic-gate  }
5418*0Sstevel@tonic-gate  join " ", @result;
5419*0Sstevel@tonic-gate}
5420*0Sstevel@tonic-gate
5421*0Sstevel@tonic-gate#-> sub CPAN::Module::cpan_file ;
5422*0Sstevel@tonic-gate# Note: also inherited by CPAN::Bundle
5423*0Sstevel@tonic-gatesub cpan_file {
5424*0Sstevel@tonic-gate    my $self = shift;
5425*0Sstevel@tonic-gate    CPAN->debug(sprintf "id[%s]", $self->id) if $CPAN::DEBUG;
5426*0Sstevel@tonic-gate    unless (defined $self->{RO}{CPAN_FILE}) {
5427*0Sstevel@tonic-gate	CPAN::Index->reload;
5428*0Sstevel@tonic-gate    }
5429*0Sstevel@tonic-gate    if (exists $self->{RO}{CPAN_FILE} && defined $self->{RO}{CPAN_FILE}){
5430*0Sstevel@tonic-gate	return $self->{RO}{CPAN_FILE};
5431*0Sstevel@tonic-gate    } else {
5432*0Sstevel@tonic-gate        my $userid = $self->userid;
5433*0Sstevel@tonic-gate        if ( $userid ) {
5434*0Sstevel@tonic-gate            if ($CPAN::META->exists("CPAN::Author",$userid)) {
5435*0Sstevel@tonic-gate                my $author = $CPAN::META->instance("CPAN::Author",
5436*0Sstevel@tonic-gate                                                   $userid);
5437*0Sstevel@tonic-gate                my $fullname = $author->fullname;
5438*0Sstevel@tonic-gate                my $email = $author->email;
5439*0Sstevel@tonic-gate                unless (defined $fullname && defined $email) {
5440*0Sstevel@tonic-gate                    return sprintf("Contact Author %s",
5441*0Sstevel@tonic-gate                                   $userid,
5442*0Sstevel@tonic-gate                                  );
5443*0Sstevel@tonic-gate                }
5444*0Sstevel@tonic-gate                return "Contact Author $fullname <$email>";
5445*0Sstevel@tonic-gate            } else {
5446*0Sstevel@tonic-gate                return "Contact Author $userid (Email address not available)";
5447*0Sstevel@tonic-gate            }
5448*0Sstevel@tonic-gate        } else {
5449*0Sstevel@tonic-gate            return "N/A";
5450*0Sstevel@tonic-gate        }
5451*0Sstevel@tonic-gate    }
5452*0Sstevel@tonic-gate}
5453*0Sstevel@tonic-gate
5454*0Sstevel@tonic-gate#-> sub CPAN::Module::cpan_version ;
5455*0Sstevel@tonic-gatesub cpan_version {
5456*0Sstevel@tonic-gate    my $self = shift;
5457*0Sstevel@tonic-gate
5458*0Sstevel@tonic-gate    $self->{RO}{CPAN_VERSION} = 'undef'
5459*0Sstevel@tonic-gate	unless defined $self->{RO}{CPAN_VERSION};
5460*0Sstevel@tonic-gate    # I believe this is always a bug in the index and should be reported
5461*0Sstevel@tonic-gate    # as such, but usually I find out such an error and do not want to
5462*0Sstevel@tonic-gate    # provoke too many bugreports
5463*0Sstevel@tonic-gate
5464*0Sstevel@tonic-gate    $self->{RO}{CPAN_VERSION};
5465*0Sstevel@tonic-gate}
5466*0Sstevel@tonic-gate
5467*0Sstevel@tonic-gate#-> sub CPAN::Module::force ;
5468*0Sstevel@tonic-gatesub force {
5469*0Sstevel@tonic-gate    my($self) = @_;
5470*0Sstevel@tonic-gate    $self->{'force_update'}++;
5471*0Sstevel@tonic-gate}
5472*0Sstevel@tonic-gate
5473*0Sstevel@tonic-gate#-> sub CPAN::Module::rematein ;
5474*0Sstevel@tonic-gatesub rematein {
5475*0Sstevel@tonic-gate    my($self,$meth) = @_;
5476*0Sstevel@tonic-gate    $CPAN::Frontend->myprint(sprintf("Running %s for module %s\n",
5477*0Sstevel@tonic-gate                                     $meth,
5478*0Sstevel@tonic-gate                                     $self->id));
5479*0Sstevel@tonic-gate    my $cpan_file = $self->cpan_file;
5480*0Sstevel@tonic-gate    if ($cpan_file eq "N/A" || $cpan_file =~ /^Contact Author/){
5481*0Sstevel@tonic-gate      $CPAN::Frontend->mywarn(sprintf qq{
5482*0Sstevel@tonic-gate  The module %s isn\'t available on CPAN.
5483*0Sstevel@tonic-gate
5484*0Sstevel@tonic-gate  Either the module has not yet been uploaded to CPAN, or it is
5485*0Sstevel@tonic-gate  temporary unavailable. Please contact the author to find out
5486*0Sstevel@tonic-gate  more about the status. Try 'i %s'.
5487*0Sstevel@tonic-gate},
5488*0Sstevel@tonic-gate			      $self->id,
5489*0Sstevel@tonic-gate			      $self->id,
5490*0Sstevel@tonic-gate			     );
5491*0Sstevel@tonic-gate      return;
5492*0Sstevel@tonic-gate    }
5493*0Sstevel@tonic-gate    my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file);
5494*0Sstevel@tonic-gate    $pack->called_for($self->id);
5495*0Sstevel@tonic-gate    $pack->force($meth) if exists $self->{'force_update'};
5496*0Sstevel@tonic-gate    $pack->$meth();
5497*0Sstevel@tonic-gate    $pack->unforce if $pack->can("unforce") && exists $self->{'force_update'};
5498*0Sstevel@tonic-gate    delete $self->{'force_update'};
5499*0Sstevel@tonic-gate}
5500*0Sstevel@tonic-gate
5501*0Sstevel@tonic-gate#-> sub CPAN::Module::readme ;
5502*0Sstevel@tonic-gatesub readme { shift->rematein('readme') }
5503*0Sstevel@tonic-gate#-> sub CPAN::Module::look ;
5504*0Sstevel@tonic-gatesub look { shift->rematein('look') }
5505*0Sstevel@tonic-gate#-> sub CPAN::Module::cvs_import ;
5506*0Sstevel@tonic-gatesub cvs_import { shift->rematein('cvs_import') }
5507*0Sstevel@tonic-gate#-> sub CPAN::Module::get ;
5508*0Sstevel@tonic-gatesub get    { shift->rematein('get',@_); }
5509*0Sstevel@tonic-gate#-> sub CPAN::Module::make ;
5510*0Sstevel@tonic-gatesub make   {
5511*0Sstevel@tonic-gate    my $self = shift;
5512*0Sstevel@tonic-gate    $self->rematein('make');
5513*0Sstevel@tonic-gate}
5514*0Sstevel@tonic-gate#-> sub CPAN::Module::test ;
5515*0Sstevel@tonic-gatesub test   {
5516*0Sstevel@tonic-gate    my $self = shift;
5517*0Sstevel@tonic-gate    $self->{badtestcnt} ||= 0;
5518*0Sstevel@tonic-gate    $self->rematein('test',@_);
5519*0Sstevel@tonic-gate}
5520*0Sstevel@tonic-gate#-> sub CPAN::Module::uptodate ;
5521*0Sstevel@tonic-gatesub uptodate {
5522*0Sstevel@tonic-gate    my($self) = @_;
5523*0Sstevel@tonic-gate    my($latest) = $self->cpan_version;
5524*0Sstevel@tonic-gate    $latest ||= 0;
5525*0Sstevel@tonic-gate    my($inst_file) = $self->inst_file;
5526*0Sstevel@tonic-gate    my($have) = 0;
5527*0Sstevel@tonic-gate    if (defined $inst_file) {
5528*0Sstevel@tonic-gate	$have = $self->inst_version;
5529*0Sstevel@tonic-gate    }
5530*0Sstevel@tonic-gate    local($^W)=0;
5531*0Sstevel@tonic-gate    if ($inst_file
5532*0Sstevel@tonic-gate	&&
5533*0Sstevel@tonic-gate	! CPAN::Version->vgt($latest, $have)
5534*0Sstevel@tonic-gate       ) {
5535*0Sstevel@tonic-gate        CPAN->debug("returning uptodate. inst_file[$inst_file] ".
5536*0Sstevel@tonic-gate                    "latest[$latest] have[$have]") if $CPAN::DEBUG;
5537*0Sstevel@tonic-gate        return 1;
5538*0Sstevel@tonic-gate    }
5539*0Sstevel@tonic-gate    return;
5540*0Sstevel@tonic-gate}
5541*0Sstevel@tonic-gate#-> sub CPAN::Module::install ;
5542*0Sstevel@tonic-gatesub install {
5543*0Sstevel@tonic-gate    my($self) = @_;
5544*0Sstevel@tonic-gate    my($doit) = 0;
5545*0Sstevel@tonic-gate    if ($self->uptodate
5546*0Sstevel@tonic-gate	&&
5547*0Sstevel@tonic-gate	not exists $self->{'force_update'}
5548*0Sstevel@tonic-gate       ) {
5549*0Sstevel@tonic-gate	$CPAN::Frontend->myprint( $self->id. " is up to date.\n");
5550*0Sstevel@tonic-gate    } else {
5551*0Sstevel@tonic-gate	$doit = 1;
5552*0Sstevel@tonic-gate    }
5553*0Sstevel@tonic-gate    if ($self->{RO}{stats} && $self->{RO}{stats} eq "a") {
5554*0Sstevel@tonic-gate        $CPAN::Frontend->mywarn(qq{
5555*0Sstevel@tonic-gate\n\n\n     ***WARNING***
5556*0Sstevel@tonic-gate     The module $self->{ID} has no active maintainer.\n\n\n
5557*0Sstevel@tonic-gate});
5558*0Sstevel@tonic-gate        sleep 5;
5559*0Sstevel@tonic-gate    }
5560*0Sstevel@tonic-gate    $self->rematein('install') if $doit;
5561*0Sstevel@tonic-gate}
5562*0Sstevel@tonic-gate#-> sub CPAN::Module::clean ;
5563*0Sstevel@tonic-gatesub clean  { shift->rematein('clean') }
5564*0Sstevel@tonic-gate
5565*0Sstevel@tonic-gate#-> sub CPAN::Module::inst_file ;
5566*0Sstevel@tonic-gatesub inst_file {
5567*0Sstevel@tonic-gate    my($self) = @_;
5568*0Sstevel@tonic-gate    my($dir,@packpath);
5569*0Sstevel@tonic-gate    @packpath = split /::/, $self->{ID};
5570*0Sstevel@tonic-gate    $packpath[-1] .= ".pm";
5571*0Sstevel@tonic-gate    foreach $dir (@INC) {
5572*0Sstevel@tonic-gate	my $pmfile = File::Spec->catfile($dir,@packpath);
5573*0Sstevel@tonic-gate	if (-f $pmfile){
5574*0Sstevel@tonic-gate	    return $pmfile;
5575*0Sstevel@tonic-gate	}
5576*0Sstevel@tonic-gate    }
5577*0Sstevel@tonic-gate    return;
5578*0Sstevel@tonic-gate}
5579*0Sstevel@tonic-gate
5580*0Sstevel@tonic-gate#-> sub CPAN::Module::xs_file ;
5581*0Sstevel@tonic-gatesub xs_file {
5582*0Sstevel@tonic-gate    my($self) = @_;
5583*0Sstevel@tonic-gate    my($dir,@packpath);
5584*0Sstevel@tonic-gate    @packpath = split /::/, $self->{ID};
5585*0Sstevel@tonic-gate    push @packpath, $packpath[-1];
5586*0Sstevel@tonic-gate    $packpath[-1] .= "." . $Config::Config{'dlext'};
5587*0Sstevel@tonic-gate    foreach $dir (@INC) {
5588*0Sstevel@tonic-gate	my $xsfile = File::Spec->catfile($dir,'auto',@packpath);
5589*0Sstevel@tonic-gate	if (-f $xsfile){
5590*0Sstevel@tonic-gate	    return $xsfile;
5591*0Sstevel@tonic-gate	}
5592*0Sstevel@tonic-gate    }
5593*0Sstevel@tonic-gate    return;
5594*0Sstevel@tonic-gate}
5595*0Sstevel@tonic-gate
5596*0Sstevel@tonic-gate#-> sub CPAN::Module::inst_version ;
5597*0Sstevel@tonic-gatesub inst_version {
5598*0Sstevel@tonic-gate    my($self) = @_;
5599*0Sstevel@tonic-gate    my $parsefile = $self->inst_file or return;
5600*0Sstevel@tonic-gate    local($^W) = 0 if $] < 5.00303 && $ExtUtils::MakeMaker::VERSION < 5.38;
5601*0Sstevel@tonic-gate    my $have;
5602*0Sstevel@tonic-gate
5603*0Sstevel@tonic-gate    # there was a bug in 5.6.0 that let lots of unini warnings out of
5604*0Sstevel@tonic-gate    # parse_version. Fixed shortly after 5.6.0 by PMQS. We can remove
5605*0Sstevel@tonic-gate    # the following workaround after 5.6.1 is out.
5606*0Sstevel@tonic-gate    local($SIG{__WARN__}) =  sub { my $w = shift;
5607*0Sstevel@tonic-gate                                   return if $w =~ /uninitialized/i;
5608*0Sstevel@tonic-gate                                   warn $w;
5609*0Sstevel@tonic-gate                                 };
5610*0Sstevel@tonic-gate
5611*0Sstevel@tonic-gate    $have = MM->parse_version($parsefile) || "undef";
5612*0Sstevel@tonic-gate    $have =~ s/^ //; # since the %vd hack these two lines here are needed
5613*0Sstevel@tonic-gate    $have =~ s/ $//; # trailing whitespace happens all the time
5614*0Sstevel@tonic-gate
5615*0Sstevel@tonic-gate    # My thoughts about why %vd processing should happen here
5616*0Sstevel@tonic-gate
5617*0Sstevel@tonic-gate    # Alt1 maintain it as string with leading v:
5618*0Sstevel@tonic-gate    # read index files     do nothing
5619*0Sstevel@tonic-gate    # compare it           use utility for compare
5620*0Sstevel@tonic-gate    # print it             do nothing
5621*0Sstevel@tonic-gate
5622*0Sstevel@tonic-gate    # Alt2 maintain it as what it is
5623*0Sstevel@tonic-gate    # read index files     convert
5624*0Sstevel@tonic-gate    # compare it           use utility because there's still a ">" vs "gt" issue
5625*0Sstevel@tonic-gate    # print it             use CPAN::Version for print
5626*0Sstevel@tonic-gate
5627*0Sstevel@tonic-gate    # Seems cleaner to hold it in memory as a string starting with a "v"
5628*0Sstevel@tonic-gate
5629*0Sstevel@tonic-gate    # If the author of this module made a mistake and wrote a quoted
5630*0Sstevel@tonic-gate    # "v1.13" instead of v1.13, we simply leave it at that with the
5631*0Sstevel@tonic-gate    # effect that *we* will treat it like a v-tring while the rest of
5632*0Sstevel@tonic-gate    # perl won't. Seems sensible when we consider that any action we
5633*0Sstevel@tonic-gate    # could take now would just add complexity.
5634*0Sstevel@tonic-gate
5635*0Sstevel@tonic-gate    $have = CPAN::Version->readable($have);
5636*0Sstevel@tonic-gate
5637*0Sstevel@tonic-gate    $have =~ s/\s*//g; # stringify to float around floating point issues
5638*0Sstevel@tonic-gate    $have; # no stringify needed, \s* above matches always
5639*0Sstevel@tonic-gate}
5640*0Sstevel@tonic-gate
5641*0Sstevel@tonic-gatepackage CPAN::Tarzip;
5642*0Sstevel@tonic-gate
5643*0Sstevel@tonic-gate# CPAN::Tarzip::gzip
5644*0Sstevel@tonic-gatesub gzip {
5645*0Sstevel@tonic-gate  my($class,$read,$write) = @_;
5646*0Sstevel@tonic-gate  if ($CPAN::META->has_inst("Compress::Zlib")) {
5647*0Sstevel@tonic-gate    my($buffer,$fhw);
5648*0Sstevel@tonic-gate    $fhw = FileHandle->new($read)
5649*0Sstevel@tonic-gate	or $CPAN::Frontend->mydie("Could not open $read: $!");
5650*0Sstevel@tonic-gate    my $gz = Compress::Zlib::gzopen($write, "wb")
5651*0Sstevel@tonic-gate	or $CPAN::Frontend->mydie("Cannot gzopen $write: $!\n");
5652*0Sstevel@tonic-gate    $gz->gzwrite($buffer)
5653*0Sstevel@tonic-gate	while read($fhw,$buffer,4096) > 0 ;
5654*0Sstevel@tonic-gate    $gz->gzclose() ;
5655*0Sstevel@tonic-gate    $fhw->close;
5656*0Sstevel@tonic-gate    return 1;
5657*0Sstevel@tonic-gate  } else {
5658*0Sstevel@tonic-gate    system("$CPAN::Config->{gzip} -c $read > $write")==0;
5659*0Sstevel@tonic-gate  }
5660*0Sstevel@tonic-gate}
5661*0Sstevel@tonic-gate
5662*0Sstevel@tonic-gate
5663*0Sstevel@tonic-gate# CPAN::Tarzip::gunzip
5664*0Sstevel@tonic-gatesub gunzip {
5665*0Sstevel@tonic-gate  my($class,$read,$write) = @_;
5666*0Sstevel@tonic-gate  if ($CPAN::META->has_inst("Compress::Zlib")) {
5667*0Sstevel@tonic-gate    my($buffer,$fhw);
5668*0Sstevel@tonic-gate    $fhw = FileHandle->new(">$write")
5669*0Sstevel@tonic-gate	or $CPAN::Frontend->mydie("Could not open >$write: $!");
5670*0Sstevel@tonic-gate    my $gz = Compress::Zlib::gzopen($read, "rb")
5671*0Sstevel@tonic-gate	or $CPAN::Frontend->mydie("Cannot gzopen $read: $!\n");
5672*0Sstevel@tonic-gate    $fhw->print($buffer)
5673*0Sstevel@tonic-gate	while $gz->gzread($buffer) > 0 ;
5674*0Sstevel@tonic-gate    $CPAN::Frontend->mydie("Error reading from $read: $!\n")
5675*0Sstevel@tonic-gate	if $gz->gzerror != Compress::Zlib::Z_STREAM_END();
5676*0Sstevel@tonic-gate    $gz->gzclose() ;
5677*0Sstevel@tonic-gate    $fhw->close;
5678*0Sstevel@tonic-gate    return 1;
5679*0Sstevel@tonic-gate  } else {
5680*0Sstevel@tonic-gate    system("$CPAN::Config->{gzip} -dc $read > $write")==0;
5681*0Sstevel@tonic-gate  }
5682*0Sstevel@tonic-gate}
5683*0Sstevel@tonic-gate
5684*0Sstevel@tonic-gate
5685*0Sstevel@tonic-gate# CPAN::Tarzip::gtest
5686*0Sstevel@tonic-gatesub gtest {
5687*0Sstevel@tonic-gate  my($class,$read) = @_;
5688*0Sstevel@tonic-gate  # After I had reread the documentation in zlib.h, I discovered that
5689*0Sstevel@tonic-gate  # uncompressed files do not lead to an gzerror (anymore?).
5690*0Sstevel@tonic-gate  if ( $CPAN::META->has_inst("Compress::Zlib") ) {
5691*0Sstevel@tonic-gate    my($buffer,$len);
5692*0Sstevel@tonic-gate    $len = 0;
5693*0Sstevel@tonic-gate    my $gz = Compress::Zlib::gzopen($read, "rb")
5694*0Sstevel@tonic-gate	or $CPAN::Frontend->mydie(sprintf("Cannot gzopen %s: %s\n",
5695*0Sstevel@tonic-gate                                          $read,
5696*0Sstevel@tonic-gate                                          $Compress::Zlib::gzerrno));
5697*0Sstevel@tonic-gate    while ($gz->gzread($buffer) > 0 ){
5698*0Sstevel@tonic-gate        $len += length($buffer);
5699*0Sstevel@tonic-gate        $buffer = "";
5700*0Sstevel@tonic-gate    }
5701*0Sstevel@tonic-gate    my $err = $gz->gzerror;
5702*0Sstevel@tonic-gate    my $success = ! $err || $err == Compress::Zlib::Z_STREAM_END();
5703*0Sstevel@tonic-gate    if ($len == -s $read){
5704*0Sstevel@tonic-gate        $success = 0;
5705*0Sstevel@tonic-gate        CPAN->debug("hit an uncompressed file") if $CPAN::DEBUG;
5706*0Sstevel@tonic-gate    }
5707*0Sstevel@tonic-gate    $gz->gzclose();
5708*0Sstevel@tonic-gate    CPAN->debug("err[$err]success[$success]") if $CPAN::DEBUG;
5709*0Sstevel@tonic-gate    return $success;
5710*0Sstevel@tonic-gate  } else {
5711*0Sstevel@tonic-gate      return system("$CPAN::Config->{gzip} -dt $read")==0;
5712*0Sstevel@tonic-gate  }
5713*0Sstevel@tonic-gate}
5714*0Sstevel@tonic-gate
5715*0Sstevel@tonic-gate
5716*0Sstevel@tonic-gate# CPAN::Tarzip::TIEHANDLE
5717*0Sstevel@tonic-gatesub TIEHANDLE {
5718*0Sstevel@tonic-gate  my($class,$file) = @_;
5719*0Sstevel@tonic-gate  my $ret;
5720*0Sstevel@tonic-gate  $class->debug("file[$file]");
5721*0Sstevel@tonic-gate  if ($CPAN::META->has_inst("Compress::Zlib")) {
5722*0Sstevel@tonic-gate    my $gz = Compress::Zlib::gzopen($file,"rb") or
5723*0Sstevel@tonic-gate	die "Could not gzopen $file";
5724*0Sstevel@tonic-gate    $ret = bless {GZ => $gz}, $class;
5725*0Sstevel@tonic-gate  } else {
5726*0Sstevel@tonic-gate    my $pipe = "$CPAN::Config->{gzip} --decompress --stdout $file |";
5727*0Sstevel@tonic-gate    my $fh = FileHandle->new($pipe) or die "Could not pipe[$pipe]: $!";
5728*0Sstevel@tonic-gate    binmode $fh;
5729*0Sstevel@tonic-gate    $ret = bless {FH => $fh}, $class;
5730*0Sstevel@tonic-gate  }
5731*0Sstevel@tonic-gate  $ret;
5732*0Sstevel@tonic-gate}
5733*0Sstevel@tonic-gate
5734*0Sstevel@tonic-gate
5735*0Sstevel@tonic-gate# CPAN::Tarzip::READLINE
5736*0Sstevel@tonic-gatesub READLINE {
5737*0Sstevel@tonic-gate  my($self) = @_;
5738*0Sstevel@tonic-gate  if (exists $self->{GZ}) {
5739*0Sstevel@tonic-gate    my $gz = $self->{GZ};
5740*0Sstevel@tonic-gate    my($line,$bytesread);
5741*0Sstevel@tonic-gate    $bytesread = $gz->gzreadline($line);
5742*0Sstevel@tonic-gate    return undef if $bytesread <= 0;
5743*0Sstevel@tonic-gate    return $line;
5744*0Sstevel@tonic-gate  } else {
5745*0Sstevel@tonic-gate    my $fh = $self->{FH};
5746*0Sstevel@tonic-gate    return scalar <$fh>;
5747*0Sstevel@tonic-gate  }
5748*0Sstevel@tonic-gate}
5749*0Sstevel@tonic-gate
5750*0Sstevel@tonic-gate
5751*0Sstevel@tonic-gate# CPAN::Tarzip::READ
5752*0Sstevel@tonic-gatesub READ {
5753*0Sstevel@tonic-gate  my($self,$ref,$length,$offset) = @_;
5754*0Sstevel@tonic-gate  die "read with offset not implemented" if defined $offset;
5755*0Sstevel@tonic-gate  if (exists $self->{GZ}) {
5756*0Sstevel@tonic-gate    my $gz = $self->{GZ};
5757*0Sstevel@tonic-gate    my $byteread = $gz->gzread($$ref,$length);# 30eaf79e8b446ef52464b5422da328a8
5758*0Sstevel@tonic-gate    return $byteread;
5759*0Sstevel@tonic-gate  } else {
5760*0Sstevel@tonic-gate    my $fh = $self->{FH};
5761*0Sstevel@tonic-gate    return read($fh,$$ref,$length);
5762*0Sstevel@tonic-gate  }
5763*0Sstevel@tonic-gate}
5764*0Sstevel@tonic-gate
5765*0Sstevel@tonic-gate
5766*0Sstevel@tonic-gate# CPAN::Tarzip::DESTROY
5767*0Sstevel@tonic-gatesub DESTROY {
5768*0Sstevel@tonic-gate    my($self) = @_;
5769*0Sstevel@tonic-gate    if (exists $self->{GZ}) {
5770*0Sstevel@tonic-gate        my $gz = $self->{GZ};
5771*0Sstevel@tonic-gate        $gz->gzclose() if defined $gz; # hard to say if it is allowed
5772*0Sstevel@tonic-gate                                       # to be undef ever. AK, 2000-09
5773*0Sstevel@tonic-gate    } else {
5774*0Sstevel@tonic-gate        my $fh = $self->{FH};
5775*0Sstevel@tonic-gate        $fh->close if defined $fh;
5776*0Sstevel@tonic-gate    }
5777*0Sstevel@tonic-gate    undef $self;
5778*0Sstevel@tonic-gate}
5779*0Sstevel@tonic-gate
5780*0Sstevel@tonic-gate
5781*0Sstevel@tonic-gate# CPAN::Tarzip::untar
5782*0Sstevel@tonic-gatesub untar {
5783*0Sstevel@tonic-gate  my($class,$file) = @_;
5784*0Sstevel@tonic-gate  my($prefer) = 0;
5785*0Sstevel@tonic-gate
5786*0Sstevel@tonic-gate  if (0) { # makes changing order easier
5787*0Sstevel@tonic-gate  } elsif ($BUGHUNTING){
5788*0Sstevel@tonic-gate      $prefer=2;
5789*0Sstevel@tonic-gate  } elsif (MM->maybe_command($CPAN::Config->{gzip})
5790*0Sstevel@tonic-gate           &&
5791*0Sstevel@tonic-gate           MM->maybe_command($CPAN::Config->{'tar'})) {
5792*0Sstevel@tonic-gate      # should be default until Archive::Tar is fixed
5793*0Sstevel@tonic-gate      $prefer = 1;
5794*0Sstevel@tonic-gate  } elsif (
5795*0Sstevel@tonic-gate           $CPAN::META->has_inst("Archive::Tar")
5796*0Sstevel@tonic-gate           &&
5797*0Sstevel@tonic-gate           $CPAN::META->has_inst("Compress::Zlib") ) {
5798*0Sstevel@tonic-gate      $prefer = 2;
5799*0Sstevel@tonic-gate  } else {
5800*0Sstevel@tonic-gate    $CPAN::Frontend->mydie(qq{
5801*0Sstevel@tonic-gateCPAN.pm needs either both external programs tar and gzip installed or
5802*0Sstevel@tonic-gateboth the modules Archive::Tar and Compress::Zlib. Neither prerequisite
5803*0Sstevel@tonic-gateis available. Can\'t continue.
5804*0Sstevel@tonic-gate});
5805*0Sstevel@tonic-gate  }
5806*0Sstevel@tonic-gate  if ($prefer==1) { # 1 => external gzip+tar
5807*0Sstevel@tonic-gate    my($system);
5808*0Sstevel@tonic-gate    my $is_compressed = $class->gtest($file);
5809*0Sstevel@tonic-gate    if ($is_compressed) {
5810*0Sstevel@tonic-gate        $system = "$CPAN::Config->{gzip} --decompress --stdout " .
5811*0Sstevel@tonic-gate            "< $file | $CPAN::Config->{tar} xvf -";
5812*0Sstevel@tonic-gate    } else {
5813*0Sstevel@tonic-gate        $system = "$CPAN::Config->{tar} xvf $file";
5814*0Sstevel@tonic-gate    }
5815*0Sstevel@tonic-gate    if (system($system) != 0) {
5816*0Sstevel@tonic-gate        # people find the most curious tar binaries that cannot handle
5817*0Sstevel@tonic-gate        # pipes
5818*0Sstevel@tonic-gate        if ($is_compressed) {
5819*0Sstevel@tonic-gate            (my $ungzf = $file) =~ s/\.gz(?!\n)\Z//;
5820*0Sstevel@tonic-gate            if (CPAN::Tarzip->gunzip($file, $ungzf)) {
5821*0Sstevel@tonic-gate                $CPAN::Frontend->myprint(qq{Uncompressed $file successfully\n});
5822*0Sstevel@tonic-gate            } else {
5823*0Sstevel@tonic-gate                $CPAN::Frontend->mydie(qq{Couldn\'t uncompress $file\n});
5824*0Sstevel@tonic-gate            }
5825*0Sstevel@tonic-gate            $file = $ungzf;
5826*0Sstevel@tonic-gate        }
5827*0Sstevel@tonic-gate        $system = "$CPAN::Config->{tar} xvf $file";
5828*0Sstevel@tonic-gate        $CPAN::Frontend->myprint(qq{Using Tar:$system:\n});
5829*0Sstevel@tonic-gate        if (system($system)==0) {
5830*0Sstevel@tonic-gate            $CPAN::Frontend->myprint(qq{Untarred $file successfully\n});
5831*0Sstevel@tonic-gate        } else {
5832*0Sstevel@tonic-gate            $CPAN::Frontend->mydie(qq{Couldn\'t untar $file\n});
5833*0Sstevel@tonic-gate        }
5834*0Sstevel@tonic-gate        return 1;
5835*0Sstevel@tonic-gate    } else {
5836*0Sstevel@tonic-gate        return 1;
5837*0Sstevel@tonic-gate    }
5838*0Sstevel@tonic-gate  } elsif ($prefer==2) { # 2 => modules
5839*0Sstevel@tonic-gate    my $tar = Archive::Tar->new($file,1);
5840*0Sstevel@tonic-gate    my $af; # archive file
5841*0Sstevel@tonic-gate    my @af;
5842*0Sstevel@tonic-gate    if ($BUGHUNTING) {
5843*0Sstevel@tonic-gate        # RCS 1.337 had this code, it turned out unacceptable slow but
5844*0Sstevel@tonic-gate        # it revealed a bug in Archive::Tar. Code is only here to hunt
5845*0Sstevel@tonic-gate        # the bug again. It should never be enabled in published code.
5846*0Sstevel@tonic-gate        # GDGraph3d-0.53 was an interesting case according to Larry
5847*0Sstevel@tonic-gate        # Virden.
5848*0Sstevel@tonic-gate        warn(">>>Bughunting code enabled<<< " x 20);
5849*0Sstevel@tonic-gate        for $af ($tar->list_files) {
5850*0Sstevel@tonic-gate            if ($af =~ m!^(/|\.\./)!) {
5851*0Sstevel@tonic-gate                $CPAN::Frontend->mydie("ALERT: Archive contains ".
5852*0Sstevel@tonic-gate                                       "illegal member [$af]");
5853*0Sstevel@tonic-gate            }
5854*0Sstevel@tonic-gate            $CPAN::Frontend->myprint("$af\n");
5855*0Sstevel@tonic-gate            $tar->extract($af); # slow but effective for finding the bug
5856*0Sstevel@tonic-gate            return if $CPAN::Signal;
5857*0Sstevel@tonic-gate        }
5858*0Sstevel@tonic-gate    } else {
5859*0Sstevel@tonic-gate        for $af ($tar->list_files) {
5860*0Sstevel@tonic-gate            if ($af =~ m!^(/|\.\./)!) {
5861*0Sstevel@tonic-gate                $CPAN::Frontend->mydie("ALERT: Archive contains ".
5862*0Sstevel@tonic-gate                                       "illegal member [$af]");
5863*0Sstevel@tonic-gate            }
5864*0Sstevel@tonic-gate            $CPAN::Frontend->myprint("$af\n");
5865*0Sstevel@tonic-gate            push @af, $af;
5866*0Sstevel@tonic-gate            return if $CPAN::Signal;
5867*0Sstevel@tonic-gate        }
5868*0Sstevel@tonic-gate        $tar->extract(@af);
5869*0Sstevel@tonic-gate    }
5870*0Sstevel@tonic-gate
5871*0Sstevel@tonic-gate    Mac::BuildTools::convert_files([$tar->list_files], 1)
5872*0Sstevel@tonic-gate        if ($^O eq 'MacOS');
5873*0Sstevel@tonic-gate
5874*0Sstevel@tonic-gate    return 1;
5875*0Sstevel@tonic-gate  }
5876*0Sstevel@tonic-gate}
5877*0Sstevel@tonic-gate
5878*0Sstevel@tonic-gatesub unzip {
5879*0Sstevel@tonic-gate    my($class,$file) = @_;
5880*0Sstevel@tonic-gate    if ($CPAN::META->has_inst("Archive::Zip")) {
5881*0Sstevel@tonic-gate        # blueprint of the code from Archive::Zip::Tree::extractTree();
5882*0Sstevel@tonic-gate        my $zip = Archive::Zip->new();
5883*0Sstevel@tonic-gate        my $status;
5884*0Sstevel@tonic-gate        $status = $zip->read($file);
5885*0Sstevel@tonic-gate        die "Read of file[$file] failed\n" if $status != Archive::Zip::AZ_OK();
5886*0Sstevel@tonic-gate        $CPAN::META->debug("Successfully read file[$file]") if $CPAN::DEBUG;
5887*0Sstevel@tonic-gate        my @members = $zip->members();
5888*0Sstevel@tonic-gate        for my $member ( @members ) {
5889*0Sstevel@tonic-gate            my $af = $member->fileName();
5890*0Sstevel@tonic-gate            if ($af =~ m!^(/|\.\./)!) {
5891*0Sstevel@tonic-gate                $CPAN::Frontend->mydie("ALERT: Archive contains ".
5892*0Sstevel@tonic-gate                                       "illegal member [$af]");
5893*0Sstevel@tonic-gate            }
5894*0Sstevel@tonic-gate            my $status = $member->extractToFileNamed( $af );
5895*0Sstevel@tonic-gate            $CPAN::META->debug("af[$af]status[$status]") if $CPAN::DEBUG;
5896*0Sstevel@tonic-gate            die "Extracting of file[$af] from zipfile[$file] failed\n" if
5897*0Sstevel@tonic-gate                $status != Archive::Zip::AZ_OK();
5898*0Sstevel@tonic-gate            return if $CPAN::Signal;
5899*0Sstevel@tonic-gate        }
5900*0Sstevel@tonic-gate        return 1;
5901*0Sstevel@tonic-gate    } else {
5902*0Sstevel@tonic-gate        my $unzip = $CPAN::Config->{unzip} or
5903*0Sstevel@tonic-gate            $CPAN::Frontend->mydie("Cannot unzip, no unzip program available");
5904*0Sstevel@tonic-gate        my @system = ($unzip, $file);
5905*0Sstevel@tonic-gate        return system(@system) == 0;
5906*0Sstevel@tonic-gate    }
5907*0Sstevel@tonic-gate}
5908*0Sstevel@tonic-gate
5909*0Sstevel@tonic-gate
5910*0Sstevel@tonic-gatepackage CPAN::Version;
5911*0Sstevel@tonic-gate# CPAN::Version::vcmp courtesy Jost Krieger
5912*0Sstevel@tonic-gatesub vcmp {
5913*0Sstevel@tonic-gate  my($self,$l,$r) = @_;
5914*0Sstevel@tonic-gate  local($^W) = 0;
5915*0Sstevel@tonic-gate  CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG;
5916*0Sstevel@tonic-gate
5917*0Sstevel@tonic-gate  return 0 if $l eq $r; # short circuit for quicker success
5918*0Sstevel@tonic-gate
5919*0Sstevel@tonic-gate  if ($l=~/^v/ <=> $r=~/^v/) {
5920*0Sstevel@tonic-gate      for ($l,$r) {
5921*0Sstevel@tonic-gate          next if /^v/;
5922*0Sstevel@tonic-gate          $_ = $self->float2vv($_);
5923*0Sstevel@tonic-gate      }
5924*0Sstevel@tonic-gate  }
5925*0Sstevel@tonic-gate
5926*0Sstevel@tonic-gate  return
5927*0Sstevel@tonic-gate      ($l ne "undef") <=> ($r ne "undef") ||
5928*0Sstevel@tonic-gate          ($] >= 5.006 &&
5929*0Sstevel@tonic-gate           $l =~ /^v/ &&
5930*0Sstevel@tonic-gate           $r =~ /^v/ &&
5931*0Sstevel@tonic-gate           $self->vstring($l) cmp $self->vstring($r)) ||
5932*0Sstevel@tonic-gate               $l <=> $r ||
5933*0Sstevel@tonic-gate                   $l cmp $r;
5934*0Sstevel@tonic-gate}
5935*0Sstevel@tonic-gate
5936*0Sstevel@tonic-gatesub vgt {
5937*0Sstevel@tonic-gate  my($self,$l,$r) = @_;
5938*0Sstevel@tonic-gate  $self->vcmp($l,$r) > 0;
5939*0Sstevel@tonic-gate}
5940*0Sstevel@tonic-gate
5941*0Sstevel@tonic-gatesub vstring {
5942*0Sstevel@tonic-gate  my($self,$n) = @_;
5943*0Sstevel@tonic-gate  $n =~ s/^v// or die "CPAN::Version::vstring() called with invalid arg [$n]";
5944*0Sstevel@tonic-gate  pack "U*", split /\./, $n;
5945*0Sstevel@tonic-gate}
5946*0Sstevel@tonic-gate
5947*0Sstevel@tonic-gate# vv => visible vstring
5948*0Sstevel@tonic-gatesub float2vv {
5949*0Sstevel@tonic-gate    my($self,$n) = @_;
5950*0Sstevel@tonic-gate    my($rev) = int($n);
5951*0Sstevel@tonic-gate    $rev ||= 0;
5952*0Sstevel@tonic-gate    my($mantissa) = $n =~ /\.(\d{1,12})/; # limit to 12 digits to limit
5953*0Sstevel@tonic-gate                                          # architecture influence
5954*0Sstevel@tonic-gate    $mantissa ||= 0;
5955*0Sstevel@tonic-gate    $mantissa .= "0" while length($mantissa)%3;
5956*0Sstevel@tonic-gate    my $ret = "v" . $rev;
5957*0Sstevel@tonic-gate    while ($mantissa) {
5958*0Sstevel@tonic-gate        $mantissa =~ s/(\d{1,3})// or
5959*0Sstevel@tonic-gate            die "Panic: length>0 but not a digit? mantissa[$mantissa]";
5960*0Sstevel@tonic-gate        $ret .= ".".int($1);
5961*0Sstevel@tonic-gate    }
5962*0Sstevel@tonic-gate    # warn "n[$n]ret[$ret]";
5963*0Sstevel@tonic-gate    $ret;
5964*0Sstevel@tonic-gate}
5965*0Sstevel@tonic-gate
5966*0Sstevel@tonic-gatesub readable {
5967*0Sstevel@tonic-gate  my($self,$n) = @_;
5968*0Sstevel@tonic-gate  $n =~ /^([\w\-\+\.]+)/;
5969*0Sstevel@tonic-gate
5970*0Sstevel@tonic-gate  return $1 if defined $1 && length($1)>0;
5971*0Sstevel@tonic-gate  # if the first user reaches version v43, he will be treated as "+".
5972*0Sstevel@tonic-gate  # We'll have to decide about a new rule here then, depending on what
5973*0Sstevel@tonic-gate  # will be the prevailing versioning behavior then.
5974*0Sstevel@tonic-gate
5975*0Sstevel@tonic-gate  if ($] < 5.006) { # or whenever v-strings were introduced
5976*0Sstevel@tonic-gate    # we get them wrong anyway, whatever we do, because 5.005 will
5977*0Sstevel@tonic-gate    # have already interpreted 0.2.4 to be "0.24". So even if he
5978*0Sstevel@tonic-gate    # indexer sends us something like "v0.2.4" we compare wrongly.
5979*0Sstevel@tonic-gate
5980*0Sstevel@tonic-gate    # And if they say v1.2, then the old perl takes it as "v12"
5981*0Sstevel@tonic-gate
5982*0Sstevel@tonic-gate    $CPAN::Frontend->mywarn("Suspicious version string seen [$n]\n");
5983*0Sstevel@tonic-gate    return $n;
5984*0Sstevel@tonic-gate  }
5985*0Sstevel@tonic-gate  my $better = sprintf "v%vd", $n;
5986*0Sstevel@tonic-gate  CPAN->debug("n[$n] better[$better]") if $CPAN::DEBUG;
5987*0Sstevel@tonic-gate  return $better;
5988*0Sstevel@tonic-gate}
5989*0Sstevel@tonic-gate
5990*0Sstevel@tonic-gatepackage CPAN;
5991*0Sstevel@tonic-gate
5992*0Sstevel@tonic-gate1;
5993*0Sstevel@tonic-gate
5994*0Sstevel@tonic-gate__END__
5995*0Sstevel@tonic-gate
5996*0Sstevel@tonic-gate=head1 NAME
5997*0Sstevel@tonic-gate
5998*0Sstevel@tonic-gateCPAN - query, download and build perl modules from CPAN sites
5999*0Sstevel@tonic-gate
6000*0Sstevel@tonic-gate=head1 SYNOPSIS
6001*0Sstevel@tonic-gate
6002*0Sstevel@tonic-gateInteractive mode:
6003*0Sstevel@tonic-gate
6004*0Sstevel@tonic-gate  perl -MCPAN -e shell;
6005*0Sstevel@tonic-gate
6006*0Sstevel@tonic-gateBatch mode:
6007*0Sstevel@tonic-gate
6008*0Sstevel@tonic-gate  use CPAN;
6009*0Sstevel@tonic-gate
6010*0Sstevel@tonic-gate  autobundle, clean, install, make, recompile, test
6011*0Sstevel@tonic-gate
6012*0Sstevel@tonic-gate=head1 STATUS
6013*0Sstevel@tonic-gate
6014*0Sstevel@tonic-gateThis module will eventually be replaced by CPANPLUS. CPANPLUS is kind
6015*0Sstevel@tonic-gateof a modern rewrite from ground up with greater extensibility and more
6016*0Sstevel@tonic-gatefeatures but no full compatibility. If you're new to CPAN.pm, you
6017*0Sstevel@tonic-gateprobably should investigate if CPANPLUS is the better choice for you.
6018*0Sstevel@tonic-gateIf you're already used to CPAN.pm you're welcome to continue using it,
6019*0Sstevel@tonic-gateif you accept that its development is mostly (though not completely)
6020*0Sstevel@tonic-gatestalled.
6021*0Sstevel@tonic-gate
6022*0Sstevel@tonic-gate=head1 DESCRIPTION
6023*0Sstevel@tonic-gate
6024*0Sstevel@tonic-gateThe CPAN module is designed to automate the make and install of perl
6025*0Sstevel@tonic-gatemodules and extensions. It includes some primitive searching capabilities and
6026*0Sstevel@tonic-gateknows how to use Net::FTP or LWP (or lynx or an external ftp client)
6027*0Sstevel@tonic-gateto fetch the raw data from the net.
6028*0Sstevel@tonic-gate
6029*0Sstevel@tonic-gateModules are fetched from one or more of the mirrored CPAN
6030*0Sstevel@tonic-gate(Comprehensive Perl Archive Network) sites and unpacked in a dedicated
6031*0Sstevel@tonic-gatedirectory.
6032*0Sstevel@tonic-gate
6033*0Sstevel@tonic-gateThe CPAN module also supports the concept of named and versioned
6034*0Sstevel@tonic-gateI<bundles> of modules. Bundles simplify the handling of sets of
6035*0Sstevel@tonic-gaterelated modules. See Bundles below.
6036*0Sstevel@tonic-gate
6037*0Sstevel@tonic-gateThe package contains a session manager and a cache manager. There is
6038*0Sstevel@tonic-gateno status retained between sessions. The session manager keeps track
6039*0Sstevel@tonic-gateof what has been fetched, built and installed in the current
6040*0Sstevel@tonic-gatesession. The cache manager keeps track of the disk space occupied by
6041*0Sstevel@tonic-gatethe make processes and deletes excess space according to a simple FIFO
6042*0Sstevel@tonic-gatemechanism.
6043*0Sstevel@tonic-gate
6044*0Sstevel@tonic-gateFor extended searching capabilities there's a plugin for CPAN available,
6045*0Sstevel@tonic-gateL<C<CPAN::WAIT>|CPAN::WAIT>. C<CPAN::WAIT> is a full-text search engine
6046*0Sstevel@tonic-gatethat indexes all documents available in CPAN authors directories. If
6047*0Sstevel@tonic-gateC<CPAN::WAIT> is installed on your system, the interactive shell of
6048*0Sstevel@tonic-gateCPAN.pm will enable the C<wq>, C<wr>, C<wd>, C<wl>, and C<wh> commands
6049*0Sstevel@tonic-gatewhich send queries to the WAIT server that has been configured for your
6050*0Sstevel@tonic-gateinstallation.
6051*0Sstevel@tonic-gate
6052*0Sstevel@tonic-gateAll other methods provided are accessible in a programmer style and in an
6053*0Sstevel@tonic-gateinteractive shell style.
6054*0Sstevel@tonic-gate
6055*0Sstevel@tonic-gate=head2 Interactive Mode
6056*0Sstevel@tonic-gate
6057*0Sstevel@tonic-gateThe interactive mode is entered by running
6058*0Sstevel@tonic-gate
6059*0Sstevel@tonic-gate    perl -MCPAN -e shell
6060*0Sstevel@tonic-gate
6061*0Sstevel@tonic-gatewhich puts you into a readline interface. You will have the most fun if
6062*0Sstevel@tonic-gateyou install Term::ReadKey and Term::ReadLine to enjoy both history and
6063*0Sstevel@tonic-gatecommand completion.
6064*0Sstevel@tonic-gate
6065*0Sstevel@tonic-gateOnce you are on the command line, type 'h' and the rest should be
6066*0Sstevel@tonic-gateself-explanatory.
6067*0Sstevel@tonic-gate
6068*0Sstevel@tonic-gateThe function call C<shell> takes two optional arguments, one is the
6069*0Sstevel@tonic-gateprompt, the second is the default initial command line (the latter
6070*0Sstevel@tonic-gateonly works if a real ReadLine interface module is installed).
6071*0Sstevel@tonic-gate
6072*0Sstevel@tonic-gateThe most common uses of the interactive modes are
6073*0Sstevel@tonic-gate
6074*0Sstevel@tonic-gate=over 2
6075*0Sstevel@tonic-gate
6076*0Sstevel@tonic-gate=item Searching for authors, bundles, distribution files and modules
6077*0Sstevel@tonic-gate
6078*0Sstevel@tonic-gateThere are corresponding one-letter commands C<a>, C<b>, C<d>, and C<m>
6079*0Sstevel@tonic-gatefor each of the four categories and another, C<i> for any of the
6080*0Sstevel@tonic-gatementioned four. Each of the four entities is implemented as a class
6081*0Sstevel@tonic-gatewith slightly differing methods for displaying an object.
6082*0Sstevel@tonic-gate
6083*0Sstevel@tonic-gateArguments you pass to these commands are either strings exactly matching
6084*0Sstevel@tonic-gatethe identification string of an object or regular expressions that are
6085*0Sstevel@tonic-gatethen matched case-insensitively against various attributes of the
6086*0Sstevel@tonic-gateobjects. The parser recognizes a regular expression only if you
6087*0Sstevel@tonic-gateenclose it between two slashes.
6088*0Sstevel@tonic-gate
6089*0Sstevel@tonic-gateThe principle is that the number of found objects influences how an
6090*0Sstevel@tonic-gateitem is displayed. If the search finds one item, the result is
6091*0Sstevel@tonic-gatedisplayed with the rather verbose method C<as_string>, but if we find
6092*0Sstevel@tonic-gatemore than one, we display each object with the terse method
6093*0Sstevel@tonic-gate<as_glimpse>.
6094*0Sstevel@tonic-gate
6095*0Sstevel@tonic-gate=item make, test, install, clean  modules or distributions
6096*0Sstevel@tonic-gate
6097*0Sstevel@tonic-gateThese commands take any number of arguments and investigate what is
6098*0Sstevel@tonic-gatenecessary to perform the action. If the argument is a distribution
6099*0Sstevel@tonic-gatefile name (recognized by embedded slashes), it is processed. If it is
6100*0Sstevel@tonic-gatea module, CPAN determines the distribution file in which this module
6101*0Sstevel@tonic-gateis included and processes that, following any dependencies named in
6102*0Sstevel@tonic-gatethe module's Makefile.PL (this behavior is controlled by
6103*0Sstevel@tonic-gateI<prerequisites_policy>.)
6104*0Sstevel@tonic-gate
6105*0Sstevel@tonic-gateAny C<make> or C<test> are run unconditionally. An
6106*0Sstevel@tonic-gate
6107*0Sstevel@tonic-gate  install <distribution_file>
6108*0Sstevel@tonic-gate
6109*0Sstevel@tonic-gatealso is run unconditionally. But for
6110*0Sstevel@tonic-gate
6111*0Sstevel@tonic-gate  install <module>
6112*0Sstevel@tonic-gate
6113*0Sstevel@tonic-gateCPAN checks if an install is actually needed for it and prints
6114*0Sstevel@tonic-gateI<module up to date> in the case that the distribution file containing
6115*0Sstevel@tonic-gatethe module doesn't need to be updated.
6116*0Sstevel@tonic-gate
6117*0Sstevel@tonic-gateCPAN also keeps track of what it has done within the current session
6118*0Sstevel@tonic-gateand doesn't try to build a package a second time regardless if it
6119*0Sstevel@tonic-gatesucceeded or not. The C<force> command takes as a first argument the
6120*0Sstevel@tonic-gatemethod to invoke (currently: C<make>, C<test>, or C<install>) and executes the
6121*0Sstevel@tonic-gatecommand from scratch.
6122*0Sstevel@tonic-gate
6123*0Sstevel@tonic-gateExample:
6124*0Sstevel@tonic-gate
6125*0Sstevel@tonic-gate    cpan> install OpenGL
6126*0Sstevel@tonic-gate    OpenGL is up to date.
6127*0Sstevel@tonic-gate    cpan> force install OpenGL
6128*0Sstevel@tonic-gate    Running make
6129*0Sstevel@tonic-gate    OpenGL-0.4/
6130*0Sstevel@tonic-gate    OpenGL-0.4/COPYRIGHT
6131*0Sstevel@tonic-gate    [...]
6132*0Sstevel@tonic-gate
6133*0Sstevel@tonic-gateA C<clean> command results in a
6134*0Sstevel@tonic-gate
6135*0Sstevel@tonic-gate  make clean
6136*0Sstevel@tonic-gate
6137*0Sstevel@tonic-gatebeing executed within the distribution file's working directory.
6138*0Sstevel@tonic-gate
6139*0Sstevel@tonic-gate=item get, readme, look module or distribution
6140*0Sstevel@tonic-gate
6141*0Sstevel@tonic-gateC<get> downloads a distribution file without further action. C<readme>
6142*0Sstevel@tonic-gatedisplays the README file of the associated distribution. C<Look> gets
6143*0Sstevel@tonic-gateand untars (if not yet done) the distribution file, changes to the
6144*0Sstevel@tonic-gateappropriate directory and opens a subshell process in that directory.
6145*0Sstevel@tonic-gate
6146*0Sstevel@tonic-gate=item ls author
6147*0Sstevel@tonic-gate
6148*0Sstevel@tonic-gateC<ls> lists all distribution files in and below an author's CPAN
6149*0Sstevel@tonic-gatedirectory. Only those files that contain modules are listed and if
6150*0Sstevel@tonic-gatethere is more than one for any given module, only the most recent one
6151*0Sstevel@tonic-gateis listed.
6152*0Sstevel@tonic-gate
6153*0Sstevel@tonic-gate=item Signals
6154*0Sstevel@tonic-gate
6155*0Sstevel@tonic-gateCPAN.pm installs signal handlers for SIGINT and SIGTERM. While you are
6156*0Sstevel@tonic-gatein the cpan-shell it is intended that you can press C<^C> anytime and
6157*0Sstevel@tonic-gatereturn to the cpan-shell prompt. A SIGTERM will cause the cpan-shell
6158*0Sstevel@tonic-gateto clean up and leave the shell loop. You can emulate the effect of a
6159*0Sstevel@tonic-gateSIGTERM by sending two consecutive SIGINTs, which usually means by
6160*0Sstevel@tonic-gatepressing C<^C> twice.
6161*0Sstevel@tonic-gate
6162*0Sstevel@tonic-gateCPAN.pm ignores a SIGPIPE. If the user sets inactivity_timeout, a
6163*0Sstevel@tonic-gateSIGALRM is used during the run of the C<perl Makefile.PL> subprocess.
6164*0Sstevel@tonic-gate
6165*0Sstevel@tonic-gate=back
6166*0Sstevel@tonic-gate
6167*0Sstevel@tonic-gate=head2 CPAN::Shell
6168*0Sstevel@tonic-gate
6169*0Sstevel@tonic-gateThe commands that are available in the shell interface are methods in
6170*0Sstevel@tonic-gatethe package CPAN::Shell. If you enter the shell command, all your
6171*0Sstevel@tonic-gateinput is split by the Text::ParseWords::shellwords() routine which
6172*0Sstevel@tonic-gateacts like most shells do. The first word is being interpreted as the
6173*0Sstevel@tonic-gatemethod to be called and the rest of the words are treated as arguments
6174*0Sstevel@tonic-gateto this method. Continuation lines are supported if a line ends with a
6175*0Sstevel@tonic-gateliteral backslash.
6176*0Sstevel@tonic-gate
6177*0Sstevel@tonic-gate=head2 autobundle
6178*0Sstevel@tonic-gate
6179*0Sstevel@tonic-gateC<autobundle> writes a bundle file into the
6180*0Sstevel@tonic-gateC<$CPAN::Config-E<gt>{cpan_home}/Bundle> directory. The file contains
6181*0Sstevel@tonic-gatea list of all modules that are both available from CPAN and currently
6182*0Sstevel@tonic-gateinstalled within @INC. The name of the bundle file is based on the
6183*0Sstevel@tonic-gatecurrent date and a counter.
6184*0Sstevel@tonic-gate
6185*0Sstevel@tonic-gate=head2 recompile
6186*0Sstevel@tonic-gate
6187*0Sstevel@tonic-gaterecompile() is a very special command in that it takes no argument and
6188*0Sstevel@tonic-gateruns the make/test/install cycle with brute force over all installed
6189*0Sstevel@tonic-gatedynamically loadable extensions (aka XS modules) with 'force' in
6190*0Sstevel@tonic-gateeffect. The primary purpose of this command is to finish a network
6191*0Sstevel@tonic-gateinstallation. Imagine, you have a common source tree for two different
6192*0Sstevel@tonic-gatearchitectures. You decide to do a completely independent fresh
6193*0Sstevel@tonic-gateinstallation. You start on one architecture with the help of a Bundle
6194*0Sstevel@tonic-gatefile produced earlier. CPAN installs the whole Bundle for you, but
6195*0Sstevel@tonic-gatewhen you try to repeat the job on the second architecture, CPAN
6196*0Sstevel@tonic-gateresponds with a C<"Foo up to date"> message for all modules. So you
6197*0Sstevel@tonic-gateinvoke CPAN's recompile on the second architecture and you're done.
6198*0Sstevel@tonic-gate
6199*0Sstevel@tonic-gateAnother popular use for C<recompile> is to act as a rescue in case your
6200*0Sstevel@tonic-gateperl breaks binary compatibility. If one of the modules that CPAN uses
6201*0Sstevel@tonic-gateis in turn depending on binary compatibility (so you cannot run CPAN
6202*0Sstevel@tonic-gatecommands), then you should try the CPAN::Nox module for recovery.
6203*0Sstevel@tonic-gate
6204*0Sstevel@tonic-gate=head2 The four C<CPAN::*> Classes: Author, Bundle, Module, Distribution
6205*0Sstevel@tonic-gate
6206*0Sstevel@tonic-gateAlthough it may be considered internal, the class hierarchy does matter
6207*0Sstevel@tonic-gatefor both users and programmer. CPAN.pm deals with above mentioned four
6208*0Sstevel@tonic-gateclasses, and all those classes share a set of methods. A classical
6209*0Sstevel@tonic-gatesingle polymorphism is in effect. A metaclass object registers all
6210*0Sstevel@tonic-gateobjects of all kinds and indexes them with a string. The strings
6211*0Sstevel@tonic-gatereferencing objects have a separated namespace (well, not completely
6212*0Sstevel@tonic-gateseparated):
6213*0Sstevel@tonic-gate
6214*0Sstevel@tonic-gate         Namespace                         Class
6215*0Sstevel@tonic-gate
6216*0Sstevel@tonic-gate   words containing a "/" (slash)      Distribution
6217*0Sstevel@tonic-gate    words starting with Bundle::          Bundle
6218*0Sstevel@tonic-gate          everything else            Module or Author
6219*0Sstevel@tonic-gate
6220*0Sstevel@tonic-gateModules know their associated Distribution objects. They always refer
6221*0Sstevel@tonic-gateto the most recent official release. Developers may mark their releases
6222*0Sstevel@tonic-gateas unstable development versions (by inserting an underbar into the
6223*0Sstevel@tonic-gatemodule version number which will also be reflected in the distribution
6224*0Sstevel@tonic-gatename when you run 'make dist'), so the really hottest and newest
6225*0Sstevel@tonic-gatedistribution is not always the default.  If a module Foo circulates
6226*0Sstevel@tonic-gateon CPAN in both version 1.23 and 1.23_90, CPAN.pm offers a convenient
6227*0Sstevel@tonic-gateway to install version 1.23 by saying
6228*0Sstevel@tonic-gate
6229*0Sstevel@tonic-gate    install Foo
6230*0Sstevel@tonic-gate
6231*0Sstevel@tonic-gateThis would install the complete distribution file (say
6232*0Sstevel@tonic-gateBAR/Foo-1.23.tar.gz) with all accompanying material. But if you would
6233*0Sstevel@tonic-gatelike to install version 1.23_90, you need to know where the
6234*0Sstevel@tonic-gatedistribution file resides on CPAN relative to the authors/id/
6235*0Sstevel@tonic-gatedirectory. If the author is BAR, this might be BAR/Foo-1.23_90.tar.gz;
6236*0Sstevel@tonic-gateso you would have to say
6237*0Sstevel@tonic-gate
6238*0Sstevel@tonic-gate    install BAR/Foo-1.23_90.tar.gz
6239*0Sstevel@tonic-gate
6240*0Sstevel@tonic-gateThe first example will be driven by an object of the class
6241*0Sstevel@tonic-gateCPAN::Module, the second by an object of class CPAN::Distribution.
6242*0Sstevel@tonic-gate
6243*0Sstevel@tonic-gate=head2 Programmer's interface
6244*0Sstevel@tonic-gate
6245*0Sstevel@tonic-gateIf you do not enter the shell, the available shell commands are both
6246*0Sstevel@tonic-gateavailable as methods (C<CPAN::Shell-E<gt>install(...)>) and as
6247*0Sstevel@tonic-gatefunctions in the calling package (C<install(...)>).
6248*0Sstevel@tonic-gate
6249*0Sstevel@tonic-gateThere's currently only one class that has a stable interface -
6250*0Sstevel@tonic-gateCPAN::Shell. All commands that are available in the CPAN shell are
6251*0Sstevel@tonic-gatemethods of the class CPAN::Shell. Each of the commands that produce
6252*0Sstevel@tonic-gatelistings of modules (C<r>, C<autobundle>, C<u>) also return a list of
6253*0Sstevel@tonic-gatethe IDs of all modules within the list.
6254*0Sstevel@tonic-gate
6255*0Sstevel@tonic-gate=over 2
6256*0Sstevel@tonic-gate
6257*0Sstevel@tonic-gate=item expand($type,@things)
6258*0Sstevel@tonic-gate
6259*0Sstevel@tonic-gateThe IDs of all objects available within a program are strings that can
6260*0Sstevel@tonic-gatebe expanded to the corresponding real objects with the
6261*0Sstevel@tonic-gateC<CPAN::Shell-E<gt>expand("Module",@things)> method. Expand returns a
6262*0Sstevel@tonic-gatelist of CPAN::Module objects according to the C<@things> arguments
6263*0Sstevel@tonic-gategiven. In scalar context it only returns the first element of the
6264*0Sstevel@tonic-gatelist.
6265*0Sstevel@tonic-gate
6266*0Sstevel@tonic-gate=item expandany(@things)
6267*0Sstevel@tonic-gate
6268*0Sstevel@tonic-gateLike expand, but returns objects of the appropriate type, i.e.
6269*0Sstevel@tonic-gateCPAN::Bundle objects for bundles, CPAN::Module objects for modules and
6270*0Sstevel@tonic-gateCPAN::Distribution objects fro distributions.
6271*0Sstevel@tonic-gate
6272*0Sstevel@tonic-gate=item Programming Examples
6273*0Sstevel@tonic-gate
6274*0Sstevel@tonic-gateThis enables the programmer to do operations that combine
6275*0Sstevel@tonic-gatefunctionalities that are available in the shell.
6276*0Sstevel@tonic-gate
6277*0Sstevel@tonic-gate    # install everything that is outdated on my disk:
6278*0Sstevel@tonic-gate    perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)'
6279*0Sstevel@tonic-gate
6280*0Sstevel@tonic-gate    # install my favorite programs if necessary:
6281*0Sstevel@tonic-gate    for $mod (qw(Net::FTP Digest::MD5 Data::Dumper)){
6282*0Sstevel@tonic-gate        my $obj = CPAN::Shell->expand('Module',$mod);
6283*0Sstevel@tonic-gate        $obj->install;
6284*0Sstevel@tonic-gate    }
6285*0Sstevel@tonic-gate
6286*0Sstevel@tonic-gate    # list all modules on my disk that have no VERSION number
6287*0Sstevel@tonic-gate    for $mod (CPAN::Shell->expand("Module","/./")){
6288*0Sstevel@tonic-gate	next unless $mod->inst_file;
6289*0Sstevel@tonic-gate        # MakeMaker convention for undefined $VERSION:
6290*0Sstevel@tonic-gate	next unless $mod->inst_version eq "undef";
6291*0Sstevel@tonic-gate	print "No VERSION in ", $mod->id, "\n";
6292*0Sstevel@tonic-gate    }
6293*0Sstevel@tonic-gate
6294*0Sstevel@tonic-gate    # find out which distribution on CPAN contains a module:
6295*0Sstevel@tonic-gate    print CPAN::Shell->expand("Module","Apache::Constants")->cpan_file
6296*0Sstevel@tonic-gate
6297*0Sstevel@tonic-gateOr if you want to write a cronjob to watch The CPAN, you could list
6298*0Sstevel@tonic-gateall modules that need updating. First a quick and dirty way:
6299*0Sstevel@tonic-gate
6300*0Sstevel@tonic-gate    perl -e 'use CPAN; CPAN::Shell->r;'
6301*0Sstevel@tonic-gate
6302*0Sstevel@tonic-gateIf you don't want to get any output in the case that all modules are
6303*0Sstevel@tonic-gateup to date, you can parse the output of above command for the regular
6304*0Sstevel@tonic-gateexpression //modules are up to date// and decide to mail the output
6305*0Sstevel@tonic-gateonly if it doesn't match. Ick?
6306*0Sstevel@tonic-gate
6307*0Sstevel@tonic-gateIf you prefer to do it more in a programmer style in one single
6308*0Sstevel@tonic-gateprocess, maybe something like this suits you better:
6309*0Sstevel@tonic-gate
6310*0Sstevel@tonic-gate  # list all modules on my disk that have newer versions on CPAN
6311*0Sstevel@tonic-gate  for $mod (CPAN::Shell->expand("Module","/./")){
6312*0Sstevel@tonic-gate    next unless $mod->inst_file;
6313*0Sstevel@tonic-gate    next if $mod->uptodate;
6314*0Sstevel@tonic-gate    printf "Module %s is installed as %s, could be updated to %s from CPAN\n",
6315*0Sstevel@tonic-gate        $mod->id, $mod->inst_version, $mod->cpan_version;
6316*0Sstevel@tonic-gate  }
6317*0Sstevel@tonic-gate
6318*0Sstevel@tonic-gateIf that gives you too much output every day, you maybe only want to
6319*0Sstevel@tonic-gatewatch for three modules. You can write
6320*0Sstevel@tonic-gate
6321*0Sstevel@tonic-gate  for $mod (CPAN::Shell->expand("Module","/Apache|LWP|CGI/")){
6322*0Sstevel@tonic-gate
6323*0Sstevel@tonic-gateas the first line instead. Or you can combine some of the above
6324*0Sstevel@tonic-gatetricks:
6325*0Sstevel@tonic-gate
6326*0Sstevel@tonic-gate  # watch only for a new mod_perl module
6327*0Sstevel@tonic-gate  $mod = CPAN::Shell->expand("Module","mod_perl");
6328*0Sstevel@tonic-gate  exit if $mod->uptodate;
6329*0Sstevel@tonic-gate  # new mod_perl arrived, let me know all update recommendations
6330*0Sstevel@tonic-gate  CPAN::Shell->r;
6331*0Sstevel@tonic-gate
6332*0Sstevel@tonic-gate=back
6333*0Sstevel@tonic-gate
6334*0Sstevel@tonic-gate=head2 Methods in the other Classes
6335*0Sstevel@tonic-gate
6336*0Sstevel@tonic-gateThe programming interface for the classes CPAN::Module,
6337*0Sstevel@tonic-gateCPAN::Distribution, CPAN::Bundle, and CPAN::Author is still considered
6338*0Sstevel@tonic-gatebeta and partially even alpha. In the following paragraphs only those
6339*0Sstevel@tonic-gatemethods are documented that have proven useful over a longer time and
6340*0Sstevel@tonic-gatethus are unlikely to change.
6341*0Sstevel@tonic-gate
6342*0Sstevel@tonic-gate=over 4
6343*0Sstevel@tonic-gate
6344*0Sstevel@tonic-gate=item CPAN::Author::as_glimpse()
6345*0Sstevel@tonic-gate
6346*0Sstevel@tonic-gateReturns a one-line description of the author
6347*0Sstevel@tonic-gate
6348*0Sstevel@tonic-gate=item CPAN::Author::as_string()
6349*0Sstevel@tonic-gate
6350*0Sstevel@tonic-gateReturns a multi-line description of the author
6351*0Sstevel@tonic-gate
6352*0Sstevel@tonic-gate=item CPAN::Author::email()
6353*0Sstevel@tonic-gate
6354*0Sstevel@tonic-gateReturns the author's email address
6355*0Sstevel@tonic-gate
6356*0Sstevel@tonic-gate=item CPAN::Author::fullname()
6357*0Sstevel@tonic-gate
6358*0Sstevel@tonic-gateReturns the author's name
6359*0Sstevel@tonic-gate
6360*0Sstevel@tonic-gate=item CPAN::Author::name()
6361*0Sstevel@tonic-gate
6362*0Sstevel@tonic-gateAn alias for fullname
6363*0Sstevel@tonic-gate
6364*0Sstevel@tonic-gate=item CPAN::Bundle::as_glimpse()
6365*0Sstevel@tonic-gate
6366*0Sstevel@tonic-gateReturns a one-line description of the bundle
6367*0Sstevel@tonic-gate
6368*0Sstevel@tonic-gate=item CPAN::Bundle::as_string()
6369*0Sstevel@tonic-gate
6370*0Sstevel@tonic-gateReturns a multi-line description of the bundle
6371*0Sstevel@tonic-gate
6372*0Sstevel@tonic-gate=item CPAN::Bundle::clean()
6373*0Sstevel@tonic-gate
6374*0Sstevel@tonic-gateRecursively runs the C<clean> method on all items contained in the bundle.
6375*0Sstevel@tonic-gate
6376*0Sstevel@tonic-gate=item CPAN::Bundle::contains()
6377*0Sstevel@tonic-gate
6378*0Sstevel@tonic-gateReturns a list of objects' IDs contained in a bundle. The associated
6379*0Sstevel@tonic-gateobjects may be bundles, modules or distributions.
6380*0Sstevel@tonic-gate
6381*0Sstevel@tonic-gate=item CPAN::Bundle::force($method,@args)
6382*0Sstevel@tonic-gate
6383*0Sstevel@tonic-gateForces CPAN to perform a task that normally would have failed. Force
6384*0Sstevel@tonic-gatetakes as arguments a method name to be called and any number of
6385*0Sstevel@tonic-gateadditional arguments that should be passed to the called method. The
6386*0Sstevel@tonic-gateinternals of the object get the needed changes so that CPAN.pm does
6387*0Sstevel@tonic-gatenot refuse to take the action. The C<force> is passed recursively to
6388*0Sstevel@tonic-gateall contained objects.
6389*0Sstevel@tonic-gate
6390*0Sstevel@tonic-gate=item CPAN::Bundle::get()
6391*0Sstevel@tonic-gate
6392*0Sstevel@tonic-gateRecursively runs the C<get> method on all items contained in the bundle
6393*0Sstevel@tonic-gate
6394*0Sstevel@tonic-gate=item CPAN::Bundle::inst_file()
6395*0Sstevel@tonic-gate
6396*0Sstevel@tonic-gateReturns the highest installed version of the bundle in either @INC or
6397*0Sstevel@tonic-gateC<$CPAN::Config->{cpan_home}>. Note that this is different from
6398*0Sstevel@tonic-gateCPAN::Module::inst_file.
6399*0Sstevel@tonic-gate
6400*0Sstevel@tonic-gate=item CPAN::Bundle::inst_version()
6401*0Sstevel@tonic-gate
6402*0Sstevel@tonic-gateLike CPAN::Bundle::inst_file, but returns the $VERSION
6403*0Sstevel@tonic-gate
6404*0Sstevel@tonic-gate=item CPAN::Bundle::uptodate()
6405*0Sstevel@tonic-gate
6406*0Sstevel@tonic-gateReturns 1 if the bundle itself and all its members are uptodate.
6407*0Sstevel@tonic-gate
6408*0Sstevel@tonic-gate=item CPAN::Bundle::install()
6409*0Sstevel@tonic-gate
6410*0Sstevel@tonic-gateRecursively runs the C<install> method on all items contained in the bundle
6411*0Sstevel@tonic-gate
6412*0Sstevel@tonic-gate=item CPAN::Bundle::make()
6413*0Sstevel@tonic-gate
6414*0Sstevel@tonic-gateRecursively runs the C<make> method on all items contained in the bundle
6415*0Sstevel@tonic-gate
6416*0Sstevel@tonic-gate=item CPAN::Bundle::readme()
6417*0Sstevel@tonic-gate
6418*0Sstevel@tonic-gateRecursively runs the C<readme> method on all items contained in the bundle
6419*0Sstevel@tonic-gate
6420*0Sstevel@tonic-gate=item CPAN::Bundle::test()
6421*0Sstevel@tonic-gate
6422*0Sstevel@tonic-gateRecursively runs the C<test> method on all items contained in the bundle
6423*0Sstevel@tonic-gate
6424*0Sstevel@tonic-gate=item CPAN::Distribution::as_glimpse()
6425*0Sstevel@tonic-gate
6426*0Sstevel@tonic-gateReturns a one-line description of the distribution
6427*0Sstevel@tonic-gate
6428*0Sstevel@tonic-gate=item CPAN::Distribution::as_string()
6429*0Sstevel@tonic-gate
6430*0Sstevel@tonic-gateReturns a multi-line description of the distribution
6431*0Sstevel@tonic-gate
6432*0Sstevel@tonic-gate=item CPAN::Distribution::clean()
6433*0Sstevel@tonic-gate
6434*0Sstevel@tonic-gateChanges to the directory where the distribution has been unpacked and
6435*0Sstevel@tonic-gateruns C<make clean> there.
6436*0Sstevel@tonic-gate
6437*0Sstevel@tonic-gate=item CPAN::Distribution::containsmods()
6438*0Sstevel@tonic-gate
6439*0Sstevel@tonic-gateReturns a list of IDs of modules contained in a distribution file.
6440*0Sstevel@tonic-gateOnly works for distributions listed in the 02packages.details.txt.gz
6441*0Sstevel@tonic-gatefile. This typically means that only the most recent version of a
6442*0Sstevel@tonic-gatedistribution is covered.
6443*0Sstevel@tonic-gate
6444*0Sstevel@tonic-gate=item CPAN::Distribution::cvs_import()
6445*0Sstevel@tonic-gate
6446*0Sstevel@tonic-gateChanges to the directory where the distribution has been unpacked and
6447*0Sstevel@tonic-gateruns something like
6448*0Sstevel@tonic-gate
6449*0Sstevel@tonic-gate    cvs -d $cvs_root import -m $cvs_log $cvs_dir $userid v$version
6450*0Sstevel@tonic-gate
6451*0Sstevel@tonic-gatethere.
6452*0Sstevel@tonic-gate
6453*0Sstevel@tonic-gate=item CPAN::Distribution::dir()
6454*0Sstevel@tonic-gate
6455*0Sstevel@tonic-gateReturns the directory into which this distribution has been unpacked.
6456*0Sstevel@tonic-gate
6457*0Sstevel@tonic-gate=item CPAN::Distribution::force($method,@args)
6458*0Sstevel@tonic-gate
6459*0Sstevel@tonic-gateForces CPAN to perform a task that normally would have failed. Force
6460*0Sstevel@tonic-gatetakes as arguments a method name to be called and any number of
6461*0Sstevel@tonic-gateadditional arguments that should be passed to the called method. The
6462*0Sstevel@tonic-gateinternals of the object get the needed changes so that CPAN.pm does
6463*0Sstevel@tonic-gatenot refuse to take the action.
6464*0Sstevel@tonic-gate
6465*0Sstevel@tonic-gate=item CPAN::Distribution::get()
6466*0Sstevel@tonic-gate
6467*0Sstevel@tonic-gateDownloads the distribution from CPAN and unpacks it. Does nothing if
6468*0Sstevel@tonic-gatethe distribution has already been downloaded and unpacked within the
6469*0Sstevel@tonic-gatecurrent session.
6470*0Sstevel@tonic-gate
6471*0Sstevel@tonic-gate=item CPAN::Distribution::install()
6472*0Sstevel@tonic-gate
6473*0Sstevel@tonic-gateChanges to the directory where the distribution has been unpacked and
6474*0Sstevel@tonic-gateruns the external command C<make install> there. If C<make> has not
6475*0Sstevel@tonic-gateyet been run, it will be run first. A C<make test> will be issued in
6476*0Sstevel@tonic-gateany case and if this fails, the install will be canceled. The
6477*0Sstevel@tonic-gatecancellation can be avoided by letting C<force> run the C<install> for
6478*0Sstevel@tonic-gateyou.
6479*0Sstevel@tonic-gate
6480*0Sstevel@tonic-gate=item CPAN::Distribution::isa_perl()
6481*0Sstevel@tonic-gate
6482*0Sstevel@tonic-gateReturns 1 if this distribution file seems to be a perl distribution.
6483*0Sstevel@tonic-gateNormally this is derived from the file name only, but the index from
6484*0Sstevel@tonic-gateCPAN can contain a hint to achieve a return value of true for other
6485*0Sstevel@tonic-gatefilenames too.
6486*0Sstevel@tonic-gate
6487*0Sstevel@tonic-gate=item CPAN::Distribution::look()
6488*0Sstevel@tonic-gate
6489*0Sstevel@tonic-gateChanges to the directory where the distribution has been unpacked and
6490*0Sstevel@tonic-gateopens a subshell there. Exiting the subshell returns.
6491*0Sstevel@tonic-gate
6492*0Sstevel@tonic-gate=item CPAN::Distribution::make()
6493*0Sstevel@tonic-gate
6494*0Sstevel@tonic-gateFirst runs the C<get> method to make sure the distribution is
6495*0Sstevel@tonic-gatedownloaded and unpacked. Changes to the directory where the
6496*0Sstevel@tonic-gatedistribution has been unpacked and runs the external commands C<perl
6497*0Sstevel@tonic-gateMakefile.PL> and C<make> there.
6498*0Sstevel@tonic-gate
6499*0Sstevel@tonic-gate=item CPAN::Distribution::prereq_pm()
6500*0Sstevel@tonic-gate
6501*0Sstevel@tonic-gateReturns the hash reference that has been announced by a distribution
6502*0Sstevel@tonic-gateas the PREREQ_PM hash in the Makefile.PL. Note: works only after an
6503*0Sstevel@tonic-gateattempt has been made to C<make> the distribution. Returns undef
6504*0Sstevel@tonic-gateotherwise.
6505*0Sstevel@tonic-gate
6506*0Sstevel@tonic-gate=item CPAN::Distribution::readme()
6507*0Sstevel@tonic-gate
6508*0Sstevel@tonic-gateDownloads the README file associated with a distribution and runs it
6509*0Sstevel@tonic-gatethrough the pager specified in C<$CPAN::Config->{pager}>.
6510*0Sstevel@tonic-gate
6511*0Sstevel@tonic-gate=item CPAN::Distribution::test()
6512*0Sstevel@tonic-gate
6513*0Sstevel@tonic-gateChanges to the directory where the distribution has been unpacked and
6514*0Sstevel@tonic-gateruns C<make test> there.
6515*0Sstevel@tonic-gate
6516*0Sstevel@tonic-gate=item CPAN::Distribution::uptodate()
6517*0Sstevel@tonic-gate
6518*0Sstevel@tonic-gateReturns 1 if all the modules contained in the distribution are
6519*0Sstevel@tonic-gateuptodate. Relies on containsmods.
6520*0Sstevel@tonic-gate
6521*0Sstevel@tonic-gate=item CPAN::Index::force_reload()
6522*0Sstevel@tonic-gate
6523*0Sstevel@tonic-gateForces a reload of all indices.
6524*0Sstevel@tonic-gate
6525*0Sstevel@tonic-gate=item CPAN::Index::reload()
6526*0Sstevel@tonic-gate
6527*0Sstevel@tonic-gateReloads all indices if they have been read more than
6528*0Sstevel@tonic-gateC<$CPAN::Config->{index_expire}> days.
6529*0Sstevel@tonic-gate
6530*0Sstevel@tonic-gate=item CPAN::InfoObj::dump()
6531*0Sstevel@tonic-gate
6532*0Sstevel@tonic-gateCPAN::Author, CPAN::Bundle, CPAN::Module, and CPAN::Distribution
6533*0Sstevel@tonic-gateinherit this method. It prints the data structure associated with an
6534*0Sstevel@tonic-gateobject. Useful for debugging. Note: the data structure is considered
6535*0Sstevel@tonic-gateinternal and thus subject to change without notice.
6536*0Sstevel@tonic-gate
6537*0Sstevel@tonic-gate=item CPAN::Module::as_glimpse()
6538*0Sstevel@tonic-gate
6539*0Sstevel@tonic-gateReturns a one-line description of the module
6540*0Sstevel@tonic-gate
6541*0Sstevel@tonic-gate=item CPAN::Module::as_string()
6542*0Sstevel@tonic-gate
6543*0Sstevel@tonic-gateReturns a multi-line description of the module
6544*0Sstevel@tonic-gate
6545*0Sstevel@tonic-gate=item CPAN::Module::clean()
6546*0Sstevel@tonic-gate
6547*0Sstevel@tonic-gateRuns a clean on the distribution associated with this module.
6548*0Sstevel@tonic-gate
6549*0Sstevel@tonic-gate=item CPAN::Module::cpan_file()
6550*0Sstevel@tonic-gate
6551*0Sstevel@tonic-gateReturns the filename on CPAN that is associated with the module.
6552*0Sstevel@tonic-gate
6553*0Sstevel@tonic-gate=item CPAN::Module::cpan_version()
6554*0Sstevel@tonic-gate
6555*0Sstevel@tonic-gateReturns the latest version of this module available on CPAN.
6556*0Sstevel@tonic-gate
6557*0Sstevel@tonic-gate=item CPAN::Module::cvs_import()
6558*0Sstevel@tonic-gate
6559*0Sstevel@tonic-gateRuns a cvs_import on the distribution associated with this module.
6560*0Sstevel@tonic-gate
6561*0Sstevel@tonic-gate=item CPAN::Module::description()
6562*0Sstevel@tonic-gate
6563*0Sstevel@tonic-gateReturns a 44 character description of this module. Only available for
6564*0Sstevel@tonic-gatemodules listed in The Module List (CPAN/modules/00modlist.long.html
6565*0Sstevel@tonic-gateor 00modlist.long.txt.gz)
6566*0Sstevel@tonic-gate
6567*0Sstevel@tonic-gate=item CPAN::Module::force($method,@args)
6568*0Sstevel@tonic-gate
6569*0Sstevel@tonic-gateForces CPAN to perform a task that normally would have failed. Force
6570*0Sstevel@tonic-gatetakes as arguments a method name to be called and any number of
6571*0Sstevel@tonic-gateadditional arguments that should be passed to the called method. The
6572*0Sstevel@tonic-gateinternals of the object get the needed changes so that CPAN.pm does
6573*0Sstevel@tonic-gatenot refuse to take the action.
6574*0Sstevel@tonic-gate
6575*0Sstevel@tonic-gate=item CPAN::Module::get()
6576*0Sstevel@tonic-gate
6577*0Sstevel@tonic-gateRuns a get on the distribution associated with this module.
6578*0Sstevel@tonic-gate
6579*0Sstevel@tonic-gate=item CPAN::Module::inst_file()
6580*0Sstevel@tonic-gate
6581*0Sstevel@tonic-gateReturns the filename of the module found in @INC. The first file found
6582*0Sstevel@tonic-gateis reported just like perl itself stops searching @INC when it finds a
6583*0Sstevel@tonic-gatemodule.
6584*0Sstevel@tonic-gate
6585*0Sstevel@tonic-gate=item CPAN::Module::inst_version()
6586*0Sstevel@tonic-gate
6587*0Sstevel@tonic-gateReturns the version number of the module in readable format.
6588*0Sstevel@tonic-gate
6589*0Sstevel@tonic-gate=item CPAN::Module::install()
6590*0Sstevel@tonic-gate
6591*0Sstevel@tonic-gateRuns an C<install> on the distribution associated with this module.
6592*0Sstevel@tonic-gate
6593*0Sstevel@tonic-gate=item CPAN::Module::look()
6594*0Sstevel@tonic-gate
6595*0Sstevel@tonic-gateChanges to the directory where the distribution associated with this
6596*0Sstevel@tonic-gatemodule has been unpacked and opens a subshell there. Exiting the
6597*0Sstevel@tonic-gatesubshell returns.
6598*0Sstevel@tonic-gate
6599*0Sstevel@tonic-gate=item CPAN::Module::make()
6600*0Sstevel@tonic-gate
6601*0Sstevel@tonic-gateRuns a C<make> on the distribution associated with this module.
6602*0Sstevel@tonic-gate
6603*0Sstevel@tonic-gate=item CPAN::Module::manpage_headline()
6604*0Sstevel@tonic-gate
6605*0Sstevel@tonic-gateIf module is installed, peeks into the module's manpage, reads the
6606*0Sstevel@tonic-gateheadline and returns it. Moreover, if the module has been downloaded
6607*0Sstevel@tonic-gatewithin this session, does the equivalent on the downloaded module even
6608*0Sstevel@tonic-gateif it is not installed.
6609*0Sstevel@tonic-gate
6610*0Sstevel@tonic-gate=item CPAN::Module::readme()
6611*0Sstevel@tonic-gate
6612*0Sstevel@tonic-gateRuns a C<readme> on the distribution associated with this module.
6613*0Sstevel@tonic-gate
6614*0Sstevel@tonic-gate=item CPAN::Module::test()
6615*0Sstevel@tonic-gate
6616*0Sstevel@tonic-gateRuns a C<test> on the distribution associated with this module.
6617*0Sstevel@tonic-gate
6618*0Sstevel@tonic-gate=item CPAN::Module::uptodate()
6619*0Sstevel@tonic-gate
6620*0Sstevel@tonic-gateReturns 1 if the module is installed and up-to-date.
6621*0Sstevel@tonic-gate
6622*0Sstevel@tonic-gate=item CPAN::Module::userid()
6623*0Sstevel@tonic-gate
6624*0Sstevel@tonic-gateReturns the author's ID of the module.
6625*0Sstevel@tonic-gate
6626*0Sstevel@tonic-gate=back
6627*0Sstevel@tonic-gate
6628*0Sstevel@tonic-gate=head2 Cache Manager
6629*0Sstevel@tonic-gate
6630*0Sstevel@tonic-gateCurrently the cache manager only keeps track of the build directory
6631*0Sstevel@tonic-gate($CPAN::Config->{build_dir}). It is a simple FIFO mechanism that
6632*0Sstevel@tonic-gatedeletes complete directories below C<build_dir> as soon as the size of
6633*0Sstevel@tonic-gateall directories there gets bigger than $CPAN::Config->{build_cache}
6634*0Sstevel@tonic-gate(in MB). The contents of this cache may be used for later
6635*0Sstevel@tonic-gatere-installations that you intend to do manually, but will never be
6636*0Sstevel@tonic-gatetrusted by CPAN itself. This is due to the fact that the user might
6637*0Sstevel@tonic-gateuse these directories for building modules on different architectures.
6638*0Sstevel@tonic-gate
6639*0Sstevel@tonic-gateThere is another directory ($CPAN::Config->{keep_source_where}) where
6640*0Sstevel@tonic-gatethe original distribution files are kept. This directory is not
6641*0Sstevel@tonic-gatecovered by the cache manager and must be controlled by the user. If
6642*0Sstevel@tonic-gateyou choose to have the same directory as build_dir and as
6643*0Sstevel@tonic-gatekeep_source_where directory, then your sources will be deleted with
6644*0Sstevel@tonic-gatethe same fifo mechanism.
6645*0Sstevel@tonic-gate
6646*0Sstevel@tonic-gate=head2 Bundles
6647*0Sstevel@tonic-gate
6648*0Sstevel@tonic-gateA bundle is just a perl module in the namespace Bundle:: that does not
6649*0Sstevel@tonic-gatedefine any functions or methods. It usually only contains documentation.
6650*0Sstevel@tonic-gate
6651*0Sstevel@tonic-gateIt starts like a perl module with a package declaration and a $VERSION
6652*0Sstevel@tonic-gatevariable. After that the pod section looks like any other pod with the
6653*0Sstevel@tonic-gateonly difference being that I<one special pod section> exists starting with
6654*0Sstevel@tonic-gate(verbatim):
6655*0Sstevel@tonic-gate
6656*0Sstevel@tonic-gate	=head1 CONTENTS
6657*0Sstevel@tonic-gate
6658*0Sstevel@tonic-gateIn this pod section each line obeys the format
6659*0Sstevel@tonic-gate
6660*0Sstevel@tonic-gate        Module_Name [Version_String] [- optional text]
6661*0Sstevel@tonic-gate
6662*0Sstevel@tonic-gateThe only required part is the first field, the name of a module
6663*0Sstevel@tonic-gate(e.g. Foo::Bar, ie. I<not> the name of the distribution file). The rest
6664*0Sstevel@tonic-gateof the line is optional. The comment part is delimited by a dash just
6665*0Sstevel@tonic-gateas in the man page header.
6666*0Sstevel@tonic-gate
6667*0Sstevel@tonic-gateThe distribution of a bundle should follow the same convention as
6668*0Sstevel@tonic-gateother distributions.
6669*0Sstevel@tonic-gate
6670*0Sstevel@tonic-gateBundles are treated specially in the CPAN package. If you say 'install
6671*0Sstevel@tonic-gateBundle::Tkkit' (assuming such a bundle exists), CPAN will install all
6672*0Sstevel@tonic-gatethe modules in the CONTENTS section of the pod. You can install your
6673*0Sstevel@tonic-gateown Bundles locally by placing a conformant Bundle file somewhere into
6674*0Sstevel@tonic-gateyour @INC path. The autobundle() command which is available in the
6675*0Sstevel@tonic-gateshell interface does that for you by including all currently installed
6676*0Sstevel@tonic-gatemodules in a snapshot bundle file.
6677*0Sstevel@tonic-gate
6678*0Sstevel@tonic-gate=head2 Prerequisites
6679*0Sstevel@tonic-gate
6680*0Sstevel@tonic-gateIf you have a local mirror of CPAN and can access all files with
6681*0Sstevel@tonic-gate"file:" URLs, then you only need a perl better than perl5.003 to run
6682*0Sstevel@tonic-gatethis module. Otherwise Net::FTP is strongly recommended. LWP may be
6683*0Sstevel@tonic-gaterequired for non-UNIX systems or if your nearest CPAN site is
6684*0Sstevel@tonic-gateassociated with a URL that is not C<ftp:>.
6685*0Sstevel@tonic-gate
6686*0Sstevel@tonic-gateIf you have neither Net::FTP nor LWP, there is a fallback mechanism
6687*0Sstevel@tonic-gateimplemented for an external ftp command or for an external lynx
6688*0Sstevel@tonic-gatecommand.
6689*0Sstevel@tonic-gate
6690*0Sstevel@tonic-gate=head2 Finding packages and VERSION
6691*0Sstevel@tonic-gate
6692*0Sstevel@tonic-gateThis module presumes that all packages on CPAN
6693*0Sstevel@tonic-gate
6694*0Sstevel@tonic-gate=over 2
6695*0Sstevel@tonic-gate
6696*0Sstevel@tonic-gate=item *
6697*0Sstevel@tonic-gate
6698*0Sstevel@tonic-gatedeclare their $VERSION variable in an easy to parse manner. This
6699*0Sstevel@tonic-gateprerequisite can hardly be relaxed because it consumes far too much
6700*0Sstevel@tonic-gatememory to load all packages into the running program just to determine
6701*0Sstevel@tonic-gatethe $VERSION variable. Currently all programs that are dealing with
6702*0Sstevel@tonic-gateversion use something like this
6703*0Sstevel@tonic-gate
6704*0Sstevel@tonic-gate    perl -MExtUtils::MakeMaker -le \
6705*0Sstevel@tonic-gate        'print MM->parse_version(shift)' filename
6706*0Sstevel@tonic-gate
6707*0Sstevel@tonic-gateIf you are author of a package and wonder if your $VERSION can be
6708*0Sstevel@tonic-gateparsed, please try the above method.
6709*0Sstevel@tonic-gate
6710*0Sstevel@tonic-gate=item *
6711*0Sstevel@tonic-gate
6712*0Sstevel@tonic-gatecome as compressed or gzipped tarfiles or as zip files and contain a
6713*0Sstevel@tonic-gateMakefile.PL (well, we try to handle a bit more, but without much
6714*0Sstevel@tonic-gateenthusiasm).
6715*0Sstevel@tonic-gate
6716*0Sstevel@tonic-gate=back
6717*0Sstevel@tonic-gate
6718*0Sstevel@tonic-gate=head2 Debugging
6719*0Sstevel@tonic-gate
6720*0Sstevel@tonic-gateThe debugging of this module is a bit complex, because we have
6721*0Sstevel@tonic-gateinterferences of the software producing the indices on CPAN, of the
6722*0Sstevel@tonic-gatemirroring process on CPAN, of packaging, of configuration, of
6723*0Sstevel@tonic-gatesynchronicity, and of bugs within CPAN.pm.
6724*0Sstevel@tonic-gate
6725*0Sstevel@tonic-gateFor code debugging in interactive mode you can try "o debug" which
6726*0Sstevel@tonic-gatewill list options for debugging the various parts of the code. You
6727*0Sstevel@tonic-gateshould know that "o debug" has built-in completion support.
6728*0Sstevel@tonic-gate
6729*0Sstevel@tonic-gateFor data debugging there is the C<dump> command which takes the same
6730*0Sstevel@tonic-gatearguments as make/test/install and outputs the object's Data::Dumper
6731*0Sstevel@tonic-gatedump.
6732*0Sstevel@tonic-gate
6733*0Sstevel@tonic-gate=head2 Floppy, Zip, Offline Mode
6734*0Sstevel@tonic-gate
6735*0Sstevel@tonic-gateCPAN.pm works nicely without network too. If you maintain machines
6736*0Sstevel@tonic-gatethat are not networked at all, you should consider working with file:
6737*0Sstevel@tonic-gateURLs. Of course, you have to collect your modules somewhere first. So
6738*0Sstevel@tonic-gateyou might use CPAN.pm to put together all you need on a networked
6739*0Sstevel@tonic-gatemachine. Then copy the $CPAN::Config->{keep_source_where} (but not
6740*0Sstevel@tonic-gate$CPAN::Config->{build_dir}) directory on a floppy. This floppy is kind
6741*0Sstevel@tonic-gateof a personal CPAN. CPAN.pm on the non-networked machines works nicely
6742*0Sstevel@tonic-gatewith this floppy. See also below the paragraph about CD-ROM support.
6743*0Sstevel@tonic-gate
6744*0Sstevel@tonic-gate=head1 CONFIGURATION
6745*0Sstevel@tonic-gate
6746*0Sstevel@tonic-gateWhen the CPAN module is used for the first time, a configuration
6747*0Sstevel@tonic-gatedialog tries to determine a couple of site specific options. The
6748*0Sstevel@tonic-gateresult of the dialog is stored in a hash reference C< $CPAN::Config >
6749*0Sstevel@tonic-gatein a file CPAN/Config.pm.
6750*0Sstevel@tonic-gate
6751*0Sstevel@tonic-gateThe default values defined in the CPAN/Config.pm file can be
6752*0Sstevel@tonic-gateoverridden in a user specific file: CPAN/MyConfig.pm. Such a file is
6753*0Sstevel@tonic-gatebest placed in $HOME/.cpan/CPAN/MyConfig.pm, because $HOME/.cpan is
6754*0Sstevel@tonic-gateadded to the search path of the CPAN module before the use() or
6755*0Sstevel@tonic-gaterequire() statements.
6756*0Sstevel@tonic-gate
6757*0Sstevel@tonic-gateThe configuration dialog can be started any time later again by
6758*0Sstevel@tonic-gateissueing the command C< o conf init > in the CPAN shell.
6759*0Sstevel@tonic-gate
6760*0Sstevel@tonic-gateCurrently the following keys in the hash reference $CPAN::Config are
6761*0Sstevel@tonic-gatedefined:
6762*0Sstevel@tonic-gate
6763*0Sstevel@tonic-gate  build_cache        size of cache for directories to build modules
6764*0Sstevel@tonic-gate  build_dir          locally accessible directory to build modules
6765*0Sstevel@tonic-gate  index_expire       after this many days refetch index files
6766*0Sstevel@tonic-gate  cache_metadata     use serializer to cache metadata
6767*0Sstevel@tonic-gate  cpan_home          local directory reserved for this package
6768*0Sstevel@tonic-gate  dontload_hash      anonymous hash: modules in the keys will not be
6769*0Sstevel@tonic-gate                     loaded by the CPAN::has_inst() routine
6770*0Sstevel@tonic-gate  gzip		     location of external program gzip
6771*0Sstevel@tonic-gate  histfile           file to maintain history between sessions
6772*0Sstevel@tonic-gate  histsize           maximum number of lines to keep in histfile
6773*0Sstevel@tonic-gate  inactivity_timeout breaks interactive Makefile.PLs after this
6774*0Sstevel@tonic-gate                     many seconds inactivity. Set to 0 to never break.
6775*0Sstevel@tonic-gate  inhibit_startup_message
6776*0Sstevel@tonic-gate                     if true, does not print the startup message
6777*0Sstevel@tonic-gate  keep_source_where  directory in which to keep the source (if we do)
6778*0Sstevel@tonic-gate  make               location of external make program
6779*0Sstevel@tonic-gate  make_arg	     arguments that should always be passed to 'make'
6780*0Sstevel@tonic-gate  make_install_arg   same as make_arg for 'make install'
6781*0Sstevel@tonic-gate  makepl_arg	     arguments passed to 'perl Makefile.PL'
6782*0Sstevel@tonic-gate  pager              location of external program more (or any pager)
6783*0Sstevel@tonic-gate  prerequisites_policy
6784*0Sstevel@tonic-gate                     what to do if you are missing module prerequisites
6785*0Sstevel@tonic-gate                     ('follow' automatically, 'ask' me, or 'ignore')
6786*0Sstevel@tonic-gate  proxy_user         username for accessing an authenticating proxy
6787*0Sstevel@tonic-gate  proxy_pass         password for accessing an authenticating proxy
6788*0Sstevel@tonic-gate  scan_cache	     controls scanning of cache ('atstart' or 'never')
6789*0Sstevel@tonic-gate  tar                location of external program tar
6790*0Sstevel@tonic-gate  term_is_latin      if true internal UTF-8 is translated to ISO-8859-1
6791*0Sstevel@tonic-gate                     (and nonsense for characters outside latin range)
6792*0Sstevel@tonic-gate  unzip              location of external program unzip
6793*0Sstevel@tonic-gate  urllist	     arrayref to nearby CPAN sites (or equivalent locations)
6794*0Sstevel@tonic-gate  wait_list          arrayref to a wait server to try (See CPAN::WAIT)
6795*0Sstevel@tonic-gate  ftp_proxy,      }  the three usual variables for configuring
6796*0Sstevel@tonic-gate    http_proxy,   }  proxy requests. Both as CPAN::Config variables
6797*0Sstevel@tonic-gate    no_proxy      }  and as environment variables configurable.
6798*0Sstevel@tonic-gate
6799*0Sstevel@tonic-gateYou can set and query each of these options interactively in the cpan
6800*0Sstevel@tonic-gateshell with the command set defined within the C<o conf> command:
6801*0Sstevel@tonic-gate
6802*0Sstevel@tonic-gate=over 2
6803*0Sstevel@tonic-gate
6804*0Sstevel@tonic-gate=item C<o conf E<lt>scalar optionE<gt>>
6805*0Sstevel@tonic-gate
6806*0Sstevel@tonic-gateprints the current value of the I<scalar option>
6807*0Sstevel@tonic-gate
6808*0Sstevel@tonic-gate=item C<o conf E<lt>scalar optionE<gt> E<lt>valueE<gt>>
6809*0Sstevel@tonic-gate
6810*0Sstevel@tonic-gateSets the value of the I<scalar option> to I<value>
6811*0Sstevel@tonic-gate
6812*0Sstevel@tonic-gate=item C<o conf E<lt>list optionE<gt>>
6813*0Sstevel@tonic-gate
6814*0Sstevel@tonic-gateprints the current value of the I<list option> in MakeMaker's
6815*0Sstevel@tonic-gateneatvalue format.
6816*0Sstevel@tonic-gate
6817*0Sstevel@tonic-gate=item C<o conf E<lt>list optionE<gt> [shift|pop]>
6818*0Sstevel@tonic-gate
6819*0Sstevel@tonic-gateshifts or pops the array in the I<list option> variable
6820*0Sstevel@tonic-gate
6821*0Sstevel@tonic-gate=item C<o conf E<lt>list optionE<gt> [unshift|push|splice] E<lt>listE<gt>>
6822*0Sstevel@tonic-gate
6823*0Sstevel@tonic-gateworks like the corresponding perl commands.
6824*0Sstevel@tonic-gate
6825*0Sstevel@tonic-gate=back
6826*0Sstevel@tonic-gate
6827*0Sstevel@tonic-gate=head2 Note on urllist parameter's format
6828*0Sstevel@tonic-gate
6829*0Sstevel@tonic-gateurllist parameters are URLs according to RFC 1738. We do a little
6830*0Sstevel@tonic-gateguessing if your URL is not compliant, but if you have problems with
6831*0Sstevel@tonic-gatefile URLs, please try the correct format. Either:
6832*0Sstevel@tonic-gate
6833*0Sstevel@tonic-gate    file://localhost/whatever/ftp/pub/CPAN/
6834*0Sstevel@tonic-gate
6835*0Sstevel@tonic-gateor
6836*0Sstevel@tonic-gate
6837*0Sstevel@tonic-gate    file:///home/ftp/pub/CPAN/
6838*0Sstevel@tonic-gate
6839*0Sstevel@tonic-gate=head2 urllist parameter has CD-ROM support
6840*0Sstevel@tonic-gate
6841*0Sstevel@tonic-gateThe C<urllist> parameter of the configuration table contains a list of
6842*0Sstevel@tonic-gateURLs that are to be used for downloading. If the list contains any
6843*0Sstevel@tonic-gateC<file> URLs, CPAN always tries to get files from there first. This
6844*0Sstevel@tonic-gatefeature is disabled for index files. So the recommendation for the
6845*0Sstevel@tonic-gateowner of a CD-ROM with CPAN contents is: include your local, possibly
6846*0Sstevel@tonic-gateoutdated CD-ROM as a C<file> URL at the end of urllist, e.g.
6847*0Sstevel@tonic-gate
6848*0Sstevel@tonic-gate  o conf urllist push file://localhost/CDROM/CPAN
6849*0Sstevel@tonic-gate
6850*0Sstevel@tonic-gateCPAN.pm will then fetch the index files from one of the CPAN sites
6851*0Sstevel@tonic-gatethat come at the beginning of urllist. It will later check for each
6852*0Sstevel@tonic-gatemodule if there is a local copy of the most recent version.
6853*0Sstevel@tonic-gate
6854*0Sstevel@tonic-gateAnother peculiarity of urllist is that the site that we could
6855*0Sstevel@tonic-gatesuccessfully fetch the last file from automatically gets a preference
6856*0Sstevel@tonic-gatetoken and is tried as the first site for the next request. So if you
6857*0Sstevel@tonic-gateadd a new site at runtime it may happen that the previously preferred
6858*0Sstevel@tonic-gatesite will be tried another time. This means that if you want to disallow
6859*0Sstevel@tonic-gatea site for the next transfer, it must be explicitly removed from
6860*0Sstevel@tonic-gateurllist.
6861*0Sstevel@tonic-gate
6862*0Sstevel@tonic-gate=head1 SECURITY
6863*0Sstevel@tonic-gate
6864*0Sstevel@tonic-gateThere's no strong security layer in CPAN.pm. CPAN.pm helps you to
6865*0Sstevel@tonic-gateinstall foreign, unmasked, unsigned code on your machine. We compare
6866*0Sstevel@tonic-gateto a checksum that comes from the net just as the distribution file
6867*0Sstevel@tonic-gateitself. If somebody has managed to tamper with the distribution file,
6868*0Sstevel@tonic-gatethey may have as well tampered with the CHECKSUMS file. Future
6869*0Sstevel@tonic-gatedevelopment will go towards strong authentication.
6870*0Sstevel@tonic-gate
6871*0Sstevel@tonic-gate=head1 EXPORT
6872*0Sstevel@tonic-gate
6873*0Sstevel@tonic-gateMost functions in package CPAN are exported per default. The reason
6874*0Sstevel@tonic-gatefor this is that the primary use is intended for the cpan shell or for
6875*0Sstevel@tonic-gateone-liners.
6876*0Sstevel@tonic-gate
6877*0Sstevel@tonic-gate=head1 POPULATE AN INSTALLATION WITH LOTS OF MODULES
6878*0Sstevel@tonic-gate
6879*0Sstevel@tonic-gatePopulating a freshly installed perl with my favorite modules is pretty
6880*0Sstevel@tonic-gateeasy if you maintain a private bundle definition file. To get a useful
6881*0Sstevel@tonic-gateblueprint of a bundle definition file, the command autobundle can be used
6882*0Sstevel@tonic-gateon the CPAN shell command line. This command writes a bundle definition
6883*0Sstevel@tonic-gatefile for all modules that are installed for the currently running perl
6884*0Sstevel@tonic-gateinterpreter. It's recommended to run this command only once and from then
6885*0Sstevel@tonic-gateon maintain the file manually under a private name, say
6886*0Sstevel@tonic-gateBundle/my_bundle.pm. With a clever bundle file you can then simply say
6887*0Sstevel@tonic-gate
6888*0Sstevel@tonic-gate    cpan> install Bundle::my_bundle
6889*0Sstevel@tonic-gate
6890*0Sstevel@tonic-gatethen answer a few questions and then go out for a coffee.
6891*0Sstevel@tonic-gate
6892*0Sstevel@tonic-gateMaintaining a bundle definition file means keeping track of two
6893*0Sstevel@tonic-gatethings: dependencies and interactivity. CPAN.pm sometimes fails on
6894*0Sstevel@tonic-gatecalculating dependencies because not all modules define all MakeMaker
6895*0Sstevel@tonic-gateattributes correctly, so a bundle definition file should specify
6896*0Sstevel@tonic-gateprerequisites as early as possible. On the other hand, it's a bit
6897*0Sstevel@tonic-gateannoying that many distributions need some interactive configuring. So
6898*0Sstevel@tonic-gatewhat I try to accomplish in my private bundle file is to have the
6899*0Sstevel@tonic-gatepackages that need to be configured early in the file and the gentle
6900*0Sstevel@tonic-gateones later, so I can go out after a few minutes and leave CPAN.pm
6901*0Sstevel@tonic-gateuntended.
6902*0Sstevel@tonic-gate
6903*0Sstevel@tonic-gate=head1 WORKING WITH CPAN.pm BEHIND FIREWALLS
6904*0Sstevel@tonic-gate
6905*0Sstevel@tonic-gateThanks to Graham Barr for contributing the following paragraphs about
6906*0Sstevel@tonic-gatethe interaction between perl, and various firewall configurations. For
6907*0Sstevel@tonic-gatefurther informations on firewalls, it is recommended to consult the
6908*0Sstevel@tonic-gatedocumentation that comes with the ncftp program. If you are unable to
6909*0Sstevel@tonic-gatego through the firewall with a simple Perl setup, it is very likely
6910*0Sstevel@tonic-gatethat you can configure ncftp so that it works for your firewall.
6911*0Sstevel@tonic-gate
6912*0Sstevel@tonic-gate=head2 Three basic types of firewalls
6913*0Sstevel@tonic-gate
6914*0Sstevel@tonic-gateFirewalls can be categorized into three basic types.
6915*0Sstevel@tonic-gate
6916*0Sstevel@tonic-gate=over 4
6917*0Sstevel@tonic-gate
6918*0Sstevel@tonic-gate=item http firewall
6919*0Sstevel@tonic-gate
6920*0Sstevel@tonic-gateThis is where the firewall machine runs a web server and to access the
6921*0Sstevel@tonic-gateoutside world you must do it via the web server. If you set environment
6922*0Sstevel@tonic-gatevariables like http_proxy or ftp_proxy to a values beginning with http://
6923*0Sstevel@tonic-gateor in your web browser you have to set proxy information then you know
6924*0Sstevel@tonic-gateyou are running an http firewall.
6925*0Sstevel@tonic-gate
6926*0Sstevel@tonic-gateTo access servers outside these types of firewalls with perl (even for
6927*0Sstevel@tonic-gateftp) you will need to use LWP.
6928*0Sstevel@tonic-gate
6929*0Sstevel@tonic-gate=item ftp firewall
6930*0Sstevel@tonic-gate
6931*0Sstevel@tonic-gateThis where the firewall machine runs an ftp server. This kind of
6932*0Sstevel@tonic-gatefirewall will only let you access ftp servers outside the firewall.
6933*0Sstevel@tonic-gateThis is usually done by connecting to the firewall with ftp, then
6934*0Sstevel@tonic-gateentering a username like "user@outside.host.com"
6935*0Sstevel@tonic-gate
6936*0Sstevel@tonic-gateTo access servers outside these type of firewalls with perl you
6937*0Sstevel@tonic-gatewill need to use Net::FTP.
6938*0Sstevel@tonic-gate
6939*0Sstevel@tonic-gate=item One way visibility
6940*0Sstevel@tonic-gate
6941*0Sstevel@tonic-gateI say one way visibility as these firewalls try to make themselves look
6942*0Sstevel@tonic-gateinvisible to the users inside the firewall. An FTP data connection is
6943*0Sstevel@tonic-gatenormally created by sending the remote server your IP address and then
6944*0Sstevel@tonic-gatelistening for the connection. But the remote server will not be able to
6945*0Sstevel@tonic-gateconnect to you because of the firewall. So for these types of firewall
6946*0Sstevel@tonic-gateFTP connections need to be done in a passive mode.
6947*0Sstevel@tonic-gate
6948*0Sstevel@tonic-gateThere are two that I can think off.
6949*0Sstevel@tonic-gate
6950*0Sstevel@tonic-gate=over 4
6951*0Sstevel@tonic-gate
6952*0Sstevel@tonic-gate=item SOCKS
6953*0Sstevel@tonic-gate
6954*0Sstevel@tonic-gateIf you are using a SOCKS firewall you will need to compile perl and link
6955*0Sstevel@tonic-gateit with the SOCKS library, this is what is normally called a 'socksified'
6956*0Sstevel@tonic-gateperl. With this executable you will be able to connect to servers outside
6957*0Sstevel@tonic-gatethe firewall as if it is not there.
6958*0Sstevel@tonic-gate
6959*0Sstevel@tonic-gate=item IP Masquerade
6960*0Sstevel@tonic-gate
6961*0Sstevel@tonic-gateThis is the firewall implemented in the Linux kernel, it allows you to
6962*0Sstevel@tonic-gatehide a complete network behind one IP address. With this firewall no
6963*0Sstevel@tonic-gatespecial compiling is needed as you can access hosts directly.
6964*0Sstevel@tonic-gate
6965*0Sstevel@tonic-gateFor accessing ftp servers behind such firewalls you may need to set
6966*0Sstevel@tonic-gatethe environment variable C<FTP_PASSIVE> to a true value, e.g.
6967*0Sstevel@tonic-gate
6968*0Sstevel@tonic-gate    env FTP_PASSIVE=1 perl -MCPAN -eshell
6969*0Sstevel@tonic-gate
6970*0Sstevel@tonic-gateor
6971*0Sstevel@tonic-gate
6972*0Sstevel@tonic-gate    perl -MCPAN -e '$ENV{FTP_PASSIVE} = 1; shell'
6973*0Sstevel@tonic-gate
6974*0Sstevel@tonic-gate
6975*0Sstevel@tonic-gate=back
6976*0Sstevel@tonic-gate
6977*0Sstevel@tonic-gate=back
6978*0Sstevel@tonic-gate
6979*0Sstevel@tonic-gate=head2 Configuring lynx or ncftp for going through a firewall
6980*0Sstevel@tonic-gate
6981*0Sstevel@tonic-gateIf you can go through your firewall with e.g. lynx, presumably with a
6982*0Sstevel@tonic-gatecommand such as
6983*0Sstevel@tonic-gate
6984*0Sstevel@tonic-gate    /usr/local/bin/lynx -pscott:tiger
6985*0Sstevel@tonic-gate
6986*0Sstevel@tonic-gatethen you would configure CPAN.pm with the command
6987*0Sstevel@tonic-gate
6988*0Sstevel@tonic-gate    o conf lynx "/usr/local/bin/lynx -pscott:tiger"
6989*0Sstevel@tonic-gate
6990*0Sstevel@tonic-gateThat's all. Similarly for ncftp or ftp, you would configure something
6991*0Sstevel@tonic-gatelike
6992*0Sstevel@tonic-gate
6993*0Sstevel@tonic-gate    o conf ncftp "/usr/bin/ncftp -f /home/scott/ncftplogin.cfg"
6994*0Sstevel@tonic-gate
6995*0Sstevel@tonic-gateYour mileage may vary...
6996*0Sstevel@tonic-gate
6997*0Sstevel@tonic-gate=head1 FAQ
6998*0Sstevel@tonic-gate
6999*0Sstevel@tonic-gate=over 4
7000*0Sstevel@tonic-gate
7001*0Sstevel@tonic-gate=item 1)
7002*0Sstevel@tonic-gate
7003*0Sstevel@tonic-gateI installed a new version of module X but CPAN keeps saying,
7004*0Sstevel@tonic-gateI have the old version installed
7005*0Sstevel@tonic-gate
7006*0Sstevel@tonic-gateMost probably you B<do> have the old version installed. This can
7007*0Sstevel@tonic-gatehappen if a module installs itself into a different directory in the
7008*0Sstevel@tonic-gate@INC path than it was previously installed. This is not really a
7009*0Sstevel@tonic-gateCPAN.pm problem, you would have the same problem when installing the
7010*0Sstevel@tonic-gatemodule manually. The easiest way to prevent this behaviour is to add
7011*0Sstevel@tonic-gatethe argument C<UNINST=1> to the C<make install> call, and that is why
7012*0Sstevel@tonic-gatemany people add this argument permanently by configuring
7013*0Sstevel@tonic-gate
7014*0Sstevel@tonic-gate  o conf make_install_arg UNINST=1
7015*0Sstevel@tonic-gate
7016*0Sstevel@tonic-gate=item 2)
7017*0Sstevel@tonic-gate
7018*0Sstevel@tonic-gateSo why is UNINST=1 not the default?
7019*0Sstevel@tonic-gate
7020*0Sstevel@tonic-gateBecause there are people who have their precise expectations about who
7021*0Sstevel@tonic-gatemay install where in the @INC path and who uses which @INC array. In
7022*0Sstevel@tonic-gatefine tuned environments C<UNINST=1> can cause damage.
7023*0Sstevel@tonic-gate
7024*0Sstevel@tonic-gate=item 3)
7025*0Sstevel@tonic-gate
7026*0Sstevel@tonic-gateI want to clean up my mess, and install a new perl along with
7027*0Sstevel@tonic-gateall modules I have. How do I go about it?
7028*0Sstevel@tonic-gate
7029*0Sstevel@tonic-gateRun the autobundle command for your old perl and optionally rename the
7030*0Sstevel@tonic-gateresulting bundle file (e.g. Bundle/mybundle.pm), install the new perl
7031*0Sstevel@tonic-gatewith the Configure option prefix, e.g.
7032*0Sstevel@tonic-gate
7033*0Sstevel@tonic-gate    ./Configure -Dprefix=/usr/local/perl-5.6.78.9
7034*0Sstevel@tonic-gate
7035*0Sstevel@tonic-gateInstall the bundle file you produced in the first step with something like
7036*0Sstevel@tonic-gate
7037*0Sstevel@tonic-gate    cpan> install Bundle::mybundle
7038*0Sstevel@tonic-gate
7039*0Sstevel@tonic-gateand you're done.
7040*0Sstevel@tonic-gate
7041*0Sstevel@tonic-gate=item 4)
7042*0Sstevel@tonic-gate
7043*0Sstevel@tonic-gateWhen I install bundles or multiple modules with one command
7044*0Sstevel@tonic-gatethere is too much output to keep track of.
7045*0Sstevel@tonic-gate
7046*0Sstevel@tonic-gateYou may want to configure something like
7047*0Sstevel@tonic-gate
7048*0Sstevel@tonic-gate  o conf make_arg "| tee -ai /root/.cpan/logs/make.out"
7049*0Sstevel@tonic-gate  o conf make_install_arg "| tee -ai /root/.cpan/logs/make_install.out"
7050*0Sstevel@tonic-gate
7051*0Sstevel@tonic-gateso that STDOUT is captured in a file for later inspection.
7052*0Sstevel@tonic-gate
7053*0Sstevel@tonic-gate
7054*0Sstevel@tonic-gate=item 5)
7055*0Sstevel@tonic-gate
7056*0Sstevel@tonic-gateI am not root, how can I install a module in a personal directory?
7057*0Sstevel@tonic-gate
7058*0Sstevel@tonic-gateYou will most probably like something like this:
7059*0Sstevel@tonic-gate
7060*0Sstevel@tonic-gate  o conf makepl_arg "LIB=~/myperl/lib \
7061*0Sstevel@tonic-gate                    INSTALLMAN1DIR=~/myperl/man/man1 \
7062*0Sstevel@tonic-gate                    INSTALLMAN3DIR=~/myperl/man/man3"
7063*0Sstevel@tonic-gate  install Sybase::Sybperl
7064*0Sstevel@tonic-gate
7065*0Sstevel@tonic-gateYou can make this setting permanent like all C<o conf> settings with
7066*0Sstevel@tonic-gateC<o conf commit>.
7067*0Sstevel@tonic-gate
7068*0Sstevel@tonic-gateYou will have to add ~/myperl/man to the MANPATH environment variable
7069*0Sstevel@tonic-gateand also tell your perl programs to look into ~/myperl/lib, e.g. by
7070*0Sstevel@tonic-gateincluding
7071*0Sstevel@tonic-gate
7072*0Sstevel@tonic-gate  use lib "$ENV{HOME}/myperl/lib";
7073*0Sstevel@tonic-gate
7074*0Sstevel@tonic-gateor setting the PERL5LIB environment variable.
7075*0Sstevel@tonic-gate
7076*0Sstevel@tonic-gateAnother thing you should bear in mind is that the UNINST parameter
7077*0Sstevel@tonic-gateshould never be set if you are not root.
7078*0Sstevel@tonic-gate
7079*0Sstevel@tonic-gate=item 6)
7080*0Sstevel@tonic-gate
7081*0Sstevel@tonic-gateHow to get a package, unwrap it, and make a change before building it?
7082*0Sstevel@tonic-gate
7083*0Sstevel@tonic-gate  look Sybase::Sybperl
7084*0Sstevel@tonic-gate
7085*0Sstevel@tonic-gate=item 7)
7086*0Sstevel@tonic-gate
7087*0Sstevel@tonic-gateI installed a Bundle and had a couple of fails. When I
7088*0Sstevel@tonic-gateretried, everything resolved nicely. Can this be fixed to work
7089*0Sstevel@tonic-gateon first try?
7090*0Sstevel@tonic-gate
7091*0Sstevel@tonic-gateThe reason for this is that CPAN does not know the dependencies of all
7092*0Sstevel@tonic-gatemodules when it starts out. To decide about the additional items to
7093*0Sstevel@tonic-gateinstall, it just uses data found in the generated Makefile. An
7094*0Sstevel@tonic-gateundetected missing piece breaks the process. But it may well be that
7095*0Sstevel@tonic-gateyour Bundle installs some prerequisite later than some depending item
7096*0Sstevel@tonic-gateand thus your second try is able to resolve everything. Please note,
7097*0Sstevel@tonic-gateCPAN.pm does not know the dependency tree in advance and cannot sort
7098*0Sstevel@tonic-gatethe queue of things to install in a topologically correct order. It
7099*0Sstevel@tonic-gateresolves perfectly well IFF all modules declare the prerequisites
7100*0Sstevel@tonic-gatecorrectly with the PREREQ_PM attribute to MakeMaker. For bundles which
7101*0Sstevel@tonic-gatefail and you need to install often, it is recommended sort the Bundle
7102*0Sstevel@tonic-gatedefinition file manually. It is planned to improve the metadata
7103*0Sstevel@tonic-gatesituation for dependencies on CPAN in general, but this will still
7104*0Sstevel@tonic-gatetake some time.
7105*0Sstevel@tonic-gate
7106*0Sstevel@tonic-gate=item 8)
7107*0Sstevel@tonic-gate
7108*0Sstevel@tonic-gateIn our intranet we have many modules for internal use. How
7109*0Sstevel@tonic-gatecan I integrate these modules with CPAN.pm but without uploading
7110*0Sstevel@tonic-gatethe modules to CPAN?
7111*0Sstevel@tonic-gate
7112*0Sstevel@tonic-gateHave a look at the CPAN::Site module.
7113*0Sstevel@tonic-gate
7114*0Sstevel@tonic-gate=item 9)
7115*0Sstevel@tonic-gate
7116*0Sstevel@tonic-gateWhen I run CPAN's shell, I get error msg about line 1 to 4,
7117*0Sstevel@tonic-gatesetting meta input/output via the /etc/inputrc file.
7118*0Sstevel@tonic-gate
7119*0Sstevel@tonic-gateSome versions of readline are picky about capitalization in the
7120*0Sstevel@tonic-gate/etc/inputrc file and specifically RedHat 6.2 comes with a
7121*0Sstevel@tonic-gate/etc/inputrc that contains the word C<on> in lowercase. Change the
7122*0Sstevel@tonic-gateoccurrences of C<on> to C<On> and the bug should disappear.
7123*0Sstevel@tonic-gate
7124*0Sstevel@tonic-gate=item 10)
7125*0Sstevel@tonic-gate
7126*0Sstevel@tonic-gateSome authors have strange characters in their names.
7127*0Sstevel@tonic-gate
7128*0Sstevel@tonic-gateInternally CPAN.pm uses the UTF-8 charset. If your terminal is
7129*0Sstevel@tonic-gateexpecting ISO-8859-1 charset, a converter can be activated by setting
7130*0Sstevel@tonic-gateterm_is_latin to a true value in your config file. One way of doing so
7131*0Sstevel@tonic-gatewould be
7132*0Sstevel@tonic-gate
7133*0Sstevel@tonic-gate    cpan> ! $CPAN::Config->{term_is_latin}=1
7134*0Sstevel@tonic-gate
7135*0Sstevel@tonic-gateExtended support for converters will be made available as soon as perl
7136*0Sstevel@tonic-gatebecomes stable with regard to charset issues.
7137*0Sstevel@tonic-gate
7138*0Sstevel@tonic-gate=back
7139*0Sstevel@tonic-gate
7140*0Sstevel@tonic-gate=head1 BUGS
7141*0Sstevel@tonic-gate
7142*0Sstevel@tonic-gateWe should give coverage for B<all> of the CPAN and not just the PAUSE
7143*0Sstevel@tonic-gatepart, right? In this discussion CPAN and PAUSE have become equal --
7144*0Sstevel@tonic-gatebut they are not. PAUSE is authors/, modules/ and scripts/. CPAN is
7145*0Sstevel@tonic-gatePAUSE plus the clpa/, doc/, misc/, ports/, and src/.
7146*0Sstevel@tonic-gate
7147*0Sstevel@tonic-gateFuture development should be directed towards a better integration of
7148*0Sstevel@tonic-gatethe other parts.
7149*0Sstevel@tonic-gate
7150*0Sstevel@tonic-gateIf a Makefile.PL requires special customization of libraries, prompts
7151*0Sstevel@tonic-gatethe user for special input, etc. then you may find CPAN is not able to
7152*0Sstevel@tonic-gatebuild the distribution. In that case, you should attempt the
7153*0Sstevel@tonic-gatetraditional method of building a Perl module package from a shell.
7154*0Sstevel@tonic-gate
7155*0Sstevel@tonic-gate=head1 AUTHOR
7156*0Sstevel@tonic-gate
7157*0Sstevel@tonic-gateAndreas Koenig E<lt>andreas.koenig@anima.deE<gt>
7158*0Sstevel@tonic-gate
7159*0Sstevel@tonic-gate=head1 TRANSLATIONS
7160*0Sstevel@tonic-gate
7161*0Sstevel@tonic-gateKawai,Takanori provides a Japanese translation of this manpage at
7162*0Sstevel@tonic-gatehttp://member.nifty.ne.jp/hippo2000/perltips/CPAN.htm
7163*0Sstevel@tonic-gate
7164*0Sstevel@tonic-gate=head1 SEE ALSO
7165*0Sstevel@tonic-gate
7166*0Sstevel@tonic-gateperl(1), CPAN::Nox(3)
7167*0Sstevel@tonic-gate
7168*0Sstevel@tonic-gate=cut
7169*0Sstevel@tonic-gate
7170