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