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