Commit | Line | Data |
---|---|---|
44d21104 | 1 | # -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- |
e82b9348 | 2 | use strict; |
8962fc49 | 3 | package CPAN; |
2b3bde2a | 4 | $CPAN::VERSION = '1.88_78'; |
8962fc49 | 5 | $CPAN::VERSION = eval $CPAN::VERSION; |
5f05dabc | 6 | |
e82b9348 | 7 | use CPAN::HandleConfig; |
554a9ef5 | 8 | use CPAN::Version; |
e82b9348 | 9 | use CPAN::Debug; |
135a59c2 | 10 | use CPAN::Queue; |
e82b9348 | 11 | use CPAN::Tarzip; |
5f05dabc | 12 | use Carp (); |
13 | use Config (); | |
14 | use Cwd (); | |
0cf35e6a | 15 | use DirHandle (); |
5f05dabc | 16 | use Exporter (); |
b96578bb SP |
17 | use ExtUtils::MakeMaker qw(prompt); # for some unknown reason, |
18 | # 5.005_04 does not work without | |
19 | # this | |
5f05dabc | 20 | use File::Basename (); |
10b2abe6 | 21 | use File::Copy (); |
5f05dabc | 22 | use File::Find; |
23 | use File::Path (); | |
0cf35e6a | 24 | use File::Spec (); |
da199366 | 25 | use FileHandle (); |
05bab18e | 26 | use Fcntl qw(:flock); |
5f05dabc | 27 | use Safe (); |
0cf35e6a | 28 | use Sys::Hostname qw(hostname); |
10b2abe6 | 29 | use Text::ParseWords (); |
0cf35e6a | 30 | use Text::Wrap (); |
8962fc49 SP |
31 | |
32 | # we need to run chdir all over and we would get at wrong libraries | |
33 | # there | |
34 | BEGIN { | |
35 | if (File::Spec->can("rel2abs")) { | |
36 | for my $inc (@INC) { | |
2b3bde2a | 37 | $inc = File::Spec->rel2abs($inc) unless ref $inc; |
8962fc49 SP |
38 | } |
39 | } | |
40 | } | |
41 | no lib "."; | |
5f05dabc | 42 | |
be708cc0 | 43 | require Mac::BuildTools if $^O eq 'MacOS'; |
be34b10d | 44 | $ENV{PERL5_CPAN_IS_RUNNING}=1; |
2b3bde2a | 45 | $ENV{PERL5_CPANPLUS_IS_RUNNING}=1; # https://rt.cpan.org/Ticket/Display.html?id=23735 |
be708cc0 | 46 | |
e82b9348 SP |
47 | END { $CPAN::End++; &cleanup; } |
48 | ||
da199366 | 49 | $CPAN::Signal ||= 0; |
c356248b | 50 | $CPAN::Frontend ||= "CPAN::Shell"; |
7fefbd44 RGS |
51 | unless (@CPAN::Defaultsites){ |
52 | @CPAN::Defaultsites = map { | |
53 | CPAN::URL->new(TEXT => $_, FROM => "DEF") | |
54 | } | |
55 | "http://www.perl.org/CPAN/", | |
56 | "ftp://ftp.perl.org/pub/CPAN/"; | |
57 | } | |
0cf35e6a | 58 | # $CPAN::iCwd (i for initial) is going to be initialized during find_perl |
607a774b | 59 | $CPAN::Perl ||= CPAN::find_perl(); |
554a9ef5 SP |
60 | $CPAN::Defaultdocs ||= "http://search.cpan.org/perldoc?"; |
61 | $CPAN::Defaultrecent ||= "http://search.cpan.org/recent"; | |
607a774b | 62 | |
05bab18e | 63 | # our globals are getting a mess |
6658a91b SP |
64 | use vars qw( |
65 | $AUTOLOAD | |
135a59c2 | 66 | $Be_Silent |
6658a91b | 67 | $CONFIG_DIRTY |
6658a91b SP |
68 | $Defaultdocs |
69 | $Defaultrecent | |
2b3bde2a | 70 | $Echo_readline |
6658a91b SP |
71 | $Frontend |
72 | $GOTOSHELL | |
73 | $HAS_USABLE | |
74 | $Have_warned | |
f20de9f0 | 75 | $MAX_RECURSION |
6658a91b | 76 | $META |
05bab18e | 77 | $RUN_DEGRADED |
6658a91b | 78 | $Signal |
be34b10d | 79 | $SQLite |
6658a91b SP |
80 | $Suppress_readline |
81 | $VERSION | |
135a59c2 | 82 | $autoload_recursion |
6658a91b SP |
83 | $term |
84 | @Defaultsites | |
85 | @EXPORT | |
135a59c2 | 86 | ); |
6d29edf5 | 87 | |
f20de9f0 SP |
88 | $MAX_RECURSION = 32; |
89 | ||
2e2b7522 | 90 | @CPAN::ISA = qw(CPAN::Debug Exporter); |
5f05dabc | 91 | |
44d21104 A |
92 | # note that these functions live in CPAN::Shell and get executed via |
93 | # AUTOLOAD when called directly | |
55e314ee | 94 | @EXPORT = qw( |
44d21104 A |
95 | autobundle |
96 | bundle | |
97 | clean | |
98 | cvs_import | |
99 | expand | |
100 | force | |
b72dd56f | 101 | fforce |
44d21104 A |
102 | get |
103 | install | |
05bab18e | 104 | install_tested |
f20de9f0 | 105 | is_tested |
44d21104 A |
106 | make |
107 | mkmyconfig | |
108 | notest | |
109 | perldoc | |
110 | readme | |
111 | recent | |
112 | recompile | |
8fc516fe | 113 | report |
44d21104 A |
114 | shell |
115 | test | |
ed84aac9 | 116 | upgrade |
da199366 | 117 | ); |
5f05dabc | 118 | |
0cf35e6a SP |
119 | sub soft_chdir_with_alternatives ($); |
120 | ||
135a59c2 A |
121 | { |
122 | $autoload_recursion ||= 0; | |
123 | ||
124 | #-> sub CPAN::AUTOLOAD ; | |
125 | sub AUTOLOAD { | |
126 | $autoload_recursion++; | |
127 | my($l) = $AUTOLOAD; | |
128 | $l =~ s/.*:://; | |
129 | if ($CPAN::Signal) { | |
130 | warn "Refusing to autoload '$l' while signal pending"; | |
131 | $autoload_recursion--; | |
132 | return; | |
133 | } | |
134 | if ($autoload_recursion > 1) { | |
135 | my $fullcommand = join " ", map { "'$_'" } $l, @_; | |
136 | warn "Refusing to autoload $fullcommand in recursion\n"; | |
137 | $autoload_recursion--; | |
138 | return; | |
139 | } | |
140 | my(%export); | |
141 | @export{@EXPORT} = ''; | |
142 | CPAN::HandleConfig->load unless $CPAN::Config_loaded++; | |
143 | if (exists $export{$l}){ | |
144 | CPAN::Shell->$l(@_); | |
145 | } else { | |
146 | die(qq{Unknown CPAN command "$AUTOLOAD". }. | |
147 | qq{Type ? for help.\n}); | |
148 | } | |
149 | $autoload_recursion--; | |
55e314ee A |
150 | } |
151 | } | |
152 | ||
153 | #-> sub CPAN::shell ; | |
154 | sub shell { | |
36263cb3 | 155 | my($self) = @_; |
911a92db | 156 | $Suppress_readline = ! -t STDIN unless defined $Suppress_readline; |
e82b9348 | 157 | CPAN::HandleConfig->load unless $CPAN::Config_loaded++; |
55e314ee | 158 | |
9ddc4ed0 | 159 | my $oprompt = shift || CPAN::Prompt->new; |
9d61fa1d A |
160 | my $prompt = $oprompt; |
161 | my $commandline = shift || ""; | |
9ddc4ed0 | 162 | $CPAN::CurrentCommandId ||= 1; |
5e05dca5 | 163 | |
55e314ee A |
164 | local($^W) = 1; |
165 | unless ($Suppress_readline) { | |
166 | require Term::ReadLine; | |
9d61fa1d A |
167 | if (! $term |
168 | or | |
169 | $term->ReadLine eq "Term::ReadLine::Stub" | |
170 | ) { | |
171 | $term = Term::ReadLine->new('CPAN Monitor'); | |
172 | } | |
36263cb3 GS |
173 | if ($term->ReadLine eq "Term::ReadLine::Gnu") { |
174 | my $attribs = $term->Attribs; | |
36263cb3 GS |
175 | $attribs->{attempted_completion_function} = sub { |
176 | &CPAN::Complete::gnu_cpl; | |
177 | } | |
36263cb3 GS |
178 | } else { |
179 | $readline::rl_completion_function = | |
180 | $readline::rl_completion_function = 'CPAN::Complete::cpl'; | |
181 | } | |
5fc0f0f6 JH |
182 | if (my $histfile = $CPAN::Config->{'histfile'}) {{ |
183 | unless ($term->can("AddHistory")) { | |
184 | $CPAN::Frontend->mywarn("Terminal does not support AddHistory.\n"); | |
185 | last; | |
186 | } | |
f20de9f0 | 187 | $META->readhist($term,$histfile); |
5fc0f0f6 | 188 | }} |
8962fc49 SP |
189 | for ($CPAN::Config->{term_ornaments}) { # alias |
190 | local $Term::ReadLine::termcap_nowarn = 1; | |
ed84aac9 A |
191 | $term->ornaments($_) if defined; |
192 | } | |
8962fc49 | 193 | # $term->OUT is autoflushed anyway |
911a92db GS |
194 | my $odef = select STDERR; |
195 | $| = 1; | |
196 | select STDOUT; | |
197 | $| = 1; | |
198 | select $odef; | |
55e314ee A |
199 | } |
200 | ||
55e314ee | 201 | $META->checklock(); |
135a59c2 A |
202 | my @cwd = grep { defined $_ and length $_ } |
203 | CPAN::anycwd(), | |
204 | File::Spec->can("tmpdir") ? File::Spec->tmpdir() : (), | |
205 | File::Spec->rootdir(); | |
911a92db GS |
206 | my $try_detect_readline; |
207 | $try_detect_readline = $term->ReadLine eq "Term::ReadLine::Stub" if $term; | |
55e314ee A |
208 | my $rl_avail = $Suppress_readline ? "suppressed" : |
209 | ($term->ReadLine ne "Term::ReadLine::Stub") ? "enabled" : | |
c4d24d4c | 210 | "available (try 'install Bundle::CPAN')"; |
55e314ee | 211 | |
8962fc49 SP |
212 | unless ($CPAN::Config->{'inhibit_startup_message'}){ |
213 | $CPAN::Frontend->myprint( | |
214 | sprintf qq{ | |
554a9ef5 | 215 | cpan shell -- CPAN exploration and modules installation (v%s) |
6d29edf5 | 216 | ReadLine support %s |
55e314ee | 217 | |
6d29edf5 | 218 | }, |
8962fc49 SP |
219 | $CPAN::VERSION, |
220 | $rl_avail | |
221 | ) | |
222 | } | |
c356248b | 223 | my($continuation) = ""; |
8962fc49 | 224 | my $last_term_ornaments; |
8d97e4a1 | 225 | SHELLCOMMAND: while () { |
55e314ee | 226 | if ($Suppress_readline) { |
2b3bde2a SP |
227 | if ($Echo_readline) { |
228 | $|=1; | |
229 | } | |
55e314ee | 230 | print $prompt; |
8d97e4a1 | 231 | last SHELLCOMMAND unless defined ($_ = <> ); |
2b3bde2a SP |
232 | if ($Echo_readline) { |
233 | # backdoor: I could not find a way to record sessions | |
234 | print $_; | |
235 | } | |
55e314ee A |
236 | chomp; |
237 | } else { | |
8d97e4a1 JH |
238 | last SHELLCOMMAND unless |
239 | defined ($_ = $term->readline($prompt, $commandline)); | |
55e314ee | 240 | } |
c356248b | 241 | $_ = "$continuation$_" if $continuation; |
55e314ee | 242 | s/^\s+//; |
8d97e4a1 | 243 | next SHELLCOMMAND if /^$/; |
2e2b7522 | 244 | $_ = 'h' if /^\s*\?/; |
09d9d230 | 245 | if (/^(?:q(?:uit)?|bye|exit)$/i) { |
8d97e4a1 | 246 | last SHELLCOMMAND; |
c356248b A |
247 | } elsif (s/\\$//s) { |
248 | chomp; | |
249 | $continuation = $_; | |
250 | $prompt = " > "; | |
251 | } elsif (/^\!/) { | |
55e314ee A |
252 | s/^\!//; |
253 | my($eval) = $_; | |
254 | package CPAN::Eval; | |
e82b9348 | 255 | use strict; |
55e314ee A |
256 | use vars qw($import_done); |
257 | CPAN->import(':DEFAULT') unless $import_done++; | |
258 | CPAN->debug("eval[$eval]") if $CPAN::DEBUG; | |
259 | eval($eval); | |
260 | warn $@ if $@; | |
c356248b | 261 | $continuation = ""; |
9d61fa1d | 262 | $prompt = $oprompt; |
55e314ee A |
263 | } elsif (/./) { |
264 | my(@line); | |
6a935156 SP |
265 | eval { @line = Text::ParseWords::shellwords($_) }; |
266 | warn($@), next SHELLCOMMAND if $@; | |
267 | warn("Text::Parsewords could not parse the line [$_]"), | |
268 | next SHELLCOMMAND unless @line; | |
55e314ee A |
269 | $CPAN::META->debug("line[".join("|",@line)."]") if $CPAN::DEBUG; |
270 | my $command = shift @line; | |
271 | eval { CPAN::Shell->$command(@line) }; | |
b72dd56f | 272 | if ($@ && "$@" =~ /\S/){ |
6658a91b | 273 | require Carp; |
b72dd56f | 274 | Carp::cluck("Catching error: '$@'"); |
6658a91b | 275 | } |
810a0276 | 276 | if ($command =~ /^(make|test|install|ff?orce|notest|clean|report|upgrade)$/) { |
9ddc4ed0 A |
277 | CPAN::Shell->failed($CPAN::CurrentCommandId,1); |
278 | } | |
0cf35e6a | 279 | soft_chdir_with_alternatives(\@cwd); |
c356248b A |
280 | $CPAN::Frontend->myprint("\n"); |
281 | $continuation = ""; | |
9ddc4ed0 | 282 | $CPAN::CurrentCommandId++; |
9d61fa1d | 283 | $prompt = $oprompt; |
55e314ee A |
284 | } |
285 | } continue { | |
9d61fa1d A |
286 | $commandline = ""; # I do want to be able to pass a default to |
287 | # shell, but on the second command I see no | |
288 | # use in that | |
09d9d230 | 289 | $Signal=0; |
36263cb3 GS |
290 | CPAN::Queue->nullify_queue; |
291 | if ($try_detect_readline) { | |
292 | if ($CPAN::META->has_inst("Term::ReadLine::Gnu") | |
293 | || | |
294 | $CPAN::META->has_inst("Term::ReadLine::Perl") | |
295 | ) { | |
296 | delete $INC{"Term/ReadLine.pm"}; | |
6d29edf5 JH |
297 | my $redef = 0; |
298 | local($SIG{__WARN__}) = CPAN::Shell::paintdots_onreload(\$redef); | |
36263cb3 | 299 | require Term::ReadLine; |
911a92db GS |
300 | $CPAN::Frontend->myprint("\n$redef subroutines in ". |
301 | "Term::ReadLine redefined\n"); | |
6a935156 | 302 | $GOTOSHELL = 1; |
36263cb3 GS |
303 | } |
304 | } | |
2ccf00a7 SP |
305 | if ($term and $term->can("ornaments")) { |
306 | for ($CPAN::Config->{term_ornaments}) { # alias | |
307 | if (defined $_) { | |
308 | if (not defined $last_term_ornaments | |
309 | or $_ != $last_term_ornaments | |
310 | ) { | |
311 | local $Term::ReadLine::termcap_nowarn = 1; | |
312 | $term->ornaments($_); | |
313 | $last_term_ornaments = $_; | |
314 | } | |
315 | } else { | |
316 | undef $last_term_ornaments; | |
8962fc49 | 317 | } |
8962fc49 SP |
318 | } |
319 | } | |
6658a91b SP |
320 | for my $class (qw(Module Distribution)) { |
321 | # again unsafe meta access? | |
322 | for my $dm (keys %{$CPAN::META->{readwrite}{"CPAN::$class"}}) { | |
323 | next unless $CPAN::META->{readwrite}{"CPAN::$class"}{$dm}{incommandcolor}; | |
324 | CPAN->debug("BUG: $class '$dm' was in command state, resetting"); | |
325 | delete $CPAN::META->{readwrite}{"CPAN::$class"}{$dm}{incommandcolor}; | |
6a935156 SP |
326 | } |
327 | } | |
328 | if ($GOTOSHELL) { | |
329 | $GOTOSHELL = 0; # not too often | |
330 | $META->savehist if $CPAN::term && $CPAN::term->can("GetHistory"); | |
331 | @_ = ($oprompt,""); | |
332 | goto &shell; | |
333 | } | |
55e314ee | 334 | } |
0cf35e6a | 335 | soft_chdir_with_alternatives(\@cwd); |
55e314ee A |
336 | } |
337 | ||
0cf35e6a SP |
338 | sub soft_chdir_with_alternatives ($) { |
339 | my($cwd) = @_; | |
135a59c2 A |
340 | unless (@$cwd) { |
341 | my $root = File::Spec->rootdir(); | |
342 | $CPAN::Frontend->mywarn(qq{Warning: no good directory to chdir to! | |
343 | Trying '$root' as temporary haven. | |
0cf35e6a | 344 | }); |
135a59c2 A |
345 | push @$cwd, $root; |
346 | } | |
347 | while () { | |
348 | if (chdir $cwd->[0]) { | |
349 | return; | |
0cf35e6a | 350 | } else { |
135a59c2 A |
351 | if (@$cwd>1) { |
352 | $CPAN::Frontend->mywarn(qq{Could not chdir to "$cwd->[0]": $! | |
353 | Trying to chdir to "$cwd->[1]" instead. | |
354 | }); | |
355 | shift @$cwd; | |
356 | } else { | |
357 | $CPAN::Frontend->mydie(qq{Could not chdir to "$cwd->[0]": $!}); | |
358 | } | |
0cf35e6a SP |
359 | } |
360 | } | |
361 | } | |
44d21104 | 362 | |
b72dd56f | 363 | sub _yaml_module () { |
05bab18e SP |
364 | my $yaml_module = $CPAN::Config->{yaml_module} || "YAML"; |
365 | if ( | |
366 | $yaml_module ne "YAML" | |
367 | && | |
368 | !$CPAN::META->has_inst($yaml_module) | |
369 | ) { | |
370 | # $CPAN::Frontend->mywarn("'$yaml_module' not installed, falling back to 'YAML'\n"); | |
371 | $yaml_module = "YAML"; | |
372 | } | |
373 | return $yaml_module; | |
374 | } | |
375 | ||
1e8f9a0a SP |
376 | # CPAN::_yaml_loadfile |
377 | sub _yaml_loadfile { | |
378 | my($self,$local_file) = @_; | |
05bab18e | 379 | return +[] unless -s $local_file; |
b72dd56f | 380 | my $yaml_module = _yaml_module; |
1e8f9a0a | 381 | if ($CPAN::META->has_inst($yaml_module)) { |
f20de9f0 SP |
382 | my $code; |
383 | if ($code = UNIVERSAL::can($yaml_module, "LoadFile")) { | |
384 | my @yaml; | |
385 | eval { @yaml = $code->($local_file); }; | |
386 | if ($@) { | |
387 | # this shall not be done by the frontend | |
388 | die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"parse",$@); | |
389 | } | |
390 | return \@yaml; | |
391 | } elsif ($code = UNIVERSAL::can($yaml_module, "Load")) { | |
392 | local *FH; | |
393 | open FH, $local_file or die "Could not open '$local_file': $!"; | |
394 | local $/; | |
395 | my $ystream = <FH>; | |
396 | my @yaml; | |
397 | eval { @yaml = $code->($ystream); }; | |
398 | if ($@) { | |
399 | # this shall not be done by the frontend | |
400 | die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"parse",$@); | |
401 | } | |
402 | return \@yaml; | |
1e8f9a0a | 403 | } |
1e8f9a0a | 404 | } else { |
b72dd56f SP |
405 | # this shall not be done by the frontend |
406 | die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "parse"); | |
1e8f9a0a | 407 | } |
6658a91b | 408 | return +[]; |
1e8f9a0a SP |
409 | } |
410 | ||
05bab18e SP |
411 | # CPAN::_yaml_dumpfile |
412 | sub _yaml_dumpfile { | |
b72dd56f SP |
413 | my($self,$local_file,@what) = @_; |
414 | my $yaml_module = _yaml_module; | |
05bab18e | 415 | if ($CPAN::META->has_inst($yaml_module)) { |
f20de9f0 | 416 | my $code; |
b72dd56f | 417 | if (UNIVERSAL::isa($local_file, "FileHandle")) { |
f20de9f0 | 418 | $code = UNIVERSAL::can($yaml_module, "Dump"); |
b72dd56f | 419 | eval { print $local_file $code->(@what) }; |
f20de9f0 | 420 | } elsif ($code = UNIVERSAL::can($yaml_module, "DumpFile")) { |
b72dd56f | 421 | eval { $code->($local_file,@what); }; |
f20de9f0 SP |
422 | } elsif ($code = UNIVERSAL::can($yaml_module, "Dump")) { |
423 | local *FH; | |
424 | open FH, ">$local_file" or die "Could not open '$local_file': $!"; | |
425 | print FH $code->(@what); | |
05bab18e SP |
426 | } |
427 | if ($@) { | |
b72dd56f | 428 | die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"dump",$@); |
05bab18e SP |
429 | } |
430 | } else { | |
b72dd56f | 431 | if (UNIVERSAL::isa($local_file, "FileHandle")) { |
be34b10d SP |
432 | # I think this case does not justify a warning at all |
433 | } else { | |
b72dd56f | 434 | die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "dump"); |
be34b10d | 435 | } |
05bab18e SP |
436 | } |
437 | } | |
438 | ||
be34b10d | 439 | sub _init_sqlite () { |
810a0276 | 440 | unless ($CPAN::META->has_inst("CPAN::SQLite")) { |
b72dd56f | 441 | $CPAN::Frontend->mywarn(qq{CPAN::SQLite not installed, trying to work without\n}) |
810a0276 | 442 | unless $Have_warned->{"CPAN::SQLite"}++; |
be34b10d SP |
443 | return; |
444 | } | |
810a0276 | 445 | require CPAN::SQLite::META; # not needed since CVS version of 2006-12-17 |
be34b10d SP |
446 | $CPAN::SQLite ||= CPAN::SQLite::META->new($CPAN::META); |
447 | } | |
448 | ||
810a0276 SP |
449 | { |
450 | my $negative_cache = {}; | |
451 | sub _sqlite_running { | |
452 | if ($negative_cache->{time} && time < $negative_cache->{time} + 60) { | |
453 | # need to cache the result, otherwise too slow | |
454 | return $negative_cache->{fact}; | |
455 | } else { | |
456 | $negative_cache = {}; # reset | |
457 | } | |
458 | my $ret = $CPAN::Config->{use_sqlite} && ($CPAN::SQLite || _init_sqlite()); | |
459 | return $ret if $ret; # fast anyway | |
460 | $negative_cache->{time} = time; | |
461 | return $negative_cache->{fact} = $ret; | |
462 | } | |
463 | } | |
464 | ||
55e314ee | 465 | package CPAN::CacheMgr; |
e82b9348 | 466 | use strict; |
c356248b | 467 | @CPAN::CacheMgr::ISA = qw(CPAN::InfoObj CPAN); |
55e314ee A |
468 | use File::Find; |
469 | ||
55e314ee | 470 | package CPAN::FTP; |
e82b9348 | 471 | use strict; |
05bab18e | 472 | use Fcntl qw(:flock); |
44d21104 | 473 | use vars qw($Ua $Thesite $ThesiteURL $Themethod); |
55e314ee A |
474 | @CPAN::FTP::ISA = qw(CPAN::Debug); |
475 | ||
c049f953 | 476 | package CPAN::LWP::UserAgent; |
e82b9348 | 477 | use strict; |
c049f953 | 478 | use vars qw(@ISA $USER $PASSWD $SETUPDONE); |
3c4b39be | 479 | # we delay requiring LWP::UserAgent and setting up inheritance until we need it |
c049f953 | 480 | |
55e314ee | 481 | package CPAN::Complete; |
e82b9348 | 482 | use strict; |
55e314ee | 483 | @CPAN::Complete::ISA = qw(CPAN::Debug); |
05bab18e SP |
484 | # Q: where is the "How do I add a new command" HOWTO? |
485 | # A: svn diff -r 1048:1049 where andk added the report command | |
9d61fa1d | 486 | @CPAN::Complete::COMMANDS = sort qw( |
0cf35e6a SP |
487 | ! a b d h i m o q r u |
488 | autobundle | |
489 | clean | |
490 | cvs_import | |
491 | dump | |
f20de9f0 | 492 | failed |
0cf35e6a | 493 | force |
b72dd56f | 494 | fforce |
05bab18e | 495 | hosts |
0cf35e6a | 496 | install |
05bab18e | 497 | install_tested |
f20de9f0 | 498 | is_tested |
0cf35e6a SP |
499 | look |
500 | ls | |
44d21104 A |
501 | make |
502 | mkmyconfig | |
0cf35e6a SP |
503 | notest |
504 | perldoc | |
505 | readme | |
506 | recent | |
44d21104 | 507 | recompile |
0cf35e6a | 508 | reload |
8fc516fe | 509 | report |
ed84aac9 | 510 | scripts |
44d21104 | 511 | test |
ed84aac9 | 512 | upgrade |
0cf35e6a | 513 | ); |
55e314ee A |
514 | |
515 | package CPAN::Index; | |
e82b9348 | 516 | use strict; |
05bab18e | 517 | use vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03 $HAVE_REANIMATED); |
55e314ee | 518 | @CPAN::Index::ISA = qw(CPAN::Debug); |
c049f953 JH |
519 | $LAST_TIME ||= 0; |
520 | $DATE_OF_03 ||= 0; | |
6d29edf5 JH |
521 | # use constant PROTOCOL => "2.0"; # outcommented to avoid warning on upgrade from 1.57 |
522 | sub PROTOCOL { 2.0 } | |
55e314ee A |
523 | |
524 | package CPAN::InfoObj; | |
e82b9348 | 525 | use strict; |
55e314ee A |
526 | @CPAN::InfoObj::ISA = qw(CPAN::Debug); |
527 | ||
528 | package CPAN::Author; | |
e82b9348 | 529 | use strict; |
55e314ee A |
530 | @CPAN::Author::ISA = qw(CPAN::InfoObj); |
531 | ||
532 | package CPAN::Distribution; | |
e82b9348 | 533 | use strict; |
55e314ee A |
534 | @CPAN::Distribution::ISA = qw(CPAN::InfoObj); |
535 | ||
536 | package CPAN::Bundle; | |
e82b9348 | 537 | use strict; |
55e314ee A |
538 | @CPAN::Bundle::ISA = qw(CPAN::Module); |
539 | ||
540 | package CPAN::Module; | |
e82b9348 | 541 | use strict; |
55e314ee | 542 | @CPAN::Module::ISA = qw(CPAN::InfoObj); |
10b2abe6 | 543 | |
35576f8c | 544 | package CPAN::Exception::RecursiveDependency; |
e82b9348 | 545 | use strict; |
35576f8c A |
546 | use overload '""' => "as_string"; |
547 | ||
f20de9f0 SP |
548 | # a module sees its distribution (no version) |
549 | # a distribution sees its prereqs (which are module names) (usually with versions) | |
550 | # a bundle sees its module names and/or its distributions (no version) | |
551 | ||
35576f8c A |
552 | sub new { |
553 | my($class) = shift; | |
554 | my($deps) = shift; | |
555 | my @deps; | |
556 | my %seen; | |
557 | for my $dep (@$deps) { | |
558 | push @deps, $dep; | |
559 | last if $seen{$dep}++; | |
560 | } | |
561 | bless { deps => \@deps }, $class; | |
562 | } | |
563 | ||
564 | sub as_string { | |
565 | my($self) = shift; | |
566 | "\nRecursive dependency detected:\n " . | |
567 | join("\n => ", @{$self->{deps}}) . | |
568 | ".\nCannot continue.\n"; | |
569 | } | |
570 | ||
b72dd56f SP |
571 | package CPAN::Exception::yaml_not_installed; |
572 | use strict; | |
573 | use overload '""' => "as_string"; | |
574 | ||
575 | sub new { | |
576 | my($class,$module,$file,$during) = @_; | |
577 | bless { module => $module, file => $file, during => $during }, $class; | |
578 | } | |
579 | ||
580 | sub as_string { | |
581 | my($self) = shift; | |
582 | "'$self->{module}' not installed, cannot $self->{during} '$self->{file}'\n"; | |
583 | } | |
584 | ||
585 | package CPAN::Exception::yaml_process_error; | |
586 | use strict; | |
587 | use overload '""' => "as_string"; | |
588 | ||
589 | sub new { | |
590 | my($class,$module,$file,$during,$error) = shift; | |
591 | bless { module => $module, | |
592 | file => $file, | |
593 | during => $during, | |
594 | error => $error }, $class; | |
595 | } | |
596 | ||
597 | sub as_string { | |
598 | my($self) = shift; | |
599 | "Alert: While trying to $self->{during} YAML file\n". | |
600 | " $self->{file}\n". | |
601 | "with '$self->{module}' the following error was encountered:\n". | |
602 | " $self->{error}\n"; | |
603 | } | |
604 | ||
9ddc4ed0 | 605 | package CPAN::Prompt; use overload '""' => "as_string"; |
4d1321a7 A |
606 | use vars qw($prompt); |
607 | $prompt = "cpan> "; | |
9ddc4ed0 | 608 | $CPAN::CurrentCommandId ||= 0; |
9ddc4ed0 A |
609 | sub new { |
610 | bless {}, shift; | |
611 | } | |
612 | sub as_string { | |
05bab18e SP |
613 | my $word = "cpan"; |
614 | unless ($CPAN::META->{LOCK}) { | |
615 | $word = "nolock_cpan"; | |
616 | } | |
9ddc4ed0 | 617 | if ($CPAN::Config->{commandnumber_in_prompt}) { |
05bab18e | 618 | sprintf "$word\[%d]> ", $CPAN::CurrentCommandId; |
9ddc4ed0 | 619 | } else { |
05bab18e | 620 | "$word> "; |
9ddc4ed0 A |
621 | } |
622 | } | |
623 | ||
7fefbd44 RGS |
624 | package CPAN::URL; use overload '""' => "as_string", fallback => 1; |
625 | # accessors: TEXT(the url string), FROM(DEF=>defaultlist,USER=>urllist), | |
626 | # planned are things like age or quality | |
627 | sub new { | |
628 | my($class,%args) = @_; | |
629 | bless { | |
630 | %args | |
631 | }, $class; | |
632 | } | |
633 | sub as_string { | |
634 | my($self) = @_; | |
635 | $self->text; | |
636 | } | |
637 | sub text { | |
638 | my($self,$set) = @_; | |
639 | if (defined $set) { | |
640 | $self->{TEXT} = $set; | |
641 | } | |
642 | $self->{TEXT}; | |
643 | } | |
644 | ||
9ddc4ed0 A |
645 | package CPAN::Distrostatus; |
646 | use overload '""' => "as_string", | |
647 | fallback => 1; | |
648 | sub new { | |
649 | my($class,$arg) = @_; | |
650 | bless { | |
651 | TEXT => $arg, | |
652 | FAILED => substr($arg,0,2) eq "NO", | |
653 | COMMANDID => $CPAN::CurrentCommandId, | |
be34b10d | 654 | TIME => time, |
9ddc4ed0 A |
655 | }, $class; |
656 | } | |
657 | sub commandid { shift->{COMMANDID} } | |
658 | sub failed { shift->{FAILED} } | |
659 | sub text { | |
660 | my($self,$set) = @_; | |
661 | if (defined $set) { | |
662 | $self->{TEXT} = $set; | |
663 | } | |
664 | $self->{TEXT}; | |
665 | } | |
666 | sub as_string { | |
667 | my($self) = @_; | |
4d1321a7 | 668 | $self->text; |
9ddc4ed0 A |
669 | } |
670 | ||
55e314ee | 671 | package CPAN::Shell; |
e82b9348 | 672 | use strict; |
6a935156 SP |
673 | use vars qw( |
674 | $ADVANCED_QUERY | |
675 | $AUTOLOAD | |
676 | $COLOR_REGISTERED | |
135a59c2 | 677 | $autoload_recursion |
6a935156 SP |
678 | $reload |
679 | @ISA | |
135a59c2 | 680 | ); |
55e314ee | 681 | @CPAN::Shell::ISA = qw(CPAN::Debug); |
9d61fa1d | 682 | $COLOR_REGISTERED ||= 0; |
55e314ee | 683 | |
135a59c2 | 684 | { |
135a59c2 A |
685 | $autoload_recursion ||= 0; |
686 | ||
687 | #-> sub CPAN::Shell::AUTOLOAD ; | |
688 | sub AUTOLOAD { | |
689 | $autoload_recursion++; | |
690 | my($l) = $AUTOLOAD; | |
691 | my $class = shift(@_); | |
692 | # warn "autoload[$l] class[$class]"; | |
693 | $l =~ s/.*:://; | |
694 | if ($CPAN::Signal) { | |
695 | warn "Refusing to autoload '$l' while signal pending"; | |
696 | $autoload_recursion--; | |
697 | return; | |
698 | } | |
699 | if ($autoload_recursion > 1) { | |
700 | my $fullcommand = join " ", map { "'$_'" } $l, @_; | |
701 | warn "Refusing to autoload $fullcommand in recursion\n"; | |
702 | $autoload_recursion--; | |
703 | return; | |
704 | } | |
705 | if ($l =~ /^w/) { | |
706 | # XXX needs to be reconsidered | |
707 | if ($CPAN::META->has_inst('CPAN::WAIT')) { | |
708 | CPAN::WAIT->$l(@_); | |
709 | } else { | |
710 | $CPAN::Frontend->mywarn(qq{ | |
55e314ee A |
711 | Commands starting with "w" require CPAN::WAIT to be installed. |
712 | Please consider installing CPAN::WAIT to use the fulltext index. | |
f610777f | 713 | For this you just need to type |
55e314ee | 714 | install CPAN::WAIT |
c356248b | 715 | }); |
6d29edf5 | 716 | } |
135a59c2 A |
717 | } else { |
718 | $CPAN::Frontend->mywarn(qq{Unknown shell command '$l'. }. | |
719 | qq{Type ? for help. | |
720 | }); | |
6d29edf5 | 721 | } |
135a59c2 | 722 | $autoload_recursion--; |
f610777f | 723 | } |
36263cb3 GS |
724 | } |
725 | ||
55e314ee | 726 | package CPAN; |
e82b9348 | 727 | use strict; |
55e314ee | 728 | |
2e2b7522 | 729 | $META ||= CPAN->new; # In case we re-eval ourselves we need the || |
55e314ee | 730 | |
6d29edf5 JH |
731 | # from here on only subs. |
732 | ################################################################################ | |
55e314ee | 733 | |
05bab18e SP |
734 | sub _perl_fingerprint { |
735 | my($self,$other_fingerprint) = @_; | |
736 | my $dll = eval {OS2::DLLname()}; | |
737 | my $mtime_dll = 0; | |
738 | if (defined $dll) { | |
739 | $mtime_dll = (-f $dll ? (stat(_))[9] : '-1'); | |
740 | } | |
f20de9f0 | 741 | my $mtime_perl = (-f $^X ? (stat(_))[9] : '-1'); |
05bab18e SP |
742 | my $this_fingerprint = { |
743 | '$^X' => $^X, | |
744 | sitearchexp => $Config::Config{sitearchexp}, | |
f20de9f0 | 745 | 'mtime_$^X' => $mtime_perl, |
05bab18e SP |
746 | 'mtime_dll' => $mtime_dll, |
747 | }; | |
748 | if ($other_fingerprint) { | |
749 | if (exists $other_fingerprint->{'stat($^X)'}) { # repair fp from rev. 1.88_57 | |
750 | $other_fingerprint->{'mtime_$^X'} = $other_fingerprint->{'stat($^X)'}[9]; | |
751 | } | |
752 | # mandatory keys since 1.88_57 | |
753 | for my $key (qw($^X sitearchexp mtime_dll mtime_$^X)) { | |
754 | return unless $other_fingerprint->{$key} eq $this_fingerprint->{$key}; | |
755 | } | |
756 | return 1; | |
757 | } else { | |
758 | return $this_fingerprint; | |
759 | } | |
760 | } | |
761 | ||
ed84aac9 A |
762 | sub suggest_myconfig () { |
763 | SUGGEST_MYCONFIG: if(!$INC{'CPAN/MyConfig.pm'}) { | |
764 | $CPAN::Frontend->myprint("You don't seem to have a user ". | |
765 | "configuration (MyConfig.pm) yet.\n"); | |
8962fc49 | 766 | my $new = CPAN::Shell::colorable_makemaker_prompt("Do you want to create a ". |
ed84aac9 A |
767 | "user configuration now? (Y/n)", |
768 | "yes"); | |
769 | if($new =~ m{^y}i) { | |
770 | CPAN::Shell->mkmyconfig(); | |
771 | return &checklock; | |
772 | } else { | |
773 | $CPAN::Frontend->mydie("OK, giving up."); | |
774 | } | |
775 | } | |
776 | } | |
777 | ||
6d29edf5 | 778 | #-> sub CPAN::all_objects ; |
36263cb3 | 779 | sub all_objects { |
5f05dabc | 780 | my($mgr,$class) = @_; |
e82b9348 | 781 | CPAN::HandleConfig->load unless $CPAN::Config_loaded++; |
5f05dabc | 782 | CPAN->debug("mgr[$mgr] class[$class]") if $CPAN::DEBUG; |
783 | CPAN::Index->reload; | |
6d29edf5 | 784 | values %{ $META->{readwrite}{$class} }; # unsafe meta access, ok |
5f05dabc | 785 | } |
786 | ||
c4d24d4c A |
787 | # Called by shell, not in batch mode. In batch mode I see no risk in |
788 | # having many processes updating something as installations are | |
789 | # continually checked at runtime. In shell mode I suspect it is | |
790 | # unintentional to open more than one shell at a time | |
791 | ||
10b2abe6 | 792 | #-> sub CPAN::checklock ; |
5f05dabc | 793 | sub checklock { |
794 | my($self) = @_; | |
5de3f0da | 795 | my $lockfile = File::Spec->catfile($CPAN::Config->{cpan_home},".lock"); |
5f05dabc | 796 | if (-f $lockfile && -M _ > 0) { |
6d29edf5 | 797 | my $fh = FileHandle->new($lockfile) or |
9ddc4ed0 | 798 | $CPAN::Frontend->mydie("Could not open lockfile '$lockfile': $!"); |
0dfa0441 JH |
799 | my $otherpid = <$fh>; |
800 | my $otherhost = <$fh>; | |
5f05dabc | 801 | $fh->close; |
0dfa0441 JH |
802 | if (defined $otherpid && $otherpid) { |
803 | chomp $otherpid; | |
804 | } | |
805 | if (defined $otherhost && $otherhost) { | |
806 | chomp $otherhost; | |
807 | } | |
808 | my $thishost = hostname(); | |
809 | if (defined $otherhost && defined $thishost && | |
810 | $otherhost ne '' && $thishost ne '' && | |
811 | $otherhost ne $thishost) { | |
9ddc4ed0 | 812 | $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile '$lockfile'\n". |
c9869e1c SP |
813 | "reports other host $otherhost and other ". |
814 | "process $otherpid.\n". | |
0dfa0441 | 815 | "Cannot proceed.\n")); |
05bab18e SP |
816 | } elsif ($RUN_DEGRADED) { |
817 | $CPAN::Frontend->mywarn("Running in degraded mode (experimental)\n"); | |
818 | } elsif (defined $otherpid && $otherpid) { | |
0dfa0441 | 819 | return if $$ == $otherpid; # should never happen |
c356248b A |
820 | $CPAN::Frontend->mywarn( |
821 | qq{ | |
0dfa0441 | 822 | There seems to be running another CPAN process (pid $otherpid). Contacting... |
c356248b | 823 | }); |
0dfa0441 | 824 | if (kill 0, $otherpid) { |
05bab18e SP |
825 | $CPAN::Frontend->mywarn(qq{Other job is running.\n}); |
826 | my($ans) = | |
827 | CPAN::Shell::colorable_makemaker_prompt | |
828 | (qq{Shall I try to run in degraded }. | |
829 | qq{mode? (Y/n)},"y"); | |
830 | if ($ans =~ /^y/i) { | |
831 | $CPAN::Frontend->mywarn("Running in degraded mode (experimental). | |
832 | Please report if something unexpected happens\n"); | |
833 | $RUN_DEGRADED = 1; | |
834 | for ($CPAN::Config) { | |
be34b10d SP |
835 | # XXX |
836 | # $_->{build_dir_reuse} = 0; # 2006-11-17 akoenig Why was that? | |
837 | $_->{commandnumber_in_prompt} = 0; # visibility | |
838 | $_->{histfile} = ""; # who should win otherwise? | |
839 | $_->{cache_metadata} = 0; # better would be a lock? | |
b72dd56f | 840 | $_->{use_sqlite} = 0; # better would be a write lock! |
05bab18e SP |
841 | } |
842 | } else { | |
843 | $CPAN::Frontend->mydie(" | |
844 | You may want to kill the other job and delete the lockfile. On UNIX try: | |
0dfa0441 | 845 | kill $otherpid |
c356248b | 846 | rm $lockfile |
05bab18e SP |
847 | "); |
848 | } | |
5f05dabc | 849 | } elsif (-w $lockfile) { |
e50380aa | 850 | my($ans) = |
8962fc49 | 851 | CPAN::Shell::colorable_makemaker_prompt |
05454584 | 852 | (qq{Other job not responding. Shall I overwrite }. |
9ddc4ed0 | 853 | qq{the lockfile '$lockfile'? (Y/n)},"y"); |
c356248b A |
854 | $CPAN::Frontend->myexit("Ok, bye\n") |
855 | unless $ans =~ /^y/i; | |
5f05dabc | 856 | } else { |
857 | Carp::croak( | |
9ddc4ed0 | 858 | qq{Lockfile '$lockfile' not writeable by you. }. |
05454584 | 859 | qq{Cannot proceed.\n}. |
5f05dabc | 860 | qq{ On UNIX try:\n}. |
9ddc4ed0 | 861 | qq{ rm '$lockfile'\n}. |
5f05dabc | 862 | qq{ and then rerun us.\n} |
863 | ); | |
864 | } | |
6d29edf5 | 865 | } else { |
05bab18e SP |
866 | $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Found invalid lockfile ". |
867 | "'$lockfile', please remove. Cannot proceed.\n")); | |
6d29edf5 | 868 | } |
5f05dabc | 869 | } |
36263cb3 GS |
870 | my $dotcpan = $CPAN::Config->{cpan_home}; |
871 | eval { File::Path::mkpath($dotcpan);}; | |
872 | if ($@) { | |
ed84aac9 A |
873 | # A special case at least for Jarkko. |
874 | my $firsterror = $@; | |
875 | my $seconderror; | |
876 | my $symlinkcpan; | |
877 | if (-l $dotcpan) { | |
878 | $symlinkcpan = readlink $dotcpan; | |
879 | die "readlink $dotcpan failed: $!" unless defined $symlinkcpan; | |
880 | eval { File::Path::mkpath($symlinkcpan); }; | |
881 | if ($@) { | |
882 | $seconderror = $@; | |
883 | } else { | |
884 | $CPAN::Frontend->mywarn(qq{ | |
36263cb3 GS |
885 | Working directory $symlinkcpan created. |
886 | }); | |
ed84aac9 A |
887 | } |
888 | } | |
889 | unless (-d $dotcpan) { | |
890 | my $mess = qq{ | |
36263cb3 GS |
891 | Your configuration suggests "$dotcpan" as your |
892 | CPAN.pm working directory. I could not create this directory due | |
893 | to this error: $firsterror\n}; | |
ed84aac9 | 894 | $mess .= qq{ |
36263cb3 GS |
895 | As "$dotcpan" is a symlink to "$symlinkcpan", |
896 | I tried to create that, but I failed with this error: $seconderror | |
897 | } if $seconderror; | |
ed84aac9 | 898 | $mess .= qq{ |
36263cb3 GS |
899 | Please make sure the directory exists and is writable. |
900 | }; | |
ed84aac9 A |
901 | $CPAN::Frontend->myprint($mess); |
902 | return suggest_myconfig; | |
903 | } | |
44d21104 | 904 | } # $@ after eval mkpath $dotcpan |
05bab18e SP |
905 | if (0) { # to test what happens when a race condition occurs |
906 | for (reverse 1..10) { | |
907 | print $_, "\n"; | |
908 | sleep 1; | |
909 | } | |
910 | } | |
911 | # locking | |
912 | if (!$RUN_DEGRADED && !$self->{LOCKFH}) { | |
913 | my $fh; | |
914 | unless ($fh = FileHandle->new("+>>$lockfile")) { | |
915 | if ($! =~ /Permission/) { | |
916 | $CPAN::Frontend->myprint(qq{ | |
5f05dabc | 917 | |
918 | Your configuration suggests that CPAN.pm should use a working | |
919 | directory of | |
920 | $CPAN::Config->{cpan_home} | |
921 | Unfortunately we could not create the lock file | |
922 | $lockfile | |
923 | due to permission problems. | |
924 | ||
925 | Please make sure that the configuration variable | |
926 | \$CPAN::Config->{cpan_home} | |
927 | points to a directory where you can write a .lock file. You can set | |
87892b73 RGS |
928 | this variable in either a CPAN/MyConfig.pm or a CPAN/Config.pm in your |
929 | \@INC path; | |
c356248b | 930 | }); |
05bab18e SP |
931 | return suggest_myconfig; |
932 | } | |
933 | } | |
934 | my $sleep = 1; | |
935 | while (!flock $fh, LOCK_EX|LOCK_NB) { | |
936 | if ($sleep>10) { | |
937 | $CPAN::Frontend->mydie("Giving up\n"); | |
938 | } | |
939 | $CPAN::Frontend->mysleep($sleep++); | |
940 | $CPAN::Frontend->mywarn("Could not lock lockfile with flock: $!; retrying\n"); | |
941 | } | |
942 | ||
943 | seek $fh, 0, 0; | |
944 | truncate $fh, 0; | |
945 | $fh->print($$, "\n"); | |
946 | $fh->print(hostname(), "\n"); | |
947 | $self->{LOCK} = $lockfile; | |
948 | $self->{LOCKFH} = $fh; | |
5f05dabc | 949 | } |
6d29edf5 | 950 | $SIG{TERM} = sub { |
135a59c2 A |
951 | my $sig = shift; |
952 | &cleanup; | |
953 | $CPAN::Frontend->mydie("Got SIG$sig, leaving"); | |
c356248b | 954 | }; |
6d29edf5 | 955 | $SIG{INT} = sub { |
09d9d230 | 956 | # no blocks!!! |
135a59c2 A |
957 | my $sig = shift; |
958 | &cleanup if $Signal; | |
959 | die "Got yet another signal" if $Signal > 1; | |
960 | $CPAN::Frontend->mydie("Got another SIG$sig") if $Signal; | |
961 | $CPAN::Frontend->mywarn("Caught SIG$sig, trying to continue\n"); | |
962 | $Signal++; | |
da199366 | 963 | }; |
911a92db GS |
964 | |
965 | # From: Larry Wall <larry@wall.org> | |
966 | # Subject: Re: deprecating SIGDIE | |
967 | # To: perl5-porters@perl.org | |
968 | # Date: Thu, 30 Sep 1999 14:58:40 -0700 (PDT) | |
969 | # | |
970 | # The original intent of __DIE__ was only to allow you to substitute one | |
971 | # kind of death for another on an application-wide basis without respect | |
972 | # to whether you were in an eval or not. As a global backstop, it should | |
973 | # not be used any more lightly (or any more heavily :-) than class | |
974 | # UNIVERSAL. Any attempt to build a general exception model on it should | |
975 | # be politely squashed. Any bug that causes every eval {} to have to be | |
976 | # modified should be not so politely squashed. | |
977 | # | |
978 | # Those are my current opinions. It is also my optinion that polite | |
979 | # arguments degenerate to personal arguments far too frequently, and that | |
980 | # when they do, it's because both people wanted it to, or at least didn't | |
981 | # sufficiently want it not to. | |
982 | # | |
983 | # Larry | |
984 | ||
6d29edf5 JH |
985 | # global backstop to cleanup if we should really die |
986 | $SIG{__DIE__} = \&cleanup; | |
e50380aa | 987 | $self->debug("Signal handler set.") if $CPAN::DEBUG; |
5f05dabc | 988 | } |
989 | ||
10b2abe6 | 990 | #-> sub CPAN::DESTROY ; |
5f05dabc | 991 | sub DESTROY { |
992 | &cleanup; # need an eval? | |
993 | } | |
994 | ||
9d61fa1d A |
995 | #-> sub CPAN::anycwd ; |
996 | sub anycwd () { | |
997 | my $getcwd; | |
998 | $getcwd = $CPAN::Config->{'getcwd'} || 'cwd'; | |
999 | CPAN->$getcwd(); | |
1000 | } | |
1001 | ||
55e314ee A |
1002 | #-> sub CPAN::cwd ; |
1003 | sub cwd {Cwd::cwd();} | |
1004 | ||
1005 | #-> sub CPAN::getcwd ; | |
1006 | sub getcwd {Cwd::getcwd();} | |
1007 | ||
ca79d794 SP |
1008 | #-> sub CPAN::fastcwd ; |
1009 | sub fastcwd {Cwd::fastcwd();} | |
1010 | ||
1011 | #-> sub CPAN::backtickcwd ; | |
1012 | sub backtickcwd {my $cwd = `cwd`; chomp $cwd; $cwd} | |
1013 | ||
607a774b MS |
1014 | #-> sub CPAN::find_perl ; |
1015 | sub find_perl { | |
1016 | my($perl) = File::Spec->file_name_is_absolute($^X) ? $^X : ""; | |
0cf35e6a | 1017 | my $pwd = $CPAN::iCwd = CPAN::anycwd(); |
607a774b MS |
1018 | my $candidate = File::Spec->catfile($pwd,$^X); |
1019 | $perl ||= $candidate if MM->maybe_command($candidate); | |
1020 | ||
1021 | unless ($perl) { | |
1022 | my ($component,$perl_name); | |
1023 | DIST_PERLNAME: foreach $perl_name ($^X, 'perl', 'perl5', "perl$]") { | |
1024 | PATH_COMPONENT: foreach $component (File::Spec->path(), | |
1025 | $Config::Config{'binexp'}) { | |
1026 | next unless defined($component) && $component; | |
1027 | my($abs) = File::Spec->catfile($component,$perl_name); | |
1028 | if (MM->maybe_command($abs)) { | |
1029 | $perl = $abs; | |
1030 | last DIST_PERLNAME; | |
1031 | } | |
1032 | } | |
1033 | } | |
1034 | } | |
1035 | ||
1036 | return $perl; | |
1037 | } | |
1038 | ||
1039 | ||
10b2abe6 | 1040 | #-> sub CPAN::exists ; |
5f05dabc | 1041 | sub exists { |
1042 | my($mgr,$class,$id) = @_; | |
e82b9348 | 1043 | CPAN::HandleConfig->load unless $CPAN::Config_loaded++; |
5f05dabc | 1044 | CPAN::Index->reload; |
e50380aa | 1045 | ### Carp::croak "exists called without class argument" unless $class; |
5f05dabc | 1046 | $id ||= ""; |
e82b9348 | 1047 | $id =~ s/:+/::/g if $class eq "CPAN::Module"; |
810a0276 SP |
1048 | my $exists; |
1049 | if (CPAN::_sqlite_running) { | |
1050 | $exists = (exists $META->{readonly}{$class}{$id} or | |
1051 | $CPAN::SQLite->set($class, $id)); | |
be34b10d | 1052 | } else { |
810a0276 | 1053 | $exists = exists $META->{readonly}{$class}{$id}; |
be34b10d | 1054 | } |
810a0276 | 1055 | $exists ||= exists $META->{readwrite}{$class}{$id}; # unsafe meta access, ok |
5f05dabc | 1056 | } |
1057 | ||
09d9d230 A |
1058 | #-> sub CPAN::delete ; |
1059 | sub delete { | |
1060 | my($mgr,$class,$id) = @_; | |
6d29edf5 JH |
1061 | delete $META->{readonly}{$class}{$id}; # unsafe meta access, ok |
1062 | delete $META->{readwrite}{$class}{$id}; # unsafe meta access, ok | |
09d9d230 A |
1063 | } |
1064 | ||
de34a54b JH |
1065 | #-> sub CPAN::has_usable |
1066 | # has_inst is sometimes too optimistic, we should replace it with this | |
1067 | # has_usable whenever a case is given | |
1068 | sub has_usable { | |
1069 | my($self,$mod,$message) = @_; | |
1070 | return 1 if $HAS_USABLE->{$mod}; | |
1071 | my $has_inst = $self->has_inst($mod,$message); | |
1072 | return unless $has_inst; | |
6d29edf5 JH |
1073 | my $usable; |
1074 | $usable = { | |
1075 | LWP => [ # we frequently had "Can't locate object | |
1076 | # method "new" via package "LWP::UserAgent" at | |
1077 | # (eval 69) line 2006 | |
1078 | sub {require LWP}, | |
1079 | sub {require LWP::UserAgent}, | |
1080 | sub {require HTTP::Request}, | |
1081 | sub {require URI::URL}, | |
1082 | ], | |
ec5fee46 | 1083 | 'Net::FTP' => [ |
6d29edf5 JH |
1084 | sub {require Net::FTP}, |
1085 | sub {require Net::Config}, | |
87892b73 RGS |
1086 | ], |
1087 | 'File::HomeDir' => [ | |
1088 | sub {require File::HomeDir; | |
6658a91b | 1089 | unless (File::HomeDir::->VERSION >= 0.52){ |
87892b73 | 1090 | for ("Will not use File::HomeDir, need 0.52\n") { |
ed84aac9 | 1091 | $CPAN::Frontend->mywarn($_); |
87892b73 RGS |
1092 | die $_; |
1093 | } | |
1094 | } | |
1095 | }, | |
1096 | ], | |
f20de9f0 SP |
1097 | 'Archive::Tar' => [ |
1098 | sub {require Archive::Tar; | |
1099 | unless (Archive::Tar::->VERSION >= 1.00) { | |
1100 | for ("Will not use Archive::Tar, need 1.00\n") { | |
1101 | $CPAN::Frontend->mywarn($_); | |
1102 | die $_; | |
1103 | } | |
1104 | } | |
1105 | }, | |
1106 | ], | |
6d29edf5 JH |
1107 | }; |
1108 | if ($usable->{$mod}) { | |
87892b73 RGS |
1109 | for my $c (0..$#{$usable->{$mod}}) { |
1110 | my $code = $usable->{$mod}[$c]; | |
1111 | my $ret = eval { &$code() }; | |
1112 | $ret = "" unless defined $ret; | |
1113 | if ($@) { | |
1114 | # warn "DEBUG: c[$c]\$\@[$@]ret[$ret]"; | |
1115 | return; | |
1116 | } | |
de34a54b | 1117 | } |
de34a54b JH |
1118 | } |
1119 | return $HAS_USABLE->{$mod} = 1; | |
1120 | } | |
1121 | ||
55e314ee A |
1122 | #-> sub CPAN::has_inst |
1123 | sub has_inst { | |
1124 | my($self,$mod,$message) = @_; | |
1125 | Carp::croak("CPAN->has_inst() called without an argument") | |
1126 | unless defined $mod; | |
4d1321a7 A |
1127 | my %dont = map { $_ => 1 } keys %{$CPAN::META->{dontload_hash}||{}}, |
1128 | keys %{$CPAN::Config->{dontload_hash}||{}}, | |
1129 | @{$CPAN::Config->{dontload_list}||[]}; | |
1130 | if (defined $message && $message eq "no" # afair only used by Nox | |
de34a54b | 1131 | || |
4d1321a7 | 1132 | $dont{$mod} |
de34a54b | 1133 | ) { |
6d29edf5 | 1134 | $CPAN::META->{dontload_hash}{$mod}||=1; # unsafe meta access, ok |
de34a54b | 1135 | return 0; |
55e314ee A |
1136 | } |
1137 | my $file = $mod; | |
c356248b | 1138 | my $obj; |
55e314ee | 1139 | $file =~ s|::|/|g; |
55e314ee | 1140 | $file .= ".pm"; |
c356248b | 1141 | if ($INC{$file}) { |
f14b5cec JH |
1142 | # checking %INC is wrong, because $INC{LWP} may be true |
1143 | # although $INC{"URI/URL.pm"} may have failed. But as | |
1144 | # I really want to say "bla loaded OK", I have to somehow | |
1145 | # cache results. | |
1146 | ### warn "$file in %INC"; #debug | |
55e314ee | 1147 | return 1; |
55e314ee | 1148 | } elsif (eval { require $file }) { |
c356248b A |
1149 | # eval is good: if we haven't yet read the database it's |
1150 | # perfect and if we have installed the module in the meantime, | |
1151 | # it tries again. The second require is only a NOOP returning | |
1152 | # 1 if we had success, otherwise it's retrying | |
f14b5cec | 1153 | |
6a935156 SP |
1154 | my $v = eval "\$$mod\::VERSION"; |
1155 | $v = $v ? " (v$v)" : ""; | |
1156 | $CPAN::Frontend->myprint("CPAN: $mod loaded ok$v\n"); | |
c356248b | 1157 | if ($mod eq "CPAN::WAIT") { |
ec5fee46 | 1158 | push @CPAN::Shell::ISA, 'CPAN::WAIT'; |
c356248b | 1159 | } |
55e314ee A |
1160 | return 1; |
1161 | } elsif ($mod eq "Net::FTP") { | |
6d29edf5 | 1162 | $CPAN::Frontend->mywarn(qq{ |
55e314ee A |
1163 | Please, install Net::FTP as soon as possible. CPAN.pm installs it for you |
1164 | if you just type | |
1165 | install Bundle::libnet | |
5f05dabc | 1166 | |
5a5fac02 | 1167 | }) unless $Have_warned->{"Net::FTP"}++; |
8962fc49 | 1168 | $CPAN::Frontend->mysleep(3); |
e82b9348 | 1169 | } elsif ($mod eq "Digest::SHA"){ |
4d1321a7 | 1170 | if ($Have_warned->{"Digest::SHA"}++) { |
f20de9f0 | 1171 | $CPAN::Frontend->myprint(qq{CPAN: checksum security checks disabled }. |
4d1321a7 A |
1172 | qq{because Digest::SHA not installed.\n}); |
1173 | } else { | |
8962fc49 | 1174 | $CPAN::Frontend->mywarn(qq{ |
e82b9348 SP |
1175 | CPAN: checksum security checks disabled because Digest::SHA not installed. |
1176 | Please consider installing the Digest::SHA module. | |
c356248b A |
1177 | |
1178 | }); | |
8962fc49 | 1179 | $CPAN::Frontend->mysleep(2); |
4d1321a7 | 1180 | } |
554a9ef5 | 1181 | } elsif ($mod eq "Module::Signature"){ |
be34b10d SP |
1182 | # NOT prefs_lookup, we are not a distro |
1183 | my $check_sigs = $CPAN::Config->{check_sigs}; | |
1184 | if (not $check_sigs) { | |
ed84aac9 A |
1185 | # they do not want us:-( |
1186 | } elsif (not $Have_warned->{"Module::Signature"}++) { | |
554a9ef5 SP |
1187 | # No point in complaining unless the user can |
1188 | # reasonably install and use it. | |
1189 | if (eval { require Crypt::OpenPGP; 1 } || | |
ed84aac9 A |
1190 | ( |
1191 | defined $CPAN::Config->{'gpg'} | |
1192 | && | |
1193 | $CPAN::Config->{'gpg'} =~ /\S/ | |
1194 | ) | |
1195 | ) { | |
8962fc49 | 1196 | $CPAN::Frontend->mywarn(qq{ |
554a9ef5 SP |
1197 | CPAN: Module::Signature security checks disabled because Module::Signature |
1198 | not installed. Please consider installing the Module::Signature module. | |
1199 | You may also need to be able to connect over the Internet to the public | |
1200 | keyservers like pgp.mit.edu (port 11371). | |
1201 | ||
1202 | }); | |
8962fc49 | 1203 | $CPAN::Frontend->mysleep(2); |
554a9ef5 SP |
1204 | } |
1205 | } | |
f14b5cec JH |
1206 | } else { |
1207 | delete $INC{$file}; # if it inc'd LWP but failed during, say, URI | |
05454584 | 1208 | } |
55e314ee | 1209 | return 0; |
05454584 A |
1210 | } |
1211 | ||
10b2abe6 | 1212 | #-> sub CPAN::instance ; |
5f05dabc | 1213 | sub instance { |
1214 | my($mgr,$class,$id) = @_; | |
1215 | CPAN::Index->reload; | |
5f05dabc | 1216 | $id ||= ""; |
6d29edf5 JH |
1217 | # unsafe meta access, ok? |
1218 | return $META->{readwrite}{$class}{$id} if exists $META->{readwrite}{$class}{$id}; | |
1219 | $META->{readwrite}{$class}{$id} ||= $class->new(ID => $id); | |
5f05dabc | 1220 | } |
1221 | ||
10b2abe6 | 1222 | #-> sub CPAN::new ; |
5f05dabc | 1223 | sub new { |
1224 | bless {}, shift; | |
1225 | } | |
1226 | ||
10b2abe6 | 1227 | #-> sub CPAN::cleanup ; |
5f05dabc | 1228 | sub cleanup { |
e82b9348 | 1229 | # warn "cleanup called with arg[@_] End[$CPAN::End] Signal[$Signal]"; |
2e2b7522 GS |
1230 | local $SIG{__DIE__} = ''; |
1231 | my($message) = @_; | |
1232 | my $i = 0; | |
1233 | my $ineval = 0; | |
5fc0f0f6 JH |
1234 | my($subroutine); |
1235 | while ((undef,undef,undef,$subroutine) = caller(++$i)) { | |
2e2b7522 GS |
1236 | $ineval = 1, last if |
1237 | $subroutine eq '(eval)'; | |
2e2b7522 | 1238 | } |
e82b9348 | 1239 | return if $ineval && !$CPAN::End; |
5fc0f0f6 JH |
1240 | return unless defined $META->{LOCK}; |
1241 | return unless -f $META->{LOCK}; | |
1242 | $META->savehist; | |
b72dd56f | 1243 | close $META->{LOCKFH}; |
5fc0f0f6 | 1244 | unlink $META->{LOCK}; |
2e2b7522 GS |
1245 | # require Carp; |
1246 | # Carp::cluck("DEBUGGING"); | |
6658a91b SP |
1247 | if ( $CPAN::CONFIG_DIRTY ) { |
1248 | $CPAN::Frontend->mywarn("Warning: Configuration not saved.\n"); | |
1249 | } | |
8962fc49 | 1250 | $CPAN::Frontend->myprint("Lockfile removed.\n"); |
5f05dabc | 1251 | } |
1252 | ||
f20de9f0 SP |
1253 | #-> sub CPAN::readhist |
1254 | sub readhist { | |
1255 | my($self,$term,$histfile) = @_; | |
1256 | my($fh) = FileHandle->new; | |
1257 | open $fh, "<$histfile" or last; | |
1258 | local $/ = "\n"; | |
1259 | while (<$fh>) { | |
1260 | chomp; | |
1261 | $term->AddHistory($_); | |
1262 | } | |
1263 | close $fh; | |
1264 | } | |
1265 | ||
5fc0f0f6 JH |
1266 | #-> sub CPAN::savehist |
1267 | sub savehist { | |
1268 | my($self) = @_; | |
1269 | my($histfile,$histsize); | |
1270 | unless ($histfile = $CPAN::Config->{'histfile'}){ | |
1271 | $CPAN::Frontend->mywarn("No history written (no histfile specified).\n"); | |
1272 | return; | |
1273 | } | |
1274 | $histsize = $CPAN::Config->{'histsize'} || 100; | |
35576f8c A |
1275 | if ($CPAN::term){ |
1276 | unless ($CPAN::term->can("GetHistory")) { | |
1277 | $CPAN::Frontend->mywarn("Terminal does not support GetHistory.\n"); | |
1278 | return; | |
1279 | } | |
1280 | } else { | |
5fc0f0f6 JH |
1281 | return; |
1282 | } | |
1283 | my @h = $CPAN::term->GetHistory; | |
1284 | splice @h, 0, @h-$histsize if @h>$histsize; | |
1285 | my($fh) = FileHandle->new; | |
35576f8c | 1286 | open $fh, ">$histfile" or $CPAN::Frontend->mydie("Couldn't open >$histfile: $!"); |
5fc0f0f6 JH |
1287 | local $\ = local $, = "\n"; |
1288 | print $fh @h; | |
1289 | close $fh; | |
1290 | } | |
1291 | ||
6658a91b | 1292 | #-> sub CPAN::is_tested |
4c070e31 | 1293 | sub is_tested { |
b72dd56f SP |
1294 | my($self,$what,$when) = @_; |
1295 | unless ($what) { | |
1296 | Carp::cluck("DEBUG: empty what"); | |
1297 | return; | |
1298 | } | |
1299 | $self->{is_tested}{$what} = $when; | |
4c070e31 IZ |
1300 | } |
1301 | ||
6658a91b | 1302 | #-> sub CPAN::is_installed |
135a59c2 A |
1303 | # unsets the is_tested flag: as soon as the thing is installed, it is |
1304 | # not needed in set_perl5lib anymore | |
4c070e31 IZ |
1305 | sub is_installed { |
1306 | my($self,$what) = @_; | |
1307 | delete $self->{is_tested}{$what}; | |
1308 | } | |
1309 | ||
b72dd56f SP |
1310 | sub _list_sorted_descending_is_tested { |
1311 | my($self) = @_; | |
1312 | sort | |
1313 | { ($self->{is_tested}{$b}||0) <=> ($self->{is_tested}{$a}||0) } | |
1314 | keys %{$self->{is_tested}} | |
1315 | } | |
1316 | ||
6658a91b | 1317 | #-> sub CPAN::set_perl5lib |
4c070e31 | 1318 | sub set_perl5lib { |
6658a91b SP |
1319 | my($self,$for) = @_; |
1320 | unless ($for) { | |
1321 | (undef,undef,undef,$for) = caller(1); | |
1322 | $for =~ s/.*://; | |
1323 | } | |
0362b508 | 1324 | $self->{is_tested} ||= {}; |
4c070e31 IZ |
1325 | return unless %{$self->{is_tested}}; |
1326 | my $env = $ENV{PERL5LIB}; | |
1327 | $env = $ENV{PERLLIB} unless defined $env; | |
1328 | my @env; | |
1329 | push @env, $env if defined $env and length $env; | |
6658a91b SP |
1330 | #my @dirs = map {("$_/blib/arch", "$_/blib/lib")} keys %{$self->{is_tested}}; |
1331 | #$CPAN::Frontend->myprint("Prepending @dirs to PERL5LIB.\n"); | |
b72dd56f SP |
1332 | |
1333 | my @dirs = map {("$_/blib/arch", "$_/blib/lib")} $self->_list_sorted_descending_is_tested; | |
1334 | if (@dirs < 12) { | |
1335 | $CPAN::Frontend->myprint("Prepending @dirs to PERL5LIB for '$for'\n"); | |
1336 | } elsif (@dirs < 24) { | |
1337 | my @d = map {my $cp = $_; | |
1338 | $cp =~ s/^\Q$CPAN::Config->{build_dir}\E/%BUILDDIR%/; | |
1339 | $cp | |
1340 | } @dirs; | |
1341 | $CPAN::Frontend->myprint("Prepending @d to PERL5LIB; ". | |
1342 | "%BUILDDIR%=$CPAN::Config->{build_dir} ". | |
1343 | "for '$for'\n" | |
1344 | ); | |
6658a91b | 1345 | } else { |
b72dd56f SP |
1346 | my $cnt = keys %{$self->{is_tested}}; |
1347 | $CPAN::Frontend->myprint("Prepending blib/arch and blib/lib of ". | |
1348 | "$cnt build dirs to PERL5LIB; ". | |
1349 | "for '$for'\n" | |
6658a91b SP |
1350 | ); |
1351 | } | |
1352 | ||
4c070e31 IZ |
1353 | $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env; |
1354 | } | |
1355 | ||
05454584 | 1356 | package CPAN::CacheMgr; |
e82b9348 | 1357 | use strict; |
5f05dabc | 1358 | |
05454584 A |
1359 | #-> sub CPAN::CacheMgr::as_string ; |
1360 | sub as_string { | |
1361 | eval { require Data::Dumper }; | |
1362 | if ($@) { | |
1363 | return shift->SUPER::as_string; | |
5f05dabc | 1364 | } else { |
05454584 | 1365 | return Data::Dumper::Dumper(shift); |
5f05dabc | 1366 | } |
1367 | } | |
1368 | ||
05454584 A |
1369 | #-> sub CPAN::CacheMgr::cachesize ; |
1370 | sub cachesize { | |
1371 | shift->{DU}; | |
5f05dabc | 1372 | } |
5f05dabc | 1373 | |
c4d24d4c | 1374 | #-> sub CPAN::CacheMgr::tidyup ; |
09d9d230 A |
1375 | sub tidyup { |
1376 | my($self) = @_; | |
be34b10d | 1377 | return unless $CPAN::META->{LOCK}; |
09d9d230 A |
1378 | return unless -d $self->{ID}; |
1379 | while ($self->{DU} > $self->{'MAX'} ) { | |
1380 | my($toremove) = shift @{$self->{FIFO}}; | |
810a0276 SP |
1381 | unless ($toremove =~ /\.yml$/) { |
1382 | $CPAN::Frontend->myprint(sprintf( | |
f20de9f0 SP |
1383 | "DEL(%.1f>%.1fMB): %s \n", |
1384 | $self->{DU}, | |
1385 | $self->{MAX}, | |
1386 | $toremove, | |
1387 | ) | |
810a0276 SP |
1388 | ); |
1389 | } | |
09d9d230 | 1390 | return if $CPAN::Signal; |
810a0276 | 1391 | $self->_clean_cache($toremove); |
09d9d230 A |
1392 | return if $CPAN::Signal; |
1393 | } | |
1394 | } | |
5f05dabc | 1395 | |
05454584 A |
1396 | #-> sub CPAN::CacheMgr::dir ; |
1397 | sub dir { | |
1398 | shift->{ID}; | |
1399 | } | |
1400 | ||
1401 | #-> sub CPAN::CacheMgr::entries ; | |
1402 | sub entries { | |
1403 | my($self,$dir) = @_; | |
55e314ee | 1404 | return unless defined $dir; |
e50380aa | 1405 | $self->debug("reading dir[$dir]") if $CPAN::DEBUG; |
05454584 | 1406 | $dir ||= $self->{ID}; |
9d61fa1d | 1407 | my($cwd) = CPAN::anycwd(); |
05454584 | 1408 | chdir $dir or Carp::croak("Can't chdir to $dir: $!"); |
f14b5cec JH |
1409 | my $dh = DirHandle->new(File::Spec->curdir) |
1410 | or Carp::croak("Couldn't opendir $dir: $!"); | |
05454584 A |
1411 | my(@entries); |
1412 | for ($dh->read) { | |
1413 | next if $_ eq "." || $_ eq ".."; | |
1414 | if (-f $_) { | |
5de3f0da | 1415 | push @entries, File::Spec->catfile($dir,$_); |
05454584 | 1416 | } elsif (-d _) { |
5de3f0da | 1417 | push @entries, File::Spec->catdir($dir,$_); |
5f05dabc | 1418 | } else { |
c356248b | 1419 | $CPAN::Frontend->mywarn("Warning: weird direntry in $dir: $_\n"); |
5f05dabc | 1420 | } |
5f05dabc | 1421 | } |
05454584 | 1422 | chdir $cwd or Carp::croak("Can't chdir to $cwd: $!"); |
e50380aa | 1423 | sort { -M $b <=> -M $a} @entries; |
5f05dabc | 1424 | } |
1425 | ||
05454584 A |
1426 | #-> sub CPAN::CacheMgr::disk_usage ; |
1427 | sub disk_usage { | |
1428 | my($self,$dir) = @_; | |
09d9d230 A |
1429 | return if exists $self->{SIZE}{$dir}; |
1430 | return if $CPAN::Signal; | |
1431 | my($Du) = 0; | |
c9869e1c | 1432 | if (-e $dir) { |
2b3bde2a SP |
1433 | if (-d $dir) { |
1434 | unless (-x $dir) { | |
1435 | unless (chmod 0755, $dir) { | |
1436 | $CPAN::Frontend->mywarn("I have neither the -x permission nor the ". | |
1437 | "permission to change the permission; cannot ". | |
1438 | "estimate disk usage of '$dir'\n"); | |
1439 | $CPAN::Frontend->mysleep(5); | |
1440 | return; | |
1441 | } | |
c9869e1c | 1442 | } |
2b3bde2a SP |
1443 | } elsif (-f $dir) { |
1444 | # nothing to say, no matter what the permissions | |
c9869e1c SP |
1445 | } |
1446 | } else { | |
2b3bde2a | 1447 | $CPAN::Frontend->mywarn("File or directory '$dir' has gone, ignoring\n"); |
0cf35e6a | 1448 | return; |
0cf35e6a | 1449 | } |
05454584 | 1450 | find( |
0cf35e6a SP |
1451 | sub { |
1452 | $File::Find::prune++ if $CPAN::Signal; | |
1453 | return if -l $_; | |
1454 | if ($^O eq 'MacOS') { | |
1455 | require Mac::Files; | |
1456 | my $cat = Mac::Files::FSpGetCatInfo($_); | |
1457 | $Du += $cat->ioFlLgLen() + $cat->ioFlRLgLen() if $cat; | |
1458 | } else { | |
1459 | if (-d _) { | |
1460 | unless (-x _) { | |
1461 | unless (chmod 0755, $_) { | |
1462 | $CPAN::Frontend->mywarn("I have neither the -x permission nor ". | |
1463 | "the permission to change the permission; ". | |
1464 | "can only partially estimate disk usage ". | |
1465 | "of '$_'\n"); | |
8962fc49 | 1466 | $CPAN::Frontend->mysleep(5); |
0cf35e6a SP |
1467 | return; |
1468 | } | |
1469 | } | |
1470 | } else { | |
1471 | $Du += (-s _); | |
1472 | } | |
1473 | } | |
1474 | }, | |
1475 | $dir | |
1476 | ); | |
09d9d230 | 1477 | return if $CPAN::Signal; |
05454584 A |
1478 | $self->{SIZE}{$dir} = $Du/1024/1024; |
1479 | push @{$self->{FIFO}}, $dir; | |
1480 | $self->debug("measured $dir is $Du") if $CPAN::DEBUG; | |
1481 | $self->{DU} += $Du/1024/1024; | |
05454584 | 1482 | $self->{DU}; |
5f05dabc | 1483 | } |
1484 | ||
810a0276 SP |
1485 | #-> sub CPAN::CacheMgr::_clean_cache ; |
1486 | sub _clean_cache { | |
05454584 | 1487 | my($self,$dir) = @_; |
09d9d230 | 1488 | return unless -e $dir; |
810a0276 SP |
1489 | unless (File::Spec->canonpath(File::Basename::dirname($dir)) |
1490 | eq File::Spec->canonpath($CPAN::Config->{build_dir})) { | |
be34b10d SP |
1491 | $CPAN::Frontend->mywarn("Directory '$dir' not below $CPAN::Config->{build_dir}, ". |
1492 | "will not remove\n"); | |
1493 | $CPAN::Frontend->mysleep(5); | |
1494 | return; | |
1495 | } | |
05454584 A |
1496 | $self->debug("have to rmtree $dir, will free $self->{SIZE}{$dir}") |
1497 | if $CPAN::DEBUG; | |
1498 | File::Path::rmtree($dir); | |
f20de9f0 SP |
1499 | my $id_deleted = 0; |
1500 | if ($dir !~ /\.yml$/ && -f "$dir.yml") { | |
1501 | my $yaml_module = CPAN::_yaml_module; | |
1502 | if ($CPAN::META->has_inst($yaml_module)) { | |
1503 | my($peek_yaml) = CPAN->_yaml_loadfile("$dir.yml"); | |
1504 | if (my $id = $peek_yaml->[0]{distribution}{ID}) { | |
1505 | $CPAN::META->delete("CPAN::Distribution", $id); | |
1506 | # $CPAN::Frontend->mywarn (" +++\n"); | |
1507 | $id_deleted++; | |
1508 | } | |
1509 | } | |
1510 | unlink "$dir.yml"; # may fail | |
1511 | unless ($id_deleted) { | |
1512 | CPAN->debug("no distro found associated with '$dir'"); | |
1513 | } | |
1514 | } | |
05454584 A |
1515 | $self->{DU} -= $self->{SIZE}{$dir}; |
1516 | delete $self->{SIZE}{$dir}; | |
5f05dabc | 1517 | } |
1518 | ||
05454584 A |
1519 | #-> sub CPAN::CacheMgr::new ; |
1520 | sub new { | |
1521 | my $class = shift; | |
e50380aa A |
1522 | my $time = time; |
1523 | my($debug,$t2); | |
1524 | $debug = ""; | |
05454584 | 1525 | my $self = { |
b72dd56f | 1526 | ID => $CPAN::Config->{build_dir}, |
05454584 | 1527 | MAX => $CPAN::Config->{'build_cache'}, |
f610777f | 1528 | SCAN => $CPAN::Config->{'scan_cache'} || 'atstart', |
05454584 A |
1529 | DU => 0 |
1530 | }; | |
1531 | File::Path::mkpath($self->{ID}); | |
1532 | my $dh = DirHandle->new($self->{ID}); | |
1533 | bless $self, $class; | |
f610777f A |
1534 | $self->scan_cache; |
1535 | $t2 = time; | |
1536 | $debug .= "timing of CacheMgr->new: ".($t2 - $time); | |
1537 | $time = $t2; | |
1538 | CPAN->debug($debug) if $CPAN::DEBUG; | |
1539 | $self; | |
1540 | } | |
1541 | ||
1542 | #-> sub CPAN::CacheMgr::scan_cache ; | |
1543 | sub scan_cache { | |
1544 | my $self = shift; | |
1545 | return if $self->{SCAN} eq 'never'; | |
1546 | $CPAN::Frontend->mydie("Unknown scan_cache argument: $self->{SCAN}") | |
1547 | unless $self->{SCAN} eq 'atstart'; | |
f20de9f0 | 1548 | return unless $CPAN::META->{LOCK}; |
09d9d230 A |
1549 | $CPAN::Frontend->myprint( |
1550 | sprintf("Scanning cache %s for sizes\n", | |
1551 | $self->{ID})); | |
f610777f | 1552 | my $e; |
b72dd56f SP |
1553 | my @entries = grep { !/^\.\.?$/ } $self->entries($self->{ID}); |
1554 | my $i = 0; | |
1555 | my $painted = 0; | |
1556 | for $e (@entries) { | |
1557 | # next if $e eq ".." || $e eq "."; | |
05454584 | 1558 | $self->disk_usage($e); |
b72dd56f SP |
1559 | $i++; |
1560 | while (($painted/76) < ($i/@entries)) { | |
1561 | $CPAN::Frontend->myprint("."); | |
1562 | $painted++; | |
1563 | } | |
09d9d230 | 1564 | return if $CPAN::Signal; |
5f05dabc | 1565 | } |
b72dd56f | 1566 | $CPAN::Frontend->myprint("DONE\n"); |
09d9d230 | 1567 | $self->tidyup; |
5f05dabc | 1568 | } |
1569 | ||
05454584 | 1570 | package CPAN::Shell; |
e82b9348 | 1571 | use strict; |
5f05dabc | 1572 | |
05454584 A |
1573 | #-> sub CPAN::Shell::h ; |
1574 | sub h { | |
1575 | my($class,$about) = @_; | |
1576 | if (defined $about) { | |
c356248b | 1577 | $CPAN::Frontend->myprint("Detailed help not yet implemented\n"); |
05454584 | 1578 | } else { |
9ddc4ed0 A |
1579 | my $filler = " " x (80 - 28 - length($CPAN::VERSION)); |
1580 | $CPAN::Frontend->myprint(qq{ | |
1581 | Display Information $filler (ver $CPAN::VERSION) | |
c049f953 JH |
1582 | command argument description |
1583 | a,b,d,m WORD or /REGEXP/ about authors, bundles, distributions, modules | |
6a94b120 | 1584 | i WORD or /REGEXP/ about any of the above |
0cf35e6a | 1585 | ls AUTHOR or GLOB about files in the author's directory |
ec5fee46 A |
1586 | (with WORD being a module, bundle or author name or a distribution |
1587 | name of the form AUTHOR/DISTRIBUTION) | |
911a92db GS |
1588 | |
1589 | Download, Test, Make, Install... | |
ec5fee46 A |
1590 | get download clean make clean |
1591 | make make (implies get) look open subshell in dist directory | |
1592 | test make test (implies make) readme display these README files | |
1593 | install make install (implies test) perldoc display POD documentation | |
1594 | ||
135a59c2 A |
1595 | Upgrade |
1596 | r WORDs or /REGEXP/ or NONE report updates for some/matching/all modules | |
1597 | upgrade WORDs or /REGEXP/ or NONE upgrade some/matching/all modules | |
1598 | ||
ec5fee46 | 1599 | Pragmas |
b72dd56f | 1600 | force CMD try hard to do command fforce CMD try harder |
810a0276 | 1601 | notest CMD skip testing |
911a92db GS |
1602 | |
1603 | Other | |
1604 | h,? display this menu ! perl-code eval a perl command | |
1605 | o conf [opt] set and query options q quit the cpan shell | |
1606 | reload cpan load CPAN.pm again reload index load newer indices | |
ec5fee46 | 1607 | autobundle Snapshot recent latest CPAN uploads}); |
135a59c2 | 1608 | } |
05454584 | 1609 | } |
da199366 | 1610 | |
09d9d230 A |
1611 | *help = \&h; |
1612 | ||
05454584 | 1613 | #-> sub CPAN::Shell::a ; |
de34a54b JH |
1614 | sub a { |
1615 | my($self,@arg) = @_; | |
1616 | # authors are always UPPERCASE | |
1617 | for (@arg) { | |
c049f953 | 1618 | $_ = uc $_ unless /=/; |
de34a54b JH |
1619 | } |
1620 | $CPAN::Frontend->myprint($self->format_result('Author',@arg)); | |
1621 | } | |
6d29edf5 | 1622 | |
ca79d794 SP |
1623 | #-> sub CPAN::Shell::globls ; |
1624 | sub globls { | |
1625 | my($self,$s,$pragmas) = @_; | |
0cf35e6a SP |
1626 | # ls is really very different, but we had it once as an ordinary |
1627 | # command in the Shell (upto rev. 321) and we could not handle | |
1628 | # force well then | |
e82b9348 | 1629 | my(@accept,@preexpand); |
0cf35e6a SP |
1630 | if ($s =~ /[\*\?\/]/) { |
1631 | if ($CPAN::META->has_inst("Text::Glob")) { | |
1632 | if (my($au,$pathglob) = $s =~ m|(.*?)/(.*)|) { | |
1633 | my $rau = Text::Glob::glob_to_regex(uc $au); | |
1634 | CPAN::Shell->debug("au[$au]pathglob[$pathglob]rau[$rau]") | |
1635 | if $CPAN::DEBUG; | |
1636 | push @preexpand, map { $_->id . "/" . $pathglob } | |
1637 | CPAN::Shell->expand_by_method('CPAN::Author',['id'],"/$rau/"); | |
e82b9348 | 1638 | } else { |
0cf35e6a SP |
1639 | my $rau = Text::Glob::glob_to_regex(uc $s); |
1640 | push @preexpand, map { $_->id } | |
1641 | CPAN::Shell->expand_by_method('CPAN::Author', | |
1642 | ['id'], | |
1643 | "/$rau/"); | |
e82b9348 SP |
1644 | } |
1645 | } else { | |
0cf35e6a | 1646 | $CPAN::Frontend->mydie("Text::Glob not installed, cannot proceed"); |
e82b9348 | 1647 | } |
0cf35e6a SP |
1648 | } else { |
1649 | push @preexpand, uc $s; | |
554a9ef5 | 1650 | } |
e82b9348 SP |
1651 | for (@preexpand) { |
1652 | unless (/^[A-Z0-9\-]+(\/|$)/i) { | |
5fc0f0f6 | 1653 | $CPAN::Frontend->mywarn("ls command rejects argument $_: not an author\n"); |
c049f953 JH |
1654 | next; |
1655 | } | |
e82b9348 | 1656 | push @accept, $_; |
8d97e4a1 | 1657 | } |
554a9ef5 SP |
1658 | my $silent = @accept>1; |
1659 | my $last_alpha = ""; | |
ca79d794 | 1660 | my @results; |
c049f953 | 1661 | for my $a (@accept){ |
e82b9348 SP |
1662 | my($author,$pathglob); |
1663 | if ($a =~ m|(.*?)/(.*)|) { | |
1664 | my $a2 = $1; | |
1665 | $pathglob = $2; | |
0cf35e6a SP |
1666 | $author = CPAN::Shell->expand_by_method('CPAN::Author', |
1667 | ['id'], | |
b72dd56f SP |
1668 | $a2) |
1669 | or $CPAN::Frontend->mydie("No author found for $a2\n"); | |
e82b9348 | 1670 | } else { |
0cf35e6a SP |
1671 | $author = CPAN::Shell->expand_by_method('CPAN::Author', |
1672 | ['id'], | |
b72dd56f SP |
1673 | $a) |
1674 | or $CPAN::Frontend->mydie("No author found for $a\n"); | |
e82b9348 | 1675 | } |
554a9ef5 | 1676 | if ($silent) { |
e82b9348 | 1677 | my $alpha = substr $author->id, 0, 1; |
554a9ef5 | 1678 | my $ad; |
e82b9348 SP |
1679 | if ($alpha eq $last_alpha) { |
1680 | $ad = ""; | |
554a9ef5 | 1681 | } else { |
e82b9348 SP |
1682 | $ad = "[$alpha]"; |
1683 | $last_alpha = $alpha; | |
554a9ef5 SP |
1684 | } |
1685 | $CPAN::Frontend->myprint($ad); | |
1686 | } | |
9ddc4ed0 A |
1687 | for my $pragma (@$pragmas) { |
1688 | if ($author->can($pragma)) { | |
1689 | $author->$pragma(); | |
1690 | } | |
1691 | } | |
ca79d794 SP |
1692 | push @results, $author->ls($pathglob,$silent); # silent if |
1693 | # more than one | |
1694 | # author | |
9ddc4ed0 | 1695 | for my $pragma (@$pragmas) { |
05bab18e SP |
1696 | my $unpragma = "un$pragma"; |
1697 | if ($author->can($unpragma)) { | |
1698 | $author->$unpragma(); | |
9ddc4ed0 A |
1699 | } |
1700 | } | |
8d97e4a1 | 1701 | } |
ca79d794 | 1702 | @results; |
8d97e4a1 | 1703 | } |
6d29edf5 | 1704 | |
8d97e4a1 | 1705 | #-> sub CPAN::Shell::local_bundles ; |
6d29edf5 | 1706 | sub local_bundles { |
05454584 | 1707 | my($self,@which) = @_; |
55e314ee | 1708 | my($incdir,$bdir,$dh); |
05454584 | 1709 | foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) { |
8d97e4a1 JH |
1710 | my @bbase = "Bundle"; |
1711 | while (my $bbase = shift @bbase) { | |
5de3f0da | 1712 | $bdir = File::Spec->catdir($incdir,split /::/, $bbase); |
8d97e4a1 JH |
1713 | CPAN->debug("bdir[$bdir]\@bbase[@bbase]") if $CPAN::DEBUG; |
1714 | if ($dh = DirHandle->new($bdir)) { # may fail | |
1715 | my($entry); | |
1716 | for $entry ($dh->read) { | |
c049f953 | 1717 | next if $entry =~ /^\./; |
b96578bb | 1718 | next unless $entry =~ /^\w+(\.pm)?(?!\n)\Z/; |
5de3f0da | 1719 | if (-d File::Spec->catdir($bdir,$entry)){ |
8d97e4a1 JH |
1720 | push @bbase, "$bbase\::$entry"; |
1721 | } else { | |
1722 | next unless $entry =~ s/\.pm(?!\n)\Z//; | |
1723 | $CPAN::META->instance('CPAN::Bundle',"$bbase\::$entry"); | |
1724 | } | |
1725 | } | |
1726 | } | |
1727 | } | |
05454584 | 1728 | } |
6d29edf5 JH |
1729 | } |
1730 | ||
1731 | #-> sub CPAN::Shell::b ; | |
1732 | sub b { | |
1733 | my($self,@which) = @_; | |
1734 | CPAN->debug("which[@which]") if $CPAN::DEBUG; | |
1735 | $self->local_bundles; | |
c356248b | 1736 | $CPAN::Frontend->myprint($self->format_result('Bundle',@which)); |
05454584 | 1737 | } |
6d29edf5 | 1738 | |
05454584 | 1739 | #-> sub CPAN::Shell::d ; |
c356248b | 1740 | sub d { $CPAN::Frontend->myprint(shift->format_result('Distribution',@_));} |
6d29edf5 | 1741 | |
05454584 | 1742 | #-> sub CPAN::Shell::m ; |
f610777f | 1743 | sub m { # emacs confused here }; sub mimimimimi { # emacs in sync here |
35576f8c A |
1744 | my $self = shift; |
1745 | $CPAN::Frontend->myprint($self->format_result('Module',@_)); | |
f610777f | 1746 | } |
da199366 | 1747 | |
05454584 A |
1748 | #-> sub CPAN::Shell::i ; |
1749 | sub i { | |
1750 | my($self) = shift; | |
1751 | my(@args) = @_; | |
05454584 A |
1752 | @args = '/./' unless @args; |
1753 | my(@result); | |
190aa835 | 1754 | for my $type (qw/Bundle Distribution Module/) { |
05454584 A |
1755 | push @result, $self->expand($type,@args); |
1756 | } | |
190aa835 MS |
1757 | # Authors are always uppercase. |
1758 | push @result, $self->expand("Author", map { uc $_ } @args); | |
1759 | ||
8d97e4a1 | 1760 | my $result = @result == 1 ? |
05454584 | 1761 | $result[0]->as_string : |
8d97e4a1 JH |
1762 | @result == 0 ? |
1763 | "No objects found of any type for argument @args\n" : | |
1764 | join("", | |
1765 | (map {$_->as_glimpse} @result), | |
1766 | scalar @result, " items found\n", | |
1767 | ); | |
c356248b | 1768 | $CPAN::Frontend->myprint($result); |
da199366 | 1769 | } |
da199366 | 1770 | |
05454584 | 1771 | #-> sub CPAN::Shell::o ; |
5e05dca5 | 1772 | |
8962fc49 SP |
1773 | # CPAN::Shell::o and CPAN::HandleConfig::edit are closely related. 'o |
1774 | # conf' calls through to CPAN::HandleConfig::edit. 'o conf' should | |
135a59c2 A |
1775 | # probably have been called 'set' and 'o debug' maybe 'set debug' or |
1776 | # 'debug'; 'o conf ARGS' calls ->edit in CPAN/HandleConfig.pm | |
05454584 A |
1777 | sub o { |
1778 | my($self,$o_type,@o_what) = @_; | |
1779 | $o_type ||= ""; | |
1780 | CPAN->debug("o_type[$o_type] o_what[".join(" | ",@o_what)."]\n"); | |
1781 | if ($o_type eq 'conf') { | |
5e05dca5 | 1782 | if (!@o_what) { # print all things, "o conf" |
05454584 | 1783 | my($k,$v); |
ed84aac9 A |
1784 | $CPAN::Frontend->myprint("\$CPAN::Config options from "); |
1785 | my @from; | |
09d9d230 | 1786 | if (exists $INC{'CPAN/Config.pm'}) { |
ed84aac9 | 1787 | push @from, $INC{'CPAN/Config.pm'}; |
09d9d230 A |
1788 | } |
1789 | if (exists $INC{'CPAN/MyConfig.pm'}) { | |
ed84aac9 | 1790 | push @from, $INC{'CPAN/MyConfig.pm'}; |
09d9d230 | 1791 | } |
ed84aac9 | 1792 | $CPAN::Frontend->myprint(join " and ", map {"'$_'"} @from); |
09d9d230 | 1793 | $CPAN::Frontend->myprint(":\n"); |
e82b9348 SP |
1794 | for $k (sort keys %CPAN::HandleConfig::can) { |
1795 | $v = $CPAN::HandleConfig::can{$k}; | |
554a9ef5 | 1796 | $CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v); |
05454584 | 1797 | } |
c356248b | 1798 | $CPAN::Frontend->myprint("\n"); |
05454584 | 1799 | for $k (sort keys %$CPAN::Config) { |
e82b9348 | 1800 | CPAN::HandleConfig->prettyprint($k); |
10b2abe6 | 1801 | } |
c356248b | 1802 | $CPAN::Frontend->myprint("\n"); |
f20de9f0 | 1803 | } else { |
05bab18e | 1804 | if (CPAN::HandleConfig->edit(@o_what)) { |
05bab18e SP |
1805 | } else { |
1806 | $CPAN::Frontend->myprint(qq{Type 'o conf' to view all configuration }. | |
1807 | qq{items\n\n}); | |
1808 | } | |
5f05dabc | 1809 | } |
05454584 A |
1810 | } elsif ($o_type eq 'debug') { |
1811 | my(%valid); | |
1812 | @o_what = () if defined $o_what[0] && $o_what[0] =~ /help/i; | |
1813 | if (@o_what) { | |
1814 | while (@o_what) { | |
1815 | my($what) = shift @o_what; | |
8d97e4a1 JH |
1816 | if ($what =~ s/^-// && exists $CPAN::DEBUG{$what}) { |
1817 | $CPAN::DEBUG &= $CPAN::DEBUG ^ $CPAN::DEBUG{$what}; | |
1818 | next; | |
1819 | } | |
05454584 A |
1820 | if ( exists $CPAN::DEBUG{$what} ) { |
1821 | $CPAN::DEBUG |= $CPAN::DEBUG{$what}; | |
1822 | } elsif ($what =~ /^\d/) { | |
1823 | $CPAN::DEBUG = $what; | |
1824 | } elsif (lc $what eq 'all') { | |
1825 | my($max) = 0; | |
1826 | for (values %CPAN::DEBUG) { | |
1827 | $max += $_; | |
10b2abe6 | 1828 | } |
05454584 | 1829 | $CPAN::DEBUG = $max; |
10b2abe6 | 1830 | } else { |
d4fd5c69 | 1831 | my($known) = 0; |
05454584 A |
1832 | for (keys %CPAN::DEBUG) { |
1833 | next unless lc($_) eq lc($what); | |
1834 | $CPAN::DEBUG |= $CPAN::DEBUG{$_}; | |
d4fd5c69 | 1835 | $known = 1; |
10b2abe6 | 1836 | } |
c356248b A |
1837 | $CPAN::Frontend->myprint("unknown argument [$what]\n") |
1838 | unless $known; | |
10b2abe6 CS |
1839 | } |
1840 | } | |
05454584 | 1841 | } else { |
911a92db GS |
1842 | my $raw = "Valid options for debug are ". |
1843 | join(", ",sort(keys %CPAN::DEBUG), 'all'). | |
1844 | qq{ or a number. Completion works on the options. }. | |
1845 | qq{Case is ignored.}; | |
1846 | require Text::Wrap; | |
1847 | $CPAN::Frontend->myprint(Text::Wrap::fill("","",$raw)); | |
1848 | $CPAN::Frontend->myprint("\n\n"); | |
05454584 A |
1849 | } |
1850 | if ($CPAN::DEBUG) { | |
7d97ad34 | 1851 | $CPAN::Frontend->myprint("Options set for debugging ($CPAN::DEBUG):\n"); |
05454584 A |
1852 | my($k,$v); |
1853 | for $k (sort {$CPAN::DEBUG{$a} <=> $CPAN::DEBUG{$b}} keys %CPAN::DEBUG) { | |
1854 | $v = $CPAN::DEBUG{$k}; | |
05d2a450 A |
1855 | $CPAN::Frontend->myprint(sprintf " %-14s(%s)\n", $k, $v) |
1856 | if $v & $CPAN::DEBUG; | |
05454584 A |
1857 | } |
1858 | } else { | |
c356248b | 1859 | $CPAN::Frontend->myprint("Debugging turned off completely.\n"); |
10b2abe6 | 1860 | } |
05454584 | 1861 | } else { |
c356248b | 1862 | $CPAN::Frontend->myprint(qq{ |
05454584 A |
1863 | Known options: |
1864 | conf set or get configuration variables | |
1865 | debug set or get debugging options | |
c356248b | 1866 | }); |
5f05dabc | 1867 | } |
5f05dabc | 1868 | } |
1869 | ||
6a935156 | 1870 | # CPAN::Shell::paintdots_onreload |
6d29edf5 | 1871 | sub paintdots_onreload { |
36263cb3 GS |
1872 | my($ref) = shift; |
1873 | sub { | |
5fc0f0f6 | 1874 | if ( $_[0] =~ /[Ss]ubroutine ([\w:]+) redefined/ ) { |
36263cb3 GS |
1875 | my($subr) = $1; |
1876 | ++$$ref; | |
1877 | local($|) = 1; | |
1878 | # $CPAN::Frontend->myprint(".($subr)"); | |
1879 | $CPAN::Frontend->myprint("."); | |
6a935156 SP |
1880 | if ($subr =~ /\bshell\b/i) { |
1881 | # warn "debug[$_[0]]"; | |
1882 | ||
1883 | # It would be nice if we could detect that a | |
1884 | # subroutine has actually changed, but for now we | |
1885 | # practically always set the GOTOSHELL global | |
1886 | ||
1887 | $CPAN::GOTOSHELL=1; | |
1888 | } | |
36263cb3 GS |
1889 | return; |
1890 | } | |
1891 | warn @_; | |
1892 | }; | |
1893 | } | |
1894 | ||
05bab18e SP |
1895 | #-> sub CPAN::Shell::hosts ; |
1896 | sub hosts { | |
1897 | my($self) = @_; | |
1898 | my $fullstats = CPAN::FTP->_ftp_statistics(); | |
1899 | my $history = $fullstats->{history} || []; | |
1900 | my %S; # statistics | |
1901 | while (my $last = pop @$history) { | |
1902 | my $attempts = $last->{attempts} or next; | |
1903 | my $start; | |
1904 | if (@$attempts) { | |
1905 | $start = $attempts->[-1]{start}; | |
1906 | if ($#$attempts > 0) { | |
1907 | for my $i (0..$#$attempts-1) { | |
1908 | my $url = $attempts->[$i]{url} or next; | |
1909 | $S{no}{$url}++; | |
1910 | } | |
1911 | } | |
1912 | } else { | |
1913 | $start = $last->{start}; | |
1914 | } | |
1915 | next unless $last->{thesiteurl}; # C-C? bad filenames? | |
1916 | $S{start} = $start; | |
1917 | $S{end} ||= $last->{end}; | |
1918 | my $dltime = $last->{end} - $start; | |
1919 | my $dlsize = $last->{filesize} || 0; | |
f20de9f0 | 1920 | my $url = ref $last->{thesiteurl} ? $last->{thesiteurl}->text : $last->{thesiteurl}; |
05bab18e SP |
1921 | my $s = $S{ok}{$url} ||= {}; |
1922 | $s->{n}++; | |
1923 | $s->{dlsize} ||= 0; | |
1924 | $s->{dlsize} += $dlsize/1024; | |
1925 | $s->{dltime} ||= 0; | |
1926 | $s->{dltime} += $dltime; | |
1927 | } | |
1928 | my $res; | |
1929 | for my $url (keys %{$S{ok}}) { | |
1930 | next if $S{ok}{$url}{dltime} == 0; # div by zero | |
1931 | push @{$res->{ok}}, [@{$S{ok}{$url}}{qw(n dlsize dltime)}, | |
1932 | $S{ok}{$url}{dlsize}/$S{ok}{$url}{dltime}, | |
1933 | $url, | |
1934 | ]; | |
1935 | } | |
1936 | for my $url (keys %{$S{no}}) { | |
1937 | push @{$res->{no}}, [$S{no}{$url}, | |
1938 | $url, | |
1939 | ]; | |
1940 | } | |
1941 | my $R = ""; # report | |
b72dd56f SP |
1942 | if ($S{start} && $S{end}) { |
1943 | $R .= sprintf "Log starts: %s\n", $S{start} ? scalar(localtime $S{start}) : "unknown"; | |
1944 | $R .= sprintf "Log ends : %s\n", $S{end} ? scalar(localtime $S{end}) : "unknown"; | |
1945 | } | |
05bab18e SP |
1946 | if ($res->{ok} && @{$res->{ok}}) { |
1947 | $R .= sprintf "\nSuccessful downloads: | |
1948 | N kB secs kB/s url\n"; | |
be34b10d | 1949 | my $i = 20; |
05bab18e SP |
1950 | for (sort { $b->[3] <=> $a->[3] } @{$res->{ok}}) { |
1951 | $R .= sprintf "%4d %8d %5d %9.1f %s\n", @$_; | |
be34b10d | 1952 | last if --$i<=0; |
05bab18e SP |
1953 | } |
1954 | } | |
1955 | if ($res->{no} && @{$res->{no}}) { | |
1956 | $R .= sprintf "\nUnsuccessful downloads:\n"; | |
be34b10d | 1957 | my $i = 20; |
05bab18e SP |
1958 | for (sort { $b->[0] <=> $a->[0] } @{$res->{no}}) { |
1959 | $R .= sprintf "%4d %s\n", @$_; | |
be34b10d | 1960 | last if --$i<=0; |
05bab18e SP |
1961 | } |
1962 | } | |
1963 | $CPAN::Frontend->myprint($R); | |
1964 | } | |
1965 | ||
05454584 A |
1966 | #-> sub CPAN::Shell::reload ; |
1967 | sub reload { | |
d4fd5c69 A |
1968 | my($self,$command,@arg) = @_; |
1969 | $command ||= ""; | |
1970 | $self->debug("self[$self]command[$command]arg[@arg]") if $CPAN::DEBUG; | |
135a59c2 | 1971 | if ($command =~ /^cpan$/i) { |
e82b9348 | 1972 | my $redef = 0; |
0cf35e6a SP |
1973 | chdir $CPAN::iCwd if $CPAN::iCwd; # may fail |
1974 | my $failed; | |
8962fc49 SP |
1975 | my @relo = ( |
1976 | "CPAN.pm", | |
8962fc49 | 1977 | "CPAN/Debug.pm", |
810a0276 SP |
1978 | "CPAN/FirstTime.pm", |
1979 | "CPAN/HandleConfig.pm", | |
1980 | "CPAN/Kwalify.pm", | |
135a59c2 A |
1981 | "CPAN/Queue.pm", |
1982 | "CPAN/Reporter.pm", | |
f20de9f0 | 1983 | "CPAN/SQLite.pm", |
810a0276 SP |
1984 | "CPAN/Tarzip.pm", |
1985 | "CPAN/Version.pm", | |
8962fc49 | 1986 | ); |
8962fc49 | 1987 | MFILE: for my $f (@relo) { |
135a59c2 A |
1988 | next unless exists $INC{$f}; |
1989 | my $p = $f; | |
1990 | $p =~ s/\.pm$//; | |
1991 | $p =~ s|/|::|g; | |
1992 | $CPAN::Frontend->myprint("($p"); | |
5fc0f0f6 | 1993 | local($SIG{__WARN__}) = paintdots_onreload(\$redef); |
810a0276 | 1994 | $self->_reload_this($f) or $failed++; |
135a59c2 A |
1995 | my $v = eval "$p\::->VERSION"; |
1996 | $CPAN::Frontend->myprint("v$v)"); | |
5fc0f0f6 | 1997 | } |
e82b9348 | 1998 | $CPAN::Frontend->myprint("\n$redef subroutines redefined\n"); |
0cf35e6a | 1999 | if ($failed) { |
135a59c2 A |
2000 | my $errors = $failed == 1 ? "error" : "errors"; |
2001 | $CPAN::Frontend->mywarn("\n$failed $errors during reload. You better quit ". | |
0cf35e6a SP |
2002 | "this session.\n"); |
2003 | } | |
135a59c2 | 2004 | } elsif ($command =~ /^index$/i) { |
2e2b7522 | 2005 | CPAN::Index->force_reload; |
d4fd5c69 | 2006 | } else { |
135a59c2 | 2007 | $CPAN::Frontend->myprint(qq{cpan re-evals the CPAN modules |
f14b5cec | 2008 | index re-reads the index files\n}); |
05454584 A |
2009 | } |
2010 | } | |
2011 | ||
2ccf00a7 | 2012 | # reload means only load again what we have loaded before |
810a0276 SP |
2013 | #-> sub CPAN::Shell::_reload_this ; |
2014 | sub _reload_this { | |
6a935156 | 2015 | my($self,$f,$args) = @_; |
7d97ad34 | 2016 | CPAN->debug("f[$f]") if $CPAN::DEBUG; |
2ccf00a7 SP |
2017 | return 1 unless $INC{$f}; # we never loaded this, so we do not |
2018 | # reload but say OK | |
c9869e1c | 2019 | my $pwd = CPAN::anycwd(); |
7d97ad34 SP |
2020 | CPAN->debug("pwd[$pwd]") if $CPAN::DEBUG; |
2021 | my($file); | |
c9869e1c | 2022 | for my $inc (@INC) { |
7d97ad34 SP |
2023 | $file = File::Spec->catfile($inc,split /\//, $f); |
2024 | last if -f $file; | |
2025 | $file = ""; | |
2026 | } | |
2027 | CPAN->debug("file[$file]") if $CPAN::DEBUG; | |
2028 | my @inc = @INC; | |
2029 | unless ($file && -f $file) { | |
2030 | # this thingie is not in the INC path, maybe CPAN/MyConfig.pm? | |
2031 | $file = $INC{$f}; | |
6658a91b SP |
2032 | unless (CPAN->has_inst("File::Basename")) { |
2033 | @inc = File::Basename::dirname($file); | |
2034 | } else { | |
2035 | # do we ever need this? | |
2036 | @inc = substr($file,0,-length($f)-1); # bring in back to me! | |
2037 | } | |
7d97ad34 SP |
2038 | } |
2039 | CPAN->debug("file[$file]inc[@inc]") if $CPAN::DEBUG; | |
2040 | unless (-f $file) { | |
c9869e1c SP |
2041 | $CPAN::Frontend->mywarn("Found no file to reload for '$f'\n"); |
2042 | return; | |
2043 | } | |
6a935156 SP |
2044 | my $mtime = (stat $file)[9]; |
2045 | $reload->{$f} ||= $^T; | |
2046 | my $must_reload = $mtime > $reload->{$f}; | |
2047 | $args ||= {}; | |
810a0276 | 2048 | $must_reload ||= $args->{reloforce}; |
6a935156 SP |
2049 | if ($must_reload) { |
2050 | my $fh = FileHandle->new($file) or | |
2051 | $CPAN::Frontend->mydie("Could not open $file: $!"); | |
2052 | local($/); | |
2053 | local $^W = 1; | |
2054 | my $content = <$fh>; | |
2055 | CPAN->debug(sprintf("reload file[%s] content[%s...]",$file,substr($content,0,128))) | |
2056 | if $CPAN::DEBUG; | |
2057 | delete $INC{$f}; | |
2058 | local @INC = @inc; | |
2059 | eval "require '$f'"; | |
2060 | if ($@){ | |
2061 | warn $@; | |
2062 | return; | |
2063 | } | |
2064 | $reload->{$f} = time; | |
2065 | } else { | |
2066 | $CPAN::Frontend->myprint("__unchanged__"); | |
c9869e1c SP |
2067 | } |
2068 | return 1; | |
2069 | } | |
2070 | ||
44d21104 A |
2071 | #-> sub CPAN::Shell::mkmyconfig ; |
2072 | sub mkmyconfig { | |
2073 | my($self, $cpanpm, %args) = @_; | |
2074 | require CPAN::FirstTime; | |
87892b73 RGS |
2075 | my $home = CPAN::HandleConfig::home; |
2076 | $cpanpm = $INC{'CPAN/MyConfig.pm'} || | |
2077 | File::Spec->catfile(split /\//, "$home/.cpan/CPAN/MyConfig.pm"); | |
44d21104 | 2078 | File::Path::mkpath(File::Basename::dirname($cpanpm)) unless -e $cpanpm; |
87892b73 | 2079 | CPAN::HandleConfig::require_myconfig_or_config; |
44d21104 A |
2080 | $CPAN::Config ||= {}; |
2081 | $CPAN::Config = { | |
2082 | %$CPAN::Config, | |
2083 | build_dir => undef, | |
2084 | cpan_home => undef, | |
2085 | keep_source_where => undef, | |
2086 | histfile => undef, | |
2087 | }; | |
2088 | CPAN::FirstTime::init($cpanpm, %args); | |
2089 | } | |
2090 | ||
05454584 A |
2091 | #-> sub CPAN::Shell::_binary_extensions ; |
2092 | sub _binary_extensions { | |
2093 | my($self) = shift @_; | |
2094 | my(@result,$module,%seen,%need,$headerdone); | |
2095 | for $module ($self->expand('Module','/./')) { | |
2096 | my $file = $module->cpan_file; | |
2097 | next if $file eq "N/A"; | |
2098 | next if $file =~ /^Contact Author/; | |
05d2a450 A |
2099 | my $dist = $CPAN::META->instance('CPAN::Distribution',$file); |
2100 | next if $dist->isa_perl; | |
05454584 A |
2101 | next unless $module->xs_file; |
2102 | local($|) = 1; | |
c356248b | 2103 | $CPAN::Frontend->myprint("."); |
05454584 A |
2104 | push @result, $module; |
2105 | } | |
2106 | # print join " | ", @result; | |
c356248b | 2107 | $CPAN::Frontend->myprint("\n"); |
05454584 A |
2108 | return @result; |
2109 | } | |
2110 | ||
2111 | #-> sub CPAN::Shell::recompile ; | |
2112 | sub recompile { | |
2113 | my($self) = shift @_; | |
2114 | my($module,@module,$cpan_file,%dist); | |
2115 | @module = $self->_binary_extensions(); | |
c356248b A |
2116 | for $module (@module){ # we force now and compile later, so we |
2117 | # don't do it twice | |
05454584 A |
2118 | $cpan_file = $module->cpan_file; |
2119 | my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); | |
810a0276 | 2120 | $pack->force; # |
05454584 A |
2121 | $dist{$cpan_file}++; |
2122 | } | |
2123 | for $cpan_file (sort keys %dist) { | |
c356248b | 2124 | $CPAN::Frontend->myprint(" CPAN: Recompiling $cpan_file\n\n"); |
05454584 A |
2125 | my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); |
2126 | $pack->install; | |
2127 | $CPAN::Signal = 0; # it's tempting to reset Signal, so we can | |
2128 | # stop a package from recompiling, | |
2129 | # e.g. IO-1.12 when we have perl5.003_10 | |
2130 | } | |
2131 | } | |
2132 | ||
ed84aac9 A |
2133 | #-> sub CPAN::Shell::scripts ; |
2134 | sub scripts { | |
2135 | my($self, $arg) = @_; | |
2136 | $CPAN::Frontend->mywarn(">>>> experimental command, currently unsupported <<<<\n\n"); | |
2137 | ||
8962fc49 SP |
2138 | for my $req (qw( HTML::LinkExtor Sort::Versions List::Util )) { |
2139 | unless ($CPAN::META->has_inst($req)) { | |
2140 | $CPAN::Frontend->mywarn(" $req not available\n"); | |
2141 | } | |
2142 | } | |
ed84aac9 A |
2143 | my $p = HTML::LinkExtor->new(); |
2144 | my $indexfile = "/home/ftp/pub/PAUSE/scripts/new/index.html"; | |
2145 | unless (-f $indexfile) { | |
2146 | $CPAN::Frontend->mydie("found no indexfile[$indexfile]\n"); | |
2147 | } | |
2148 | $p->parse_file($indexfile); | |
2149 | my @hrefs; | |
2150 | my $qrarg; | |
2151 | if ($arg =~ s|^/(.+)/$|$1|) { | |
8962fc49 | 2152 | $qrarg = eval 'qr/$arg/'; # hide construct from 5.004 |
ed84aac9 A |
2153 | } |
2154 | for my $l ($p->links) { | |
2155 | my $tag = shift @$l; | |
2156 | next unless $tag eq "a"; | |
2157 | my %att = @$l; | |
2158 | my $href = $att{href}; | |
2159 | next unless $href =~ s|^\.\./authors/id/./../||; | |
2160 | if ($arg) { | |
2161 | if ($qrarg) { | |
2162 | if ($href =~ $qrarg) { | |
2163 | push @hrefs, $href; | |
2164 | } | |
2165 | } else { | |
2166 | if ($href =~ /\Q$arg\E/) { | |
2167 | push @hrefs, $href; | |
2168 | } | |
2169 | } | |
2170 | } else { | |
2171 | push @hrefs, $href; | |
2172 | } | |
2173 | } | |
2174 | # now filter for the latest version if there is more than one of a name | |
2175 | my %stems; | |
2176 | for (sort @hrefs) { | |
2177 | my $href = $_; | |
2178 | s/-v?\d.*//; | |
2179 | my $stem = $_; | |
2180 | $stems{$stem} ||= []; | |
2181 | push @{$stems{$stem}}, $href; | |
2182 | } | |
2183 | for (sort keys %stems) { | |
2184 | my $highest; | |
2185 | if (@{$stems{$_}} > 1) { | |
2186 | $highest = List::Util::reduce { | |
2187 | Sort::Versions::versioncmp($a,$b) > 0 ? $a : $b | |
2188 | } @{$stems{$_}}; | |
2189 | } else { | |
2190 | $highest = $stems{$_}[0]; | |
2191 | } | |
2192 | $CPAN::Frontend->myprint("$highest\n"); | |
2193 | } | |
2194 | } | |
2195 | ||
8fc516fe SP |
2196 | #-> sub CPAN::Shell::report ; |
2197 | sub report { | |
2198 | my($self,@args) = @_; | |
2199 | unless ($CPAN::META->has_inst("CPAN::Reporter")) { | |
2200 | $CPAN::Frontend->mydie("CPAN::Reporter not installed; cannot continue"); | |
2201 | } | |
2202 | local $CPAN::Config->{test_report} = 1; | |
6658a91b SP |
2203 | $self->force("test",@args); # force is there so that the test be |
2204 | # re-run (as documented) | |
8fc516fe SP |
2205 | } |
2206 | ||
f20de9f0 | 2207 | # compare with is_tested |
05bab18e SP |
2208 | #-> sub CPAN::Shell::install_tested |
2209 | sub install_tested { | |
2210 | my($self,@some) = @_; | |
b72dd56f | 2211 | $CPAN::Frontend->mywarn("install_tested() must not be called with arguments.\n"), |
05bab18e SP |
2212 | return if @some; |
2213 | CPAN::Index->reload; | |
2214 | ||
b72dd56f SP |
2215 | for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { |
2216 | my $yaml = "$b.yml"; | |
2217 | unless (-f $yaml){ | |
2218 | $CPAN::Frontend->mywarn("No YAML file for $b available, skipping\n"); | |
2219 | next; | |
2220 | } | |
f20de9f0 SP |
2221 | my $yaml_content = CPAN->_yaml_loadfile($yaml); |
2222 | my $id = $yaml_content->[0]{distribution}{ID}; | |
b72dd56f SP |
2223 | unless ($id){ |
2224 | $CPAN::Frontend->mywarn("No ID found in '$yaml', skipping\n"); | |
2225 | next; | |
2226 | } | |
2227 | my $do = CPAN::Shell->expandany($id); | |
2228 | unless ($do){ | |
2229 | $CPAN::Frontend->mywarn("Could not expand ID '$id', skipping\n"); | |
2230 | next; | |
2231 | } | |
2232 | unless ($do->{build_dir}) { | |
2233 | $CPAN::Frontend->mywarn("Distro '$id' has no build_dir, skipping\n"); | |
2234 | next; | |
2235 | } | |
2236 | unless ($do->{build_dir} eq $b) { | |
2237 | $CPAN::Frontend->mywarn("Distro '$id' has build_dir '$do->{build_dir}' but expected '$b', skipping\n"); | |
2238 | next; | |
2239 | } | |
05bab18e SP |
2240 | push @some, $do; |
2241 | } | |
2242 | ||
2243 | $CPAN::Frontend->mywarn("No tested distributions found.\n"), | |
2244 | return unless @some; | |
2245 | ||
2246 | @some = grep { $_->{make_test} && ! $_->{make_test}->failed } @some; | |
2247 | $CPAN::Frontend->mywarn("No distributions tested with this build of perl found.\n"), | |
2248 | return unless @some; | |
2249 | ||
b72dd56f SP |
2250 | # @some = grep { not $_->uptodate } @some; |
2251 | # $CPAN::Frontend->mywarn("No non-uptodate distributions tested with this build of perl found.\n"), | |
2252 | # return unless @some; | |
05bab18e SP |
2253 | |
2254 | CPAN->debug("some[@some]"); | |
2255 | for my $d (@some) { | |
2256 | my $id = $d->can("pretty_id") ? $d->pretty_id : $d->id; | |
2257 | $CPAN::Frontend->myprint("install_tested: Running for $id\n"); | |
b72dd56f | 2258 | $CPAN::Frontend->mysleep(1); |
05bab18e SP |
2259 | $self->install($d); |
2260 | } | |
2261 | } | |
2262 | ||
ed84aac9 A |
2263 | #-> sub CPAN::Shell::upgrade ; |
2264 | sub upgrade { | |
135a59c2 A |
2265 | my($self,@args) = @_; |
2266 | $self->install($self->r(@args)); | |
ed84aac9 A |
2267 | } |
2268 | ||
05454584 A |
2269 | #-> sub CPAN::Shell::_u_r_common ; |
2270 | sub _u_r_common { | |
2271 | my($self) = shift @_; | |
2272 | my($what) = shift @_; | |
2273 | CPAN->debug("self[$self] what[$what] args[@_]") if $CPAN::DEBUG; | |
c4d24d4c A |
2274 | Carp::croak "Usage: \$obj->_u_r_common(a|r|u)" unless |
2275 | $what && $what =~ /^[aru]$/; | |
05454584 A |
2276 | my(@args) = @_; |
2277 | @args = '/./' unless @args; | |
c356248b A |
2278 | my(@result,$module,%seen,%need,$headerdone, |
2279 | $version_undefs,$version_zeroes); | |
2280 | $version_undefs = $version_zeroes = 0; | |
9d61fa1d | 2281 | my $sprintf = "%s%-25s%s %9s %9s %s\n"; |
6d29edf5 JH |
2282 | my @expand = $self->expand('Module',@args); |
2283 | my $expand = scalar @expand; | |
2284 | if (0) { # Looks like noise to me, was very useful for debugging | |
2285 | # for metadata cache | |
2286 | $CPAN::Frontend->myprint(sprintf "%d matches in the database\n", $expand); | |
2287 | } | |
554a9ef5 | 2288 | MODULE: for $module (@expand) { |
05454584 | 2289 | my $file = $module->cpan_file; |
554a9ef5 | 2290 | next MODULE unless defined $file; # ?? |
e82b9348 | 2291 | $file =~ s|^./../||; |
6d29edf5 | 2292 | my($latest) = $module->cpan_version; |
05454584 A |
2293 | my($inst_file) = $module->inst_file; |
2294 | my($have); | |
09d9d230 | 2295 | return if $CPAN::Signal; |
05454584 A |
2296 | if ($inst_file){ |
2297 | if ($what eq "a") { | |
6d29edf5 | 2298 | $have = $module->inst_version; |
05454584 | 2299 | } elsif ($what eq "r") { |
6d29edf5 | 2300 | $have = $module->inst_version; |
05454584 | 2301 | local($^W) = 0; |
c356248b A |
2302 | if ($have eq "undef"){ |
2303 | $version_undefs++; | |
2304 | } elsif ($have == 0){ | |
2305 | $version_zeroes++; | |
2306 | } | |
554a9ef5 | 2307 | next MODULE unless CPAN::Version->vgt($latest, $have); |
c356248b A |
2308 | # to be pedantic we should probably say: |
2309 | # && !($have eq "undef" && $latest ne "undef" && $latest gt ""); | |
2310 | # to catch the case where CPAN has a version 0 and we have a version undef | |
05454584 | 2311 | } elsif ($what eq "u") { |
554a9ef5 | 2312 | next MODULE; |
05454584 A |
2313 | } |
2314 | } else { | |
2315 | if ($what eq "a") { | |
554a9ef5 | 2316 | next MODULE; |
05454584 | 2317 | } elsif ($what eq "r") { |
554a9ef5 | 2318 | next MODULE; |
05454584 A |
2319 | } elsif ($what eq "u") { |
2320 | $have = "-"; | |
2321 | } | |
2322 | } | |
2323 | return if $CPAN::Signal; # this is sometimes lengthy | |
2324 | $seen{$file} ||= 0; | |
2325 | if ($what eq "a") { | |
2326 | push @result, sprintf "%s %s\n", $module->id, $have; | |
2327 | } elsif ($what eq "r") { | |
2328 | push @result, $module->id; | |
f3fe0ae6 | 2329 | next MODULE if $seen{$file}++; |
05454584 A |
2330 | } elsif ($what eq "u") { |
2331 | push @result, $module->id; | |
f3fe0ae6 NC |
2332 | next MODULE if $seen{$file}++; |
2333 | next MODULE if $file =~ /^Contact/; | |
05454584 A |
2334 | } |
2335 | unless ($headerdone++){ | |
c356248b A |
2336 | $CPAN::Frontend->myprint("\n"); |
2337 | $CPAN::Frontend->myprint(sprintf( | |
9d61fa1d A |
2338 | $sprintf, |
2339 | "", | |
2340 | "Package namespace", | |
2341 | "", | |
2342 | "installed", | |
2343 | "latest", | |
2344 | "in CPAN file" | |
2345 | )); | |
05454584 | 2346 | } |
9d61fa1d A |
2347 | my $color_on = ""; |
2348 | my $color_off = ""; | |
2349 | if ( | |
2350 | $COLOR_REGISTERED | |
2351 | && | |
2352 | $CPAN::META->has_inst("Term::ANSIColor") | |
2353 | && | |
0cf35e6a | 2354 | $module->description |
9d61fa1d A |
2355 | ) { |
2356 | $color_on = Term::ANSIColor::color("green"); | |
2357 | $color_off = Term::ANSIColor::color("reset"); | |
2358 | } | |
05d2a450 | 2359 | $CPAN::Frontend->myprint(sprintf $sprintf, |
9d61fa1d | 2360 | $color_on, |
05d2a450 | 2361 | $module->id, |
9d61fa1d | 2362 | $color_off, |
05d2a450 A |
2363 | $have, |
2364 | $latest, | |
2365 | $file); | |
05454584 A |
2366 | $need{$module->id}++; |
2367 | } | |
2368 | unless (%need) { | |
2369 | if ($what eq "u") { | |
c356248b | 2370 | $CPAN::Frontend->myprint("No modules found for @args\n"); |
05454584 | 2371 | } elsif ($what eq "r") { |
c356248b | 2372 | $CPAN::Frontend->myprint("All modules are up to date for @args\n"); |
05454584 A |
2373 | } |
2374 | } | |
c356248b A |
2375 | if ($what eq "r") { |
2376 | if ($version_zeroes) { | |
2377 | my $s_has = $version_zeroes > 1 ? "s have" : " has"; | |
2378 | $CPAN::Frontend->myprint(qq{$version_zeroes installed module$s_has }. | |
2379 | qq{a version number of 0\n}); | |
2380 | } | |
2381 | if ($version_undefs) { | |
2382 | my $s_has = $version_undefs > 1 ? "s have" : " has"; | |
2383 | $CPAN::Frontend->myprint(qq{$version_undefs installed module$s_has no }. | |
2384 | qq{parseable version number\n}); | |
2385 | } | |
05454584 A |
2386 | } |
2387 | @result; | |
2388 | } | |
2389 | ||
2390 | #-> sub CPAN::Shell::r ; | |
2391 | sub r { | |
2392 | shift->_u_r_common("r",@_); | |
2393 | } | |
2394 | ||
2395 | #-> sub CPAN::Shell::u ; | |
2396 | sub u { | |
2397 | shift->_u_r_common("u",@_); | |
2398 | } | |
2399 | ||
0cf35e6a SP |
2400 | #-> sub CPAN::Shell::failed ; |
2401 | sub failed { | |
9ddc4ed0 | 2402 | my($self,$only_id,$silent) = @_; |
c9869e1c | 2403 | my @failed; |
0cf35e6a SP |
2404 | DIST: for my $d ($CPAN::META->all_objects("CPAN::Distribution")) { |
2405 | my $failed = ""; | |
810a0276 | 2406 | NAY: for my $nosayer ( # order matters! |
6658a91b | 2407 | "unwrapped", |
87892b73 RGS |
2408 | "writemakefile", |
2409 | "signature_verify", | |
2410 | "make", | |
2411 | "make_test", | |
2412 | "install", | |
2413 | "make_clean", | |
2414 | ) { | |
0cf35e6a | 2415 | next unless exists $d->{$nosayer}; |
be34b10d | 2416 | next unless defined $d->{$nosayer}; |
44d21104 | 2417 | next unless ( |
be34b10d | 2418 | UNIVERSAL::can($d->{$nosayer},"failed") ? |
44d21104 A |
2419 | $d->{$nosayer}->failed : |
2420 | $d->{$nosayer} =~ /^NO/ | |
2421 | ); | |
87892b73 | 2422 | next NAY if $only_id && $only_id != ( |
be34b10d | 2423 | UNIVERSAL::can($d->{$nosayer},"commandid") |
87892b73 RGS |
2424 | ? |
2425 | $d->{$nosayer}->commandid | |
2426 | : | |
2427 | $CPAN::CurrentCommandId | |
2428 | ); | |
0cf35e6a SP |
2429 | $failed = $nosayer; |
2430 | last; | |
2431 | } | |
2432 | next DIST unless $failed; | |
2433 | my $id = $d->id; | |
2434 | $id =~ s|^./../||; | |
c9869e1c SP |
2435 | #$print .= sprintf( |
2436 | # " %-45s: %s %s\n", | |
44d21104 A |
2437 | push @failed, |
2438 | ( | |
be34b10d | 2439 | UNIVERSAL::can($d->{$failed},"failed") ? |
44d21104 A |
2440 | [ |
2441 | $d->{$failed}->commandid, | |
2442 | $id, | |
2443 | $failed, | |
2444 | $d->{$failed}->text, | |
be34b10d | 2445 | $d->{$failed}{TIME}||0, |
44d21104 A |
2446 | ] : |
2447 | [ | |
2448 | 1, | |
2449 | $id, | |
2450 | $failed, | |
2451 | $d->{$failed}, | |
be34b10d | 2452 | 0, |
44d21104 A |
2453 | ] |
2454 | ); | |
0cf35e6a | 2455 | } |
be34b10d SP |
2456 | my $scope; |
2457 | if ($only_id) { | |
2458 | $scope = "this command"; | |
2459 | } elsif ($CPAN::Index::HAVE_REANIMATED) { | |
2460 | $scope = "this or a previous session"; | |
2461 | # it might be nice to have a section for previous session and | |
2462 | # a second for this | |
2463 | } else { | |
2464 | $scope = "this session"; | |
2465 | } | |
c9869e1c | 2466 | if (@failed) { |
be34b10d SP |
2467 | my $print; |
2468 | my $debug = 0; | |
2469 | if ($debug) { | |
2470 | $print = join "", | |
2471 | map { sprintf "%5d %-45s: %s %s\n", @$_ } | |
2472 | sort { $a->[0] <=> $b->[0] } @failed; | |
2473 | } else { | |
2474 | $print = join "", | |
2475 | map { sprintf " %-45s: %s %s\n", @$_[1..3] } | |
2476 | sort { | |
2477 | $a->[0] <=> $b->[0] | |
2478 | || | |
2479 | $a->[4] <=> $b->[4] | |
2480 | } @failed; | |
2481 | } | |
2482 | $CPAN::Frontend->myprint("Failed during $scope:\n$print"); | |
9ddc4ed0 | 2483 | } elsif (!$only_id || !$silent) { |
be34b10d | 2484 | $CPAN::Frontend->myprint("Nothing failed in $scope\n"); |
0cf35e6a SP |
2485 | } |
2486 | } | |
2487 | ||
c9869e1c SP |
2488 | # XXX intentionally undocumented because completely bogus, unportable, |
2489 | # useless, etc. | |
2490 | ||
0cf35e6a SP |
2491 | #-> sub CPAN::Shell::status ; |
2492 | sub status { | |
2493 | my($self) = @_; | |
2494 | require Devel::Size; | |
2495 | my $ps = FileHandle->new; | |
2496 | open $ps, "/proc/$$/status"; | |
2497 | my $vm = 0; | |
2498 | while (<$ps>) { | |
2499 | next unless /VmSize:\s+(\d+)/; | |
2500 | $vm = $1; | |
2501 | last; | |
2502 | } | |
2503 | $CPAN::Frontend->mywarn(sprintf( | |
2504 | "%-27s %6d\n%-27s %6d\n", | |
2505 | "vm", | |
2506 | $vm, | |
2507 | "CPAN::META", | |
2508 | Devel::Size::total_size($CPAN::META)/1024, | |
2509 | )); | |
2510 | for my $k (sort keys %$CPAN::META) { | |
2511 | next unless substr($k,0,4) eq "read"; | |
2512 | warn sprintf " %-26s %6d\n", $k, Devel::Size::total_size($CPAN::META->{$k})/1024; | |
2513 | for my $k2 (sort keys %{$CPAN::META->{$k}}) { | |
7d97ad34 | 2514 | warn sprintf " %-25s %6d (keys: %6d)\n", |
0cf35e6a SP |
2515 | $k2, |
2516 | Devel::Size::total_size($CPAN::META->{$k}{$k2})/1024, | |
2517 | scalar keys %{$CPAN::META->{$k}{$k2}}; | |
2518 | } | |
2519 | } | |
2520 | } | |
2521 | ||
f20de9f0 | 2522 | # compare with install_tested |
b72dd56f | 2523 | #-> sub CPAN::Shell::is_tested |
f20de9f0 | 2524 | sub is_tested { |
b72dd56f | 2525 | my($self) = @_; |
f20de9f0 | 2526 | CPAN::Index->reload; |
b72dd56f SP |
2527 | for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { |
2528 | my $time; | |
2529 | if ($CPAN::META->{is_tested}{$b}) { | |
2530 | $time = scalar(localtime $CPAN::META->{is_tested}{$b}); | |
2531 | } else { | |
2532 | $time = scalar localtime; | |
2533 | $time =~ s/\S/?/g; | |
2534 | } | |
2535 | $CPAN::Frontend->myprint(sprintf "%s %s\n", $time, $b); | |
2536 | } | |
2537 | } | |
2538 | ||
05454584 A |
2539 | #-> sub CPAN::Shell::autobundle ; |
2540 | sub autobundle { | |
2541 | my($self) = shift; | |
e82b9348 | 2542 | CPAN::HandleConfig->load unless $CPAN::Config_loaded++; |
05454584 | 2543 | my(@bundle) = $self->_u_r_common("a",@_); |
5de3f0da | 2544 | my($todir) = File::Spec->catdir($CPAN::Config->{'cpan_home'},"Bundle"); |
05454584 A |
2545 | File::Path::mkpath($todir); |
2546 | unless (-d $todir) { | |
c356248b | 2547 | $CPAN::Frontend->myprint("Couldn't mkdir $todir for some reason\n"); |
05454584 A |
2548 | return; |
2549 | } | |
2550 | my($y,$m,$d) = (localtime)[5,4,3]; | |
2551 | $y+=1900; | |
2552 | $m++; | |
2553 | my($c) = 0; | |
2554 | my($me) = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, $c; | |
5de3f0da | 2555 | my($to) = File::Spec->catfile($todir,"$me.pm"); |
05454584 A |
2556 | while (-f $to) { |
2557 | $me = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, ++$c; | |
5de3f0da | 2558 | $to = File::Spec->catfile($todir,"$me.pm"); |
05454584 A |
2559 | } |
2560 | my($fh) = FileHandle->new(">$to") or Carp::croak "Can't open >$to: $!"; | |
2561 | $fh->print( | |
2562 | "package Bundle::$me;\n\n", | |
2563 | "\$VERSION = '0.01';\n\n", | |
2564 | "1;\n\n", | |
2565 | "__END__\n\n", | |
2566 | "=head1 NAME\n\n", | |
2567 | "Bundle::$me - Snapshot of installation on ", | |
2568 | $Config::Config{'myhostname'}, | |
2569 | " on ", | |
2570 | scalar(localtime), | |
2571 | "\n\n=head1 SYNOPSIS\n\n", | |
2572 | "perl -MCPAN -e 'install Bundle::$me'\n\n", | |
2573 | "=head1 CONTENTS\n\n", | |
2574 | join("\n", @bundle), | |
2575 | "\n\n=head1 CONFIGURATION\n\n", | |
2576 | Config->myconfig, | |
2577 | "\n\n=head1 AUTHOR\n\n", | |
2578 | "This Bundle has been generated automatically ", | |
2579 | "by the autobundle routine in CPAN.pm.\n", | |
2580 | ); | |
2581 | $fh->close; | |
c356248b A |
2582 | $CPAN::Frontend->myprint("\nWrote bundle file |
2583 | $to\n\n"); | |
05454584 A |
2584 | } |
2585 | ||
6d29edf5 JH |
2586 | #-> sub CPAN::Shell::expandany ; |
2587 | sub expandany { | |
2588 | my($self,$s) = @_; | |
2589 | CPAN->debug("s[$s]") if $CPAN::DEBUG; | |
8fc516fe | 2590 | if ($s =~ m|/| or substr($s,-1,1) eq ".") { # looks like a file or a directory |
8d97e4a1 | 2591 | $s = CPAN::Distribution->normalize($s); |
6d29edf5 JH |
2592 | return $CPAN::META->instance('CPAN::Distribution',$s); |
2593 | # Distributions spring into existence, not expand | |
2594 | } elsif ($s =~ m|^Bundle::|) { | |
2595 | $self->local_bundles; # scanning so late for bundles seems | |
2596 | # both attractive and crumpy: always | |
2597 | # current state but easy to forget | |
2598 | # somewhere | |
2599 | return $self->expand('Bundle',$s); | |
2600 | } else { | |
2601 | return $self->expand('Module',$s) | |
2602 | if $CPAN::META->exists('CPAN::Module',$s); | |
2603 | } | |
2604 | return; | |
2605 | } | |
2606 | ||
05454584 A |
2607 | #-> sub CPAN::Shell::expand ; |
2608 | sub expand { | |
e82b9348 | 2609 | my $self = shift; |
05454584 | 2610 | my($type,@args) = @_; |
8d97e4a1 | 2611 | CPAN->debug("type[$type]args[@args]") if $CPAN::DEBUG; |
e82b9348 SP |
2612 | my $class = "CPAN::$type"; |
2613 | my $methods = ['id']; | |
2614 | for my $meth (qw(name)) { | |
e82b9348 SP |
2615 | next unless $class->can($meth); |
2616 | push @$methods, $meth; | |
2617 | } | |
2618 | $self->expand_by_method($class,$methods,@args); | |
2619 | } | |
2620 | ||
05bab18e | 2621 | #-> sub CPAN::Shell::expand_by_method ; |
e82b9348 SP |
2622 | sub expand_by_method { |
2623 | my $self = shift; | |
2624 | my($class,$methods,@args) = @_; | |
2625 | my($arg,@m); | |
05454584 | 2626 | for $arg (@args) { |
6d29edf5 | 2627 | my($regex,$command); |
05454584 A |
2628 | if ($arg =~ m|^/(.*)/$|) { |
2629 | $regex = $1; | |
8d97e4a1 JH |
2630 | } elsif ($arg =~ m/=/) { |
2631 | $command = 1; | |
6d29edf5 | 2632 | } |
05454584 | 2633 | my $obj; |
8d97e4a1 JH |
2634 | CPAN->debug(sprintf "class[%s]regex[%s]command[%s]", |
2635 | $class, | |
2636 | defined $regex ? $regex : "UNDEFINED", | |
e82b9348 | 2637 | defined $command ? $command : "UNDEFINED", |
8d97e4a1 | 2638 | ) if $CPAN::DEBUG; |
05454584 | 2639 | if (defined $regex) { |
810a0276 | 2640 | if (CPAN::_sqlite_running) { |
be34b10d SP |
2641 | $CPAN::SQLite->search($class, $regex); |
2642 | } | |
6d29edf5 | 2643 | for $obj ( |
6d29edf5 JH |
2644 | $CPAN::META->all_objects($class) |
2645 | ) { | |
b72dd56f | 2646 | unless ($obj && UNIVERSAL::can($obj,"id") && $obj->id){ |
6d29edf5 | 2647 | # BUG, we got an empty object somewhere |
8d97e4a1 | 2648 | require Data::Dumper; |
6d29edf5 | 2649 | CPAN->debug(sprintf( |
8d97e4a1 | 2650 | "Bug in CPAN: Empty id on obj[%s][%s]", |
6d29edf5 | 2651 | $obj, |
8d97e4a1 | 2652 | Data::Dumper::Dumper($obj) |
6d29edf5 JH |
2653 | )) if $CPAN::DEBUG; |
2654 | next; | |
2655 | } | |
e82b9348 | 2656 | for my $method (@$methods) { |
135a59c2 A |
2657 | my $match = eval {$obj->$method() =~ /$regex/i}; |
2658 | if ($@) { | |
2659 | my($err) = $@ =~ /^(.+) at .+? line \d+\.$/; | |
2660 | $err ||= $@; # if we were too restrictive above | |
2661 | $CPAN::Frontend->mydie("$err\n"); | |
2662 | } elsif ($match) { | |
e82b9348 SP |
2663 | push @m, $obj; |
2664 | last; | |
2665 | } | |
2666 | } | |
6d29edf5 JH |
2667 | } |
2668 | } elsif ($command) { | |
8d97e4a1 JH |
2669 | die "equal sign in command disabled (immature interface), ". |
2670 | "you can set | |
2671 | ! \$CPAN::Shell::ADVANCED_QUERY=1 | |
2672 | to enable it. But please note, this is HIGHLY EXPERIMENTAL code | |
2673 | that may go away anytime.\n" | |
2674 | unless $ADVANCED_QUERY; | |
2675 | my($method,$criterion) = $arg =~ /(.+?)=(.+)/; | |
2676 | my($matchcrit) = $criterion =~ m/^~(.+)/; | |
6d29edf5 JH |
2677 | for my $self ( |
2678 | sort | |
2679 | {$a->id cmp $b->id} | |
2680 | $CPAN::META->all_objects($class) | |
2681 | ) { | |
8d97e4a1 JH |
2682 | my $lhs = $self->$method() or next; # () for 5.00503 |
2683 | if ($matchcrit) { | |
2684 | push @m, $self if $lhs =~ m/$matchcrit/; | |
2685 | } else { | |
2686 | push @m, $self if $lhs eq $criterion; | |
2687 | } | |
6d29edf5 | 2688 | } |
05454584 A |
2689 | } else { |
2690 | my($xarg) = $arg; | |
e82b9348 | 2691 | if ( $class eq 'CPAN::Bundle' ) { |
05454584 | 2692 | $xarg =~ s/^(Bundle::)?(.*)/Bundle::$2/; |
e82b9348 | 2693 | } elsif ($class eq "CPAN::Distribution") { |
8d97e4a1 | 2694 | $xarg = CPAN::Distribution->normalize($arg); |
e82b9348 SP |
2695 | } else { |
2696 | $xarg =~ s/:+/::/g; | |
8d97e4a1 | 2697 | } |
05454584 A |
2698 | if ($CPAN::META->exists($class,$xarg)) { |
2699 | $obj = $CPAN::META->instance($class,$xarg); | |
2700 | } elsif ($CPAN::META->exists($class,$arg)) { | |
2701 | $obj = $CPAN::META->instance($class,$arg); | |
2702 | } else { | |
2703 | next; | |
2704 | } | |
2705 | push @m, $obj; | |
2706 | } | |
2707 | } | |
e82b9348 SP |
2708 | @m = sort {$a->id cmp $b->id} @m; |
2709 | if ( $CPAN::DEBUG ) { | |
2710 | my $wantarray = wantarray; | |
2711 | my $join_m = join ",", map {$_->id} @m; | |
2712 | $self->debug("wantarray[$wantarray]join_m[$join_m]"); | |
2713 | } | |
e50380aa | 2714 | return wantarray ? @m : $m[0]; |
05454584 A |
2715 | } |
2716 | ||
2717 | #-> sub CPAN::Shell::format_result ; | |
2718 | sub format_result { | |
2719 | my($self) = shift; | |
2720 | my($type,@args) = @_; | |
2721 | @args = '/./' unless @args; | |
2722 | my(@result) = $self->expand($type,@args); | |
8d97e4a1 | 2723 | my $result = @result == 1 ? |
05454584 | 2724 | $result[0]->as_string : |
8d97e4a1 JH |
2725 | @result == 0 ? |
2726 | "No objects of type $type found for argument @args\n" : | |
2727 | join("", | |
2728 | (map {$_->as_glimpse} @result), | |
2729 | scalar @result, " items found\n", | |
2730 | ); | |
05454584 A |
2731 | $result; |
2732 | } | |
2733 | ||
554a9ef5 SP |
2734 | #-> sub CPAN::Shell::report_fh ; |
2735 | { | |
2736 | my $installation_report_fh; | |
2737 | my $previously_noticed = 0; | |
2738 | ||
2739 | sub report_fh { | |
2740 | return $installation_report_fh if $installation_report_fh; | |
4d1321a7 A |
2741 | if ($CPAN::META->has_inst("File::Temp")) { |
2742 | $installation_report_fh | |
2743 | = File::Temp->new( | |
2744 | template => 'cpan_install_XXXX', | |
2745 | suffix => '.txt', | |
2746 | unlink => 0, | |
2747 | ); | |
2748 | } | |
554a9ef5 SP |
2749 | unless ( $installation_report_fh ) { |
2750 | warn("Couldn't open installation report file; " . | |
2751 | "no report file will be generated." | |
2752 | ) unless $previously_noticed++; | |
2753 | } | |
2754 | } | |
2755 | } | |
2756 | ||
2757 | ||
c356248b A |
2758 | # The only reason for this method is currently to have a reliable |
2759 | # debugging utility that reveals which output is going through which | |
2760 | # channel. No, I don't like the colors ;-) | |
8d97e4a1 | 2761 | |
8962fc49 SP |
2762 | # to turn colordebugging on, write |
2763 | # cpan> o conf colorize_output 1 | |
2764 | ||
2765 | #-> sub CPAN::Shell::print_ornamented ; | |
2766 | { | |
2767 | my $print_ornamented_have_warned = 0; | |
2768 | sub colorize_output { | |
2769 | my $colorize_output = $CPAN::Config->{colorize_output}; | |
2770 | if ($colorize_output && !$CPAN::META->has_inst("Term::ANSIColor")) { | |
2771 | unless ($print_ornamented_have_warned++) { | |
2772 | # no myprint/mywarn within myprint/mywarn! | |
2773 | warn "Colorize_output is set to true but Term::ANSIColor is not | |
2774 | installed. To activate colorized output, please install Term::ANSIColor.\n\n"; | |
2775 | } | |
2776 | $colorize_output = 0; | |
2777 | } | |
2778 | return $colorize_output; | |
2779 | } | |
2780 | } | |
2781 | ||
2782 | ||
05bab18e | 2783 | #-> sub CPAN::Shell::print_ornamented ; |
c356248b A |
2784 | sub print_ornamented { |
2785 | my($self,$what,$ornament) = @_; | |
8d97e4a1 | 2786 | return unless defined $what; |
c356248b | 2787 | |
554a9ef5 SP |
2788 | local $| = 1; # Flush immediately |
2789 | if ( $CPAN::Be_Silent ) { | |
2790 | print {report_fh()} $what; | |
2791 | return; | |
2792 | } | |
8962fc49 | 2793 | my $swhat = "$what"; # stringify if it is an object |
8d97e4a1 JH |
2794 | if ($CPAN::Config->{term_is_latin}){ |
2795 | # courtesy jhi: | |
8962fc49 | 2796 | $swhat |
8d97e4a1 JH |
2797 | =~ s{([\xC0-\xDF])([\x80-\xBF])}{chr(ord($1)<<6&0xC0|ord($2)&0x3F)}eg; #}; |
2798 | } | |
8962fc49 | 2799 | if ($self->colorize_output) { |
135a59c2 A |
2800 | if ( $CPAN::DEBUG && $swhat =~ /^Debug\(/ ) { |
2801 | # if you want to have this configurable, please file a bugreport | |
b72dd56f | 2802 | $ornament = $CPAN::Config->{colorize_debug} || "black on_cyan"; |
135a59c2 | 2803 | } |
8962fc49 SP |
2804 | my $color_on = eval { Term::ANSIColor::color($ornament) } || ""; |
2805 | if ($@) { | |
2806 | print "Term::ANSIColor rejects color[$ornament]: $@\n | |
f20de9f0 | 2807 | Please choose a different color (Hint: try 'o conf init /color/')\n"; |
8962fc49 | 2808 | } |
135a59c2 A |
2809 | print $color_on, |
2810 | $swhat, | |
2811 | Term::ANSIColor::color("reset"); | |
c356248b | 2812 | } else { |
8962fc49 | 2813 | print $swhat; |
c356248b A |
2814 | } |
2815 | } | |
2816 | ||
05bab18e SP |
2817 | #-> sub CPAN::Shell::myprint ; |
2818 | ||
8962fc49 SP |
2819 | # where is myprint/mywarn/Frontend/etc. documented? We need guidelines |
2820 | # where to use what! I think, we send everything to STDOUT and use | |
2821 | # print for normal/good news and warn for news that need more | |
2822 | # attention. Yes, this is our working contract for now. | |
c356248b A |
2823 | sub myprint { |
2824 | my($self,$what) = @_; | |
8d97e4a1 | 2825 | |
2ccf00a7 | 2826 | $self->print_ornamented($what, $CPAN::Config->{colorize_print}||'bold blue on_white'); |
c356248b A |
2827 | } |
2828 | ||
05bab18e | 2829 | #-> sub CPAN::Shell::myexit ; |
c356248b A |
2830 | sub myexit { |
2831 | my($self,$what) = @_; | |
2832 | $self->myprint($what); | |
2833 | exit; | |
2834 | } | |
2835 | ||
05bab18e | 2836 | #-> sub CPAN::Shell::mywarn ; |
c356248b A |
2837 | sub mywarn { |
2838 | my($self,$what) = @_; | |
2ccf00a7 | 2839 | $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white'); |
c356248b A |
2840 | } |
2841 | ||
b96578bb | 2842 | # only to be used for shell commands |
05bab18e | 2843 | #-> sub CPAN::Shell::mydie ; |
c356248b A |
2844 | sub mydie { |
2845 | my($self,$what) = @_; | |
2ccf00a7 | 2846 | $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white'); |
b96578bb SP |
2847 | |
2848 | # If it is the shell, we want that the following die to be silent, | |
2849 | # but if it is not the shell, we would need a 'die $what'. We need | |
2850 | # to take care that only shell commands use mydie. Is this | |
2851 | # possible? | |
2852 | ||
c356248b A |
2853 | die "\n"; |
2854 | } | |
2855 | ||
05bab18e | 2856 | # sub CPAN::Shell::colorable_makemaker_prompt ; |
8962fc49 SP |
2857 | sub colorable_makemaker_prompt { |
2858 | my($foo,$bar) = @_; | |
2859 | if (CPAN::Shell->colorize_output) { | |
2ccf00a7 | 2860 | my $ornament = $CPAN::Config->{colorize_print}||'bold blue on_white'; |
8962fc49 SP |
2861 | my $color_on = eval { Term::ANSIColor::color($ornament); } || ""; |
2862 | print $color_on; | |
2863 | } | |
2864 | my $ans = ExtUtils::MakeMaker::prompt($foo,$bar); | |
2865 | if (CPAN::Shell->colorize_output) { | |
2866 | print Term::ANSIColor::color('reset'); | |
2867 | } | |
2868 | return $ans; | |
2869 | } | |
2870 | ||
c9869e1c | 2871 | # use this only for unrecoverable errors! |
05bab18e | 2872 | #-> sub CPAN::Shell::unrecoverable_error ; |
c9869e1c SP |
2873 | sub unrecoverable_error { |
2874 | my($self,$what) = @_; | |
2875 | my @lines = split /\n/, $what; | |
2876 | my $longest = 0; | |
2877 | for my $l (@lines) { | |
2878 | $longest = length $l if length $l > $longest; | |
2879 | } | |
2880 | $longest = 62 if $longest > 62; | |
2881 | for my $l (@lines) { | |
2882 | if ($l =~ /^\s*$/){ | |
2883 | $l = "\n"; | |
2884 | next; | |
2885 | } | |
2886 | $l = "==> $l"; | |
2887 | if (length $l < 66) { | |
2888 | $l = pack "A66 A*", $l, "<=="; | |
2889 | } | |
2890 | $l .= "\n"; | |
2891 | } | |
2892 | unshift @lines, "\n"; | |
2893 | $self->mydie(join "", @lines); | |
c9869e1c SP |
2894 | } |
2895 | ||
05bab18e | 2896 | #-> sub CPAN::Shell::mysleep ; |
9ddc4ed0 A |
2897 | sub mysleep { |
2898 | my($self, $sleep) = @_; | |
f20de9f0 | 2899 | use Time::HiRes qw(sleep); |
9ddc4ed0 A |
2900 | sleep $sleep; |
2901 | } | |
2902 | ||
05bab18e | 2903 | #-> sub CPAN::Shell::setup_output ; |
911a92db GS |
2904 | sub setup_output { |
2905 | return if -t STDOUT; | |
2906 | my $odef = select STDERR; | |
2907 | $| = 1; | |
2908 | select STDOUT; | |
2909 | $| = 1; | |
2910 | select $odef; | |
2911 | } | |
2912 | ||
05454584 | 2913 | #-> sub CPAN::Shell::rematein ; |
810a0276 | 2914 | # RE-adme||MA-ke||TE-st||IN-stall : nearly everything runs through here |
05454584 | 2915 | sub rematein { |
0cf35e6a | 2916 | my $self = shift; |
05454584 | 2917 | my($meth,@some) = @_; |
554a9ef5 | 2918 | my @pragma; |
b72dd56f | 2919 | while($meth =~ /^(ff?orce|notest)$/) { |
554a9ef5 | 2920 | push @pragma, $meth; |
0cf35e6a SP |
2921 | $meth = shift @some or |
2922 | $CPAN::Frontend->mydie("Pragma $pragma[-1] used without method: ". | |
2923 | "cannot continue"); | |
05454584 | 2924 | } |
911a92db | 2925 | setup_output(); |
554a9ef5 | 2926 | CPAN->debug("pragma[@pragma]meth[$meth]some[@some]") if $CPAN::DEBUG; |
6d29edf5 JH |
2927 | |
2928 | # Here is the place to set "test_count" on all involved parties to | |
2929 | # 0. We then can pass this counter on to the involved | |
2930 | # distributions and those can refuse to test if test_count > X. In | |
2931 | # the first stab at it we could use a 1 for "X". | |
2932 | ||
2933 | # But when do I reset the distributions to start with 0 again? | |
2934 | # Jost suggested to have a random or cycling interaction ID that | |
2935 | # we pass through. But the ID is something that is just left lying | |
2936 | # around in addition to the counter, so I'd prefer to set the | |
2937 | # counter to 0 now, and repeat at the end of the loop. But what | |
2938 | # about dependencies? They appear later and are not reset, they | |
2939 | # enter the queue but not its copy. How do they get a sensible | |
2940 | # test_count? | |
2941 | ||
f20de9f0 SP |
2942 | my $needs_recursion_protection = "make|test|install"; |
2943 | ||
6d29edf5 JH |
2944 | # construct the queue |
2945 | my($s,@s,@qcopy); | |
0cf35e6a | 2946 | STHING: foreach $s (@some) { |
05454584 A |
2947 | my $obj; |
2948 | if (ref $s) { | |
6d29edf5 | 2949 | CPAN->debug("s is an object[$s]") if $CPAN::DEBUG; |
05454584 | 2950 | $obj = $s; |
7d97ad34 | 2951 | } elsif ($s =~ m|[\$\@\%]|) { # looks like a perl variable |
c4d24d4c | 2952 | } elsif ($s =~ m|^/|) { # looks like a regexp |
8fc516fe SP |
2953 | if (substr($s,-1,1) eq ".") { |
2954 | $obj = CPAN::Shell->expandany($s); | |
2955 | } else { | |
2956 | $CPAN::Frontend->mywarn("Sorry, $meth with a regular expression is ". | |
2957 | "not supported.\nRejecting argument '$s'\n"); | |
2958 | $CPAN::Frontend->mysleep(2); | |
2959 | next; | |
2960 | } | |
0cf35e6a | 2961 | } elsif ($meth eq "ls") { |
ca79d794 | 2962 | $self->globls($s,\@pragma); |
0cf35e6a SP |
2963 | next STHING; |
2964 | } else { | |
6d29edf5 JH |
2965 | CPAN->debug("calling expandany [$s]") if $CPAN::DEBUG; |
2966 | $obj = CPAN::Shell->expandany($s); | |
05454584 | 2967 | } |
7d97ad34 SP |
2968 | if (0) { |
2969 | } elsif (ref $obj) { | |
f20de9f0 SP |
2970 | if ($meth =~ /^($needs_recursion_protection)$/) { |
2971 | # silly for look or dump | |
2972 | $obj->color_cmd_tmps(0,1); | |
2973 | } | |
135a59c2 | 2974 | CPAN::Queue->new(qmod => $obj->id, reqtype => "c"); |
6d29edf5 | 2975 | push @qcopy, $obj; |
554a9ef5 SP |
2976 | } elsif ($CPAN::META->exists('CPAN::Author',uc($s))) { |
2977 | $obj = $CPAN::META->instance('CPAN::Author',uc($s)); | |
5fc0f0f6 JH |
2978 | if ($meth =~ /^(dump|ls)$/) { |
2979 | $obj->$meth(); | |
8d97e4a1 | 2980 | } else { |
8962fc49 SP |
2981 | $CPAN::Frontend->mywarn( |
2982 | join "", | |
2983 | "Don't be silly, you can't $meth ", | |
2984 | $obj->fullname, | |
2985 | " ;-)\n" | |
2986 | ); | |
2987 | $CPAN::Frontend->mysleep(2); | |
8d97e4a1 | 2988 | } |
7d97ad34 | 2989 | } elsif ($s =~ m|[\$\@\%]| && $meth eq "dump") { |
135a59c2 A |
2990 | CPAN::InfoObj->dump($s); |
2991 | } else { | |
f610777f | 2992 | $CPAN::Frontend |
8962fc49 | 2993 | ->mywarn(qq{Warning: Cannot $meth $s, }. |
135a59c2 | 2994 | qq{don't know what it is. |
e50380aa A |
2995 | Try the command |
2996 | ||
2997 | i /$s/ | |
2998 | ||
6d29edf5 | 2999 | to find objects with matching identifiers. |
c356248b | 3000 | }); |
8962fc49 | 3001 | $CPAN::Frontend->mysleep(2); |
6d29edf5 JH |
3002 | } |
3003 | } | |
3004 | ||
3005 | # queuerunner (please be warned: when I started to change the | |
3006 | # queue to hold objects instead of names, I made one or two | |
3007 | # mistakes and never found which. I reverted back instead) | |
135a59c2 | 3008 | while (my $q = CPAN::Queue->first) { |
6d29edf5 | 3009 | my $obj; |
135a59c2 A |
3010 | my $s = $q->as_string; |
3011 | my $reqtype = $q->reqtype || ""; | |
3012 | $obj = CPAN::Shell->expandany($s); | |
f20de9f0 SP |
3013 | unless ($obj) { |
3014 | # don't know how this can happen, maybe we should panic, | |
3015 | # but maybe we get a solution from the first user who hits | |
3016 | # this unfortunate exception? | |
3017 | $CPAN::Frontend->mywarn("Warning: Could not expand string '$s' ". | |
2b3bde2a | 3018 | "to an object. Skipping.\n"); |
f20de9f0 | 3019 | $CPAN::Frontend->mysleep(5); |
2b3bde2a | 3020 | CPAN::Queue->delete_first($s); |
f20de9f0 SP |
3021 | next; |
3022 | } | |
135a59c2 | 3023 | $obj->{reqtype} ||= ""; |
810a0276 SP |
3024 | { |
3025 | # force debugging because CPAN::SQLite somehow delivers us | |
3026 | # an empty object; | |
3027 | ||
3028 | # local $CPAN::DEBUG = 1024; # Shell; probably fixed now | |
3029 | ||
3030 | CPAN->debug("s[$s]obj-reqtype[$obj->{reqtype}]". | |
3031 | "q-reqtype[$reqtype]") if $CPAN::DEBUG; | |
3032 | } | |
135a59c2 A |
3033 | if ($obj->{reqtype}) { |
3034 | if ($obj->{reqtype} eq "b" && $reqtype =~ /^[rc]$/) { | |
3035 | $obj->{reqtype} = $reqtype; | |
3036 | if ( | |
3037 | exists $obj->{install} | |
3038 | && | |
3039 | ( | |
be34b10d | 3040 | UNIVERSAL::can($obj->{install},"failed") ? |
135a59c2 A |
3041 | $obj->{install}->failed : |
3042 | $obj->{install} =~ /^NO/ | |
3043 | ) | |
3044 | ) { | |
3045 | delete $obj->{install}; | |
3046 | $CPAN::Frontend->mywarn | |
3047 | ("Promoting $obj->{ID} from 'build_requires' to 'requires'"); | |
3048 | } | |
3049 | } | |
3050 | } else { | |
3051 | $obj->{reqtype} = $reqtype; | |
3052 | } | |
3053 | ||
554a9ef5 SP |
3054 | for my $pragma (@pragma) { |
3055 | if ($pragma | |
3056 | && | |
05bab18e SP |
3057 | $obj->can($pragma)){ |
3058 | $obj->$pragma($meth); | |
554a9ef5 | 3059 | } |
6d29edf5 | 3060 | } |
810a0276 | 3061 | if (UNIVERSAL::can($obj, 'called_for')) { |
6d29edf5 JH |
3062 | $obj->called_for($s); |
3063 | } | |
135a59c2 A |
3064 | CPAN->debug(qq{pragma[@pragma]meth[$meth]}. |
3065 | qq{ID[$obj->{ID}]}) if $CPAN::DEBUG; | |
6d29edf5 | 3066 | |
6a935156 | 3067 | push @qcopy, $obj; |
810a0276 SP |
3068 | if (! UNIVERSAL::can($obj,$meth)) { |
3069 | # Must never happen | |
3070 | my $serialized = ""; | |
3071 | if (0) { | |
3072 | } elsif ($CPAN::META->has_inst("YAML::Syck")) { | |
3073 | $serialized = YAML::Syck::Dump($obj); | |
3074 | } elsif ($CPAN::META->has_inst("YAML")) { | |
3075 | $serialized = YAML::Dump($obj); | |
3076 | } elsif ($CPAN::META->has_inst("Data::Dumper")) { | |
3077 | $serialized = Data::Dumper::Dumper($obj); | |
3078 | } else { | |
3079 | require overload; | |
3080 | $serialized = overload::StrVal($obj); | |
3081 | } | |
3082 | $CPAN::Frontend->mydie("Panic: obj[$serialized] cannot meth[$meth]"); | |
3083 | } elsif ($obj->$meth()){ | |
6d29edf5 JH |
3084 | CPAN::Queue->delete($s); |
3085 | } else { | |
3086 | CPAN->debug("failed"); | |
3087 | } | |
3088 | ||
3089 | $obj->undelay; | |
05bab18e SP |
3090 | for my $pragma (@pragma) { |
3091 | my $unpragma = "un$pragma"; | |
3092 | if ($obj->can($unpragma)) { | |
3093 | $obj->$unpragma(); | |
3094 | } | |
3095 | } | |
f610777f | 3096 | CPAN::Queue->delete_first($s); |
05454584 | 3097 | } |
f20de9f0 SP |
3098 | if ($meth =~ /^($needs_recursion_protection)$/) { |
3099 | for my $obj (@qcopy) { | |
3100 | $obj->color_cmd_tmps(0,0); | |
3101 | } | |
6d29edf5 | 3102 | } |
05454584 A |
3103 | } |
3104 | ||
554a9ef5 SP |
3105 | #-> sub CPAN::Shell::recent ; |
3106 | sub recent { | |
f3fe0ae6 | 3107 | my($self) = @_; |
554a9ef5 SP |
3108 | |
3109 | CPAN::Distribution::_display_url( $self, $CPAN::Defaultrecent ); | |
3110 | return; | |
3111 | } | |
3112 | ||
3113 | { | |
3114 | # set up the dispatching methods | |
3115 | no strict "refs"; | |
3116 | for my $command (qw( | |
0cf35e6a SP |
3117 | clean |
3118 | cvs_import | |
3119 | dump | |
3120 | force | |
b72dd56f | 3121 | fforce |
0cf35e6a SP |
3122 | get |
3123 | install | |
3124 | look | |
3125 | ls | |
3126 | make | |
3127 | notest | |
3128 | perldoc | |
3129 | readme | |
3130 | test | |
554a9ef5 SP |
3131 | )) { |
3132 | *$command = sub { shift->rematein($command, @_); }; | |
3133 | } | |
3134 | } | |
05454584 | 3135 | |
c049f953 | 3136 | package CPAN::LWP::UserAgent; |
e82b9348 | 3137 | use strict; |
c049f953 JH |
3138 | |
3139 | sub config { | |
3140 | return if $SETUPDONE; | |
3141 | if ($CPAN::META->has_usable('LWP::UserAgent')) { | |
3142 | require LWP::UserAgent; | |
3143 | @ISA = qw(Exporter LWP::UserAgent); | |
3144 | $SETUPDONE++; | |
3145 | } else { | |
8962fc49 | 3146 | $CPAN::Frontend->mywarn(" LWP::UserAgent not available\n"); |
c049f953 JH |
3147 | } |
3148 | } | |
3149 | ||
3150 | sub get_basic_credentials { | |
3151 | my($self, $realm, $uri, $proxy) = @_; | |
c049f953 | 3152 | if ($USER && $PASSWD) { |
ed84aac9 A |
3153 | return ($USER, $PASSWD); |
3154 | } | |
3155 | if ( $proxy ) { | |
3156 | ($USER,$PASSWD) = $self->get_proxy_credentials(); | |
c049f953 | 3157 | } else { |
ed84aac9 A |
3158 | ($USER,$PASSWD) = $self->get_non_proxy_credentials(); |
3159 | } | |
3160 | return($USER,$PASSWD); | |
3161 | } | |
3162 | ||
3163 | sub get_proxy_credentials { | |
3164 | my $self = shift; | |
3165 | my ($user, $password); | |
3166 | if ( defined $CPAN::Config->{proxy_user} && | |
3167 | defined $CPAN::Config->{proxy_pass}) { | |
3168 | $user = $CPAN::Config->{proxy_user}; | |
3169 | $password = $CPAN::Config->{proxy_pass}; | |
3170 | return ($user, $password); | |
3171 | } | |
3172 | my $username_prompt = "\nProxy authentication needed! | |
c049f953 JH |
3173 | (Note: to permanently configure username and password run |
3174 | o conf proxy_user your_username | |
3175 | o conf proxy_pass your_password | |
ed84aac9 A |
3176 | )\nUsername:"; |
3177 | ($user, $password) = | |
3178 | _get_username_and_password_from_user($username_prompt); | |
3179 | return ($user,$password); | |
3180 | } | |
3181 | ||
3182 | sub get_non_proxy_credentials { | |
3183 | my $self = shift; | |
3184 | my ($user,$password); | |
3185 | if ( defined $CPAN::Config->{username} && | |
3186 | defined $CPAN::Config->{password}) { | |
3187 | $user = $CPAN::Config->{username}; | |
3188 | $password = $CPAN::Config->{password}; | |
3189 | return ($user, $password); | |
3190 | } | |
3191 | my $username_prompt = "\nAuthentication needed! | |
3192 | (Note: to permanently configure username and password run | |
3193 | o conf username your_username | |
3194 | o conf password your_password | |
3195 | )\nUsername:"; | |
8962fc49 | 3196 | |
ed84aac9 A |
3197 | ($user, $password) = |
3198 | _get_username_and_password_from_user($username_prompt); | |
3199 | return ($user,$password); | |
3200 | } | |
3201 | ||
3202 | sub _get_username_and_password_from_user { | |
ed84aac9 A |
3203 | my $username_message = shift; |
3204 | my ($username,$password); | |
3205 | ||
3206 | ExtUtils::MakeMaker->import(qw(prompt)); | |
3207 | $username = prompt($username_message); | |
c049f953 JH |
3208 | if ($CPAN::META->has_inst("Term::ReadKey")) { |
3209 | Term::ReadKey::ReadMode("noecho"); | |
c049f953 | 3210 | } |
ed84aac9 A |
3211 | else { |
3212 | $CPAN::Frontend->mywarn( | |
3213 | "Warning: Term::ReadKey seems not to be available, your password will be echoed to the terminal!\n" | |
3214 | ); | |
3215 | } | |
3216 | $password = prompt("Password:"); | |
3217 | ||
c049f953 JH |
3218 | if ($CPAN::META->has_inst("Term::ReadKey")) { |
3219 | Term::ReadKey::ReadMode("restore"); | |
3220 | } | |
3221 | $CPAN::Frontend->myprint("\n\n"); | |
ed84aac9 | 3222 | return ($username,$password); |
c049f953 JH |
3223 | } |
3224 | ||
1426a145 JH |
3225 | # mirror(): Its purpose is to deal with proxy authentication. When we |
3226 | # call SUPER::mirror, we relly call the mirror method in | |
3227 | # LWP::UserAgent. LWP::UserAgent will then call | |
3228 | # $self->get_basic_credentials or some equivalent and this will be | |
3229 | # $self->dispatched to our own get_basic_credentials method. | |
3230 | ||
3231 | # Our own get_basic_credentials sets $USER and $PASSWD, two globals. | |
3232 | ||
3233 | # 407 stands for HTTP_PROXY_AUTHENTICATION_REQUIRED. Which means | |
3234 | # although we have gone through our get_basic_credentials, the proxy | |
3235 | # server refuses to connect. This could be a case where the username or | |
3236 | # password has changed in the meantime, so I'm trying once again without | |
3237 | # $USER and $PASSWD to give the get_basic_credentials routine another | |
3238 | # chance to set $USER and $PASSWD. | |
3239 | ||
554a9ef5 SP |
3240 | # mirror(): Its purpose is to deal with proxy authentication. When we |
3241 | # call SUPER::mirror, we relly call the mirror method in | |
3242 | # LWP::UserAgent. LWP::UserAgent will then call | |
3243 | # $self->get_basic_credentials or some equivalent and this will be | |
3244 | # $self->dispatched to our own get_basic_credentials method. | |
3245 | ||
3246 | # Our own get_basic_credentials sets $USER and $PASSWD, two globals. | |
3247 | ||
3248 | # 407 stands for HTTP_PROXY_AUTHENTICATION_REQUIRED. Which means | |
3249 | # although we have gone through our get_basic_credentials, the proxy | |
3250 | # server refuses to connect. This could be a case where the username or | |
3251 | # password has changed in the meantime, so I'm trying once again without | |
3252 | # $USER and $PASSWD to give the get_basic_credentials routine another | |
3253 | # chance to set $USER and $PASSWD. | |
3254 | ||
c049f953 JH |
3255 | sub mirror { |
3256 | my($self,$url,$aslocal) = @_; | |
3257 | my $result = $self->SUPER::mirror($url,$aslocal); | |
3258 | if ($result->code == 407) { | |
3259 | undef $USER; | |
3260 | undef $PASSWD; | |
3261 | $result = $self->SUPER::mirror($url,$aslocal); | |
3262 | } | |
3263 | $result; | |
3264 | } | |
3265 | ||
05454584 | 3266 | package CPAN::FTP; |
e82b9348 | 3267 | use strict; |
05454584 | 3268 | |
05bab18e SP |
3269 | #-> sub CPAN::FTP::ftp_statistics |
3270 | # if they want to rewrite, they need to pass in a filehandle | |
3271 | sub _ftp_statistics { | |
3272 | my($self,$fh) = @_; | |
3273 | my $locktype = $fh ? LOCK_EX : LOCK_SH; | |
3274 | $fh ||= FileHandle->new; | |
3275 | my $file = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); | |
3276 | open $fh, "+>>$file" or $CPAN::Frontend->mydie("Could not open '$file': $!"); | |
3277 | my $sleep = 1; | |
810a0276 | 3278 | my $waitstart; |
05bab18e | 3279 | while (!flock $fh, $locktype|LOCK_NB) { |
810a0276 | 3280 | $waitstart ||= localtime(); |
05bab18e | 3281 | if ($sleep>3) { |
810a0276 | 3282 | $CPAN::Frontend->mywarn("Waiting for a read lock on '$file' (since $waitstart)\n"); |
be34b10d SP |
3283 | } |
3284 | $CPAN::Frontend->mysleep($sleep); | |
3285 | if ($sleep <= 3) { | |
3286 | $sleep+=0.33; | |
810a0276 SP |
3287 | } elsif ($sleep <=6) { |
3288 | $sleep+=0.11; | |
05bab18e | 3289 | } |
05bab18e | 3290 | } |
b72dd56f SP |
3291 | my $stats = eval { CPAN->_yaml_loadfile($file); }; |
3292 | if ($@) { | |
3293 | if (ref $@) { | |
3294 | if (ref $@ eq "CPAN::Exception::yaml_not_installed") { | |
3295 | $CPAN::Frontend->myprint("Warning (usually harmless): $@"); | |
3296 | return; | |
3297 | } elsif (ref $@ eq "CPAN::Exception::yaml_process_error") { | |
3298 | $CPAN::Frontend->mydie($@); | |
3299 | } | |
3300 | } else { | |
3301 | $CPAN::Frontend->mydie($@); | |
3302 | } | |
3303 | } | |
05bab18e SP |
3304 | return $stats->[0]; |
3305 | } | |
3306 | ||
810a0276 | 3307 | #-> sub CPAN::FTP::_mytime |
05bab18e SP |
3308 | sub _mytime () { |
3309 | if (CPAN->has_inst("Time::HiRes")) { | |
3310 | return Time::HiRes::time(); | |
3311 | } else { | |
3312 | return time; | |
3313 | } | |
3314 | } | |
3315 | ||
810a0276 | 3316 | #-> sub CPAN::FTP::_new_stats |
05bab18e SP |
3317 | sub _new_stats { |
3318 | my($self,$file) = @_; | |
3319 | my $ret = { | |
3320 | file => $file, | |
3321 | attempts => [], | |
3322 | start => _mytime, | |
3323 | }; | |
3324 | $ret; | |
3325 | } | |
3326 | ||
810a0276 | 3327 | #-> sub CPAN::FTP::_add_to_statistics |
05bab18e SP |
3328 | sub _add_to_statistics { |
3329 | my($self,$stats) = @_; | |
b72dd56f | 3330 | my $yaml_module = CPAN::_yaml_module; |
f20de9f0 | 3331 | $self->debug("yaml_module[$yaml_module]") if $CPAN::DEBUG; |
810a0276 SP |
3332 | if ($CPAN::META->has_inst($yaml_module)) { |
3333 | $stats->{thesiteurl} = $ThesiteURL; | |
3334 | if (CPAN->has_inst("Time::HiRes")) { | |
3335 | $stats->{end} = Time::HiRes::time(); | |
3336 | } else { | |
3337 | $stats->{end} = time; | |
3338 | } | |
3339 | my $fh = FileHandle->new; | |
b72dd56f SP |
3340 | my $time = time; |
3341 | my $sdebug = 0; | |
3342 | my @debug; | |
3343 | @debug = $time if $sdebug; | |
810a0276 | 3344 | my $fullstats = $self->_ftp_statistics($fh); |
b72dd56f | 3345 | close $fh; |
810a0276 | 3346 | $fullstats->{history} ||= []; |
b72dd56f SP |
3347 | push @debug, scalar @{$fullstats->{history}} if $sdebug; |
3348 | push @debug, time if $sdebug; | |
810a0276 | 3349 | push @{$fullstats->{history}}, $stats; |
b72dd56f | 3350 | # arbitrary hardcoded constants until somebody demands to have |
ed756621 SP |
3351 | # them settable; YAML.pm 0.62 is unacceptably slow with 999; |
3352 | # YAML::Syck 0.82 has no noticable performance problem with 999; | |
b72dd56f | 3353 | while ( |
ed756621 SP |
3354 | @{$fullstats->{history}} > 99 |
3355 | || $time - $fullstats->{history}[0]{start} > 14*86400 | |
b72dd56f SP |
3356 | ) { |
3357 | shift @{$fullstats->{history}} | |
3358 | } | |
3359 | push @debug, scalar @{$fullstats->{history}} if $sdebug; | |
3360 | push @debug, time if $sdebug; | |
3361 | push @debug, scalar localtime($fullstats->{history}[0]{start}) if $sdebug; | |
3362 | # need no eval because if this fails, it is serious | |
3363 | my $sfile = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); | |
3364 | CPAN->_yaml_dumpfile("$sfile.$$",$fullstats); | |
3365 | if ( $sdebug||$CPAN::DEBUG ) { | |
3366 | local $CPAN::DEBUG = 512; # FTP | |
3367 | push @debug, time; | |
3368 | CPAN->debug(sprintf("DEBUG history: before_read[%d]before[%d]at[%d]". | |
3369 | "after[%d]at[%d]oldest[%s]dumped backat[%d]", | |
810a0276 | 3370 | @debug, |
b72dd56f | 3371 | )); |
810a0276 | 3372 | } |
b72dd56f SP |
3373 | # Win32 cannot rename a file to an existing filename |
3374 | unlink($sfile) if ($^O eq 'MSWin32'); | |
3375 | rename "$sfile.$$", $sfile | |
3376 | or $CPAN::Frontend->mydie("Could not rename '$sfile.$$' to '$sfile': $!\n"); | |
05bab18e | 3377 | } |
05bab18e SP |
3378 | } |
3379 | ||
3380 | # if file is CHECKSUMS, suggest the place where we got the file to be | |
3381 | # checked from, maybe only for young files? | |
810a0276 | 3382 | #-> sub CPAN::FTP::_recommend_url_for |
05bab18e SP |
3383 | sub _recommend_url_for { |
3384 | my($self, $file) = @_; | |
3385 | my $urllist = $self->_get_urllist; | |
3386 | if ($file =~ s|/CHECKSUMS(.gz)?$||) { | |
3387 | my $fullstats = $self->_ftp_statistics(); | |
3388 | my $history = $fullstats->{history} || []; | |
3389 | while (my $last = pop @$history) { | |
3390 | last if $last->{end} - time > 3600; # only young results are interesting | |
be34b10d | 3391 | next unless $last->{file}; # dirname of nothing dies! |
05bab18e SP |
3392 | next unless $file eq File::Basename::dirname($last->{file}); |
3393 | return $last->{thesiteurl}; | |
3394 | } | |
3395 | } | |
3396 | if ($CPAN::Config->{randomize_urllist} | |
3397 | && | |
3398 | rand(1) < $CPAN::Config->{randomize_urllist} | |
3399 | ) { | |
3400 | $urllist->[int rand scalar @$urllist]; | |
3401 | } else { | |
3402 | return (); | |
3403 | } | |
3404 | } | |
3405 | ||
810a0276 | 3406 | #-> sub CPAN::FTP::_get_urllist |
05bab18e SP |
3407 | sub _get_urllist { |
3408 | my($self) = @_; | |
3409 | $CPAN::Config->{urllist} ||= []; | |
3410 | unless (ref $CPAN::Config->{urllist} eq 'ARRAY') { | |
3411 | $CPAN::Frontend->mywarn("Malformed urllist; ignoring. Configuration file corrupt?\n"); | |
3412 | $CPAN::Config->{urllist} = []; | |
3413 | } | |
3414 | my @urllist = grep { defined $_ and length $_ } @{$CPAN::Config->{urllist}}; | |
3415 | for my $u (@urllist) { | |
3416 | CPAN->debug("u[$u]") if $CPAN::DEBUG; | |
3417 | if (UNIVERSAL::can($u,"text")) { | |
3418 | $u->{TEXT} .= "/" unless substr($u->{TEXT},-1) eq "/"; | |
3419 | } else { | |
3420 | $u .= "/" unless substr($u,-1) eq "/"; | |
3421 | $u = CPAN::URL->new(TEXT => $u, FROM => "USER"); | |
3422 | } | |
3423 | } | |
3424 | \@urllist; | |
3425 | } | |
3426 | ||
05454584 A |
3427 | #-> sub CPAN::FTP::ftp_get ; |
3428 | sub ftp_get { | |
9ddc4ed0 A |
3429 | my($class,$host,$dir,$file,$target) = @_; |
3430 | $class->debug( | |
3431 | qq[Going to fetch file [$file] from dir [$dir] | |
05454584 | 3432 | on host [$host] as local [$target]\n] |
9ddc4ed0 A |
3433 | ) if $CPAN::DEBUG; |
3434 | my $ftp = Net::FTP->new($host); | |
3435 | unless ($ftp) { | |
3436 | $CPAN::Frontend->mywarn(" Could not connect to host '$host' with Net::FTP\n"); | |
3437 | return; | |
3438 | } | |
3439 | return 0 unless defined $ftp; | |
3440 | $ftp->debug(1) if $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG; | |
3441 | $class->debug(qq[Going to login("anonymous","$Config::Config{cf_email}")]); | |
3442 | unless ( $ftp->login("anonymous",$Config::Config{'cf_email'}) ){ | |
3443 | my $msg = $ftp->message; | |
3444 | $CPAN::Frontend->mywarn(" Couldn't login on $host: $msg"); | |
3445 | return; | |
3446 | } | |
3447 | unless ( $ftp->cwd($dir) ){ | |
3448 | my $msg = $ftp->message; | |
3449 | $CPAN::Frontend->mywarn(" Couldn't cwd $dir: $msg"); | |
3450 | return; | |
3451 | } | |
3452 | $ftp->binary; | |
3453 | $class->debug(qq[Going to ->get("$file","$target")\n]) if $CPAN::DEBUG; | |
3454 | unless ( $ftp->get($file,$target) ){ | |
3455 | my $msg = $ftp->message; | |
3456 | $CPAN::Frontend->mywarn(" Couldn't fetch $file from $host: $msg"); | |
3457 | return; | |
3458 | } | |
3459 | $ftp->quit; # it's ok if this fails | |
3460 | return 1; | |
05454584 A |
3461 | } |
3462 | ||
09d9d230 | 3463 | # If more accuracy is wanted/needed, Chris Leach sent me this patch... |
f610777f | 3464 | |
6d29edf5 JH |
3465 | # > *** /install/perl/live/lib/CPAN.pm- Wed Sep 24 13:08:48 1997 |
3466 | # > --- /tmp/cp Wed Sep 24 13:26:40 1997 | |
3467 | # > *************** | |
3468 | # > *** 1562,1567 **** | |
3469 | # > --- 1562,1580 ---- | |
3470 | # > return 1 if substr($url,0,4) eq "file"; | |
3471 | # > return 1 unless $url =~ m|://([^/]+)|; | |
3472 | # > my $host = $1; | |
3473 | # > + my $proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'}; | |
3474 | # > + if ($proxy) { | |
3475 | # > + $proxy =~ m|://([^/:]+)|; | |
3476 | # > + $proxy = $1; | |
3477 | # > + my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'}; | |
3478 | # > + if ($noproxy) { | |
3479 | # > + if ($host !~ /$noproxy$/) { | |
3480 | # > + $host = $proxy; | |
3481 | # > + } | |
3482 | # > + } else { | |
3483 | # > + $host = $proxy; | |
3484 | # > + } | |
3485 | # > + } | |
3486 | # > require Net::Ping; | |
3487 | # > return 1 unless $Net::Ping::VERSION >= 2; | |
3488 | # > my $p; | |
09d9d230 A |
3489 | |
3490 | ||
05454584 A |
3491 | #-> sub CPAN::FTP::localize ; |
3492 | sub localize { | |
3493 | my($self,$file,$aslocal,$force) = @_; | |
3494 | $force ||= 0; | |
3495 | Carp::croak "Usage: ->localize(cpan_file,as_local_file[,$force])" | |
3496 | unless defined $aslocal; | |
55e314ee A |
3497 | $self->debug("file[$file] aslocal[$aslocal] force[$force]") |
3498 | if $CPAN::DEBUG; | |
05454584 | 3499 | |
f14b5cec | 3500 | if ($^O eq 'MacOS') { |
6d29edf5 JH |
3501 | # Comment by AK on 2000-09-03: Uniq short filenames would be |
3502 | # available in CHECKSUMS file | |
f14b5cec JH |
3503 | my($name, $path) = File::Basename::fileparse($aslocal, ''); |
3504 | if (length($name) > 31) { | |
6d29edf5 JH |
3505 | $name =~ s/( |
3506 | \.( | |
3507 | readme(\.(gz|Z))? | | |
3508 | (tar\.)?(gz|Z) | | |
3509 | tgz | | |
3510 | zip | | |
3511 | pm\.(gz|Z) | |
3512 | ) | |
3513 | )$//x; | |
f14b5cec JH |
3514 | my $suf = $1; |
3515 | my $size = 31 - length($suf); | |
3516 | while (length($name) > $size) { | |
3517 | chop $name; | |
3518 | } | |
3519 | $name .= $suf; | |
3520 | $aslocal = File::Spec->catfile($path, $name); | |
3521 | } | |
3522 | } | |
3523 | ||
0cf35e6a | 3524 | if (-f $aslocal && -r _ && !($force & 1)){ |
b96578bb SP |
3525 | my $size; |
3526 | if ($size = -s $aslocal) { | |
3527 | $self->debug("aslocal[$aslocal]size[$size]") if $CPAN::DEBUG; | |
3528 | return $aslocal; | |
3529 | } else { | |
3530 | # empty file from a previous unsuccessful attempt to download it | |
3531 | unlink $aslocal or | |
ed84aac9 A |
3532 | $CPAN::Frontend->mydie("Found a zero-length '$aslocal' that I ". |
3533 | "could not remove."); | |
b96578bb | 3534 | } |
0cf35e6a | 3535 | } |
05bab18e | 3536 | my($maybe_restore) = 0; |
55e314ee | 3537 | if (-f $aslocal){ |
05bab18e SP |
3538 | rename $aslocal, "$aslocal.bak$$"; |
3539 | $maybe_restore++; | |
55e314ee | 3540 | } |
05454584 A |
3541 | |
3542 | my($aslocal_dir) = File::Basename::dirname($aslocal); | |
3543 | File::Path::mkpath($aslocal_dir); | |
c356248b | 3544 | $CPAN::Frontend->mywarn(qq{Warning: You are not allowed to write into }. |
05454584 | 3545 | qq{directory "$aslocal_dir". |
c356248b A |
3546 | I\'ll continue, but if you encounter problems, they may be due |
3547 | to insufficient permissions.\n}) unless -w $aslocal_dir; | |
05454584 A |
3548 | |
3549 | # Inheritance is not easier to manage than a few if/else branches | |
de34a54b | 3550 | if ($CPAN::META->has_usable('LWP::UserAgent')) { |
05454584 | 3551 | unless ($Ua) { |
c049f953 JH |
3552 | CPAN::LWP::UserAgent->config; |
3553 | eval {$Ua = CPAN::LWP::UserAgent->new;}; # Why is has_usable still not fit enough? | |
d8773709 | 3554 | if ($@) { |
5fc0f0f6 | 3555 | $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n") |
d8773709 JH |
3556 | if $CPAN::DEBUG; |
3557 | } else { | |
3558 | my($var); | |
3559 | $Ua->proxy('ftp', $var) | |
3560 | if $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy}; | |
3561 | $Ua->proxy('http', $var) | |
3562 | if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy}; | |
c049f953 JH |
3563 | |
3564 | ||
3565 | # >>>>> On Wed, 13 Dec 2000 09:21:34 -0500, "Robison, Jonathon (J.M.)" <jrobiso2@visteon.com> said: | |
3566 | # | |
3567 | # > I note that although CPAN.pm can use proxies, it doesn't seem equipped to | |
3568 | # > use ones that require basic autorization. | |
3569 | # | |
3570 | # > Example of when I use it manually in my own stuff: | |
3571 | # | |
3572 | # > $ua->proxy(['http','ftp'], http://my.proxy.server:83'); | |
3573 | # > $req->proxy_authorization_basic("username","password"); | |
3574 | # > $res = $ua->request($req); | |
3575 | # | |
3576 | ||
d8773709 JH |
3577 | $Ua->no_proxy($var) |
3578 | if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy}; | |
3579 | } | |
05454584 A |
3580 | } |
3581 | } | |
35576f8c A |
3582 | for my $prx (qw(ftp_proxy http_proxy no_proxy)) { |
3583 | $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; | |
3584 | } | |
05454584 A |
3585 | |
3586 | # Try the list of urls for each single object. We keep a record | |
3587 | # where we did get a file from | |
c356248b | 3588 | my(@reordered,$last); |
05bab18e SP |
3589 | my $ccurllist = $self->_get_urllist; |
3590 | $last = $#$ccurllist; | |
c356248b A |
3591 | if ($force & 2) { # local cpans probably out of date, don't reorder |
3592 | @reordered = (0..$last); | |
3593 | } else { | |
3594 | @reordered = | |
3595 | sort { | |
05bab18e | 3596 | (substr($ccurllist->[$b],0,4) eq "file") |
f610777f | 3597 | <=> |
05bab18e | 3598 | (substr($ccurllist->[$a],0,4) eq "file") |
c356248b | 3599 | or |
44d21104 | 3600 | defined($ThesiteURL) |
c356248b | 3601 | and |
05bab18e | 3602 | ($ccurllist->[$b] eq $ThesiteURL) |
c356248b | 3603 | <=> |
05bab18e | 3604 | ($ccurllist->[$a] eq $ThesiteURL) |
c356248b | 3605 | } 0..$last; |
c356248b | 3606 | } |
c4d24d4c | 3607 | my(@levels); |
7fefbd44 | 3608 | $Themethod ||= ""; |
05bab18e | 3609 | $self->debug("Themethod[$Themethod]reordered[@reordered]") if $CPAN::DEBUG; |
c356248b A |
3610 | if ($Themethod) { |
3611 | @levels = ($Themethod, grep {$_ ne $Themethod} qw/easy hard hardest/); | |
3612 | } else { | |
3613 | @levels = qw/easy hard hardest/; | |
3614 | } | |
f14b5cec |