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