[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 # -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- 2 use strict; 3 package CPAN; 4 $CPAN::VERSION = '1.9205'; 5 $CPAN::VERSION = eval $CPAN::VERSION if $CPAN::VERSION =~ /_/; 6 7 use CPAN::HandleConfig; 8 use CPAN::Version; 9 use CPAN::Debug; 10 use CPAN::Queue; 11 use CPAN::Tarzip; 12 use CPAN::DeferedCode; 13 use Carp (); 14 use Config (); 15 use Cwd (); 16 use DirHandle (); 17 use Exporter (); 18 use ExtUtils::MakeMaker qw(prompt); # for some unknown reason, 19 # 5.005_04 does not work without 20 # this 21 use File::Basename (); 22 use File::Copy (); 23 use File::Find; 24 use File::Path (); 25 use File::Spec (); 26 use FileHandle (); 27 use Fcntl qw(:flock); 28 use Safe (); 29 use Sys::Hostname qw(hostname); 30 use Text::ParseWords (); 31 use Text::Wrap (); 32 33 sub find_perl (); 34 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) { 40 $inc = File::Spec->rel2abs($inc) unless ref $inc; 41 } 42 } 43 } 44 no lib "."; 45 46 require Mac::BuildTools if $^O eq 'MacOS'; 47 $ENV{PERL5_CPAN_IS_RUNNING}=$$; 48 $ENV{PERL5_CPANPLUS_IS_RUNNING}=$$; # https://rt.cpan.org/Ticket/Display.html?id=23735 49 50 END { $CPAN::End++; &cleanup; } 51 52 $CPAN::Signal ||= 0; 53 $CPAN::Frontend ||= "CPAN::Shell"; 54 unless (@CPAN::Defaultsites) { 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 } 61 # $CPAN::iCwd (i for initial) is going to be initialized during find_perl 62 $CPAN::Perl ||= CPAN::find_perl(); 63 $CPAN::Defaultdocs ||= "http://search.cpan.org/perldoc?"; 64 $CPAN::Defaultrecent ||= "http://search.cpan.org/uploads.rdf"; 65 $CPAN::Defaultrecent ||= "http://cpan.uwinnipeg.ca/htdocs/cpan.xml"; 66 67 # our globals are getting a mess 68 use vars qw( 69 $AUTOLOAD 70 $Be_Silent 71 $CONFIG_DIRTY 72 $Defaultdocs 73 $Echo_readline 74 $Frontend 75 $GOTOSHELL 76 $HAS_USABLE 77 $Have_warned 78 $MAX_RECURSION 79 $META 80 $RUN_DEGRADED 81 $Signal 82 $SQLite 83 $Suppress_readline 84 $VERSION 85 $autoload_recursion 86 $term 87 @Defaultsites 88 @EXPORT 89 ); 90 91 $MAX_RECURSION = 32; 92 93 @CPAN::ISA = qw(CPAN::Debug Exporter); 94 95 # note that these functions live in CPAN::Shell and get executed via 96 # AUTOLOAD when called directly 97 @EXPORT = qw( 98 autobundle 99 bundle 100 clean 101 cvs_import 102 expand 103 force 104 fforce 105 get 106 install 107 install_tested 108 is_tested 109 make 110 mkmyconfig 111 notest 112 perldoc 113 readme 114 recent 115 recompile 116 report 117 shell 118 smoke 119 test 120 upgrade 121 ); 122 123 sub soft_chdir_with_alternatives ($); 124 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++; 147 if (exists $export{$l}) { 148 CPAN::Shell->$l(@_); 149 } else { 150 die(qq{Unknown CPAN command "$AUTOLOAD". }. 151 qq{Type ? for help.\n}); 152 } 153 $autoload_recursion--; 154 } 155 } 156 157 #-> sub CPAN::shell ; 158 sub shell { 159 my($self) = @_; 160 $Suppress_readline = ! -t STDIN unless defined $Suppress_readline; 161 CPAN::HandleConfig->load unless $CPAN::Config_loaded++; 162 163 my $oprompt = shift || CPAN::Prompt->new; 164 my $prompt = $oprompt; 165 my $commandline = shift || ""; 166 $CPAN::CurrentCommandId ||= 1; 167 168 local($^W) = 1; 169 unless ($Suppress_readline) { 170 require Term::ReadLine; 171 if (! $term 172 or 173 $term->ReadLine eq "Term::ReadLine::Stub" 174 ) { 175 $term = Term::ReadLine->new('CPAN Monitor'); 176 } 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 } 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 } 191 $META->readhist($term,$histfile); 192 }} 193 for ($CPAN::Config->{term_ornaments}) { # alias 194 local $Term::ReadLine::termcap_nowarn = 1; 195 $term->ornaments($_) if defined; 196 } 197 # $term->OUT is autoflushed anyway 198 my $odef = select STDERR; 199 $| = 1; 200 select STDOUT; 201 $| = 1; 202 select $odef; 203 } 204 205 $META->checklock(); 206 my @cwd = grep { defined $_ and length $_ } 207 CPAN::anycwd(), 208 File::Spec->can("tmpdir") ? File::Spec->tmpdir() : (), 209 File::Spec->rootdir(); 210 my $try_detect_readline; 211 $try_detect_readline = $term->ReadLine eq "Term::ReadLine::Stub" if $term; 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?)"; 216 $CPAN::Frontend->myprint( 217 sprintf qq{ 218 cpan shell -- CPAN exploration and modules installation (v%s) 219 ReadLine support %s 220 221 }, 222 $CPAN::VERSION, 223 $rl_avail 224 ) 225 } 226 my($continuation) = ""; 227 my $last_term_ornaments; 228 SHELLCOMMAND: while () { 229 if ($Suppress_readline) { 230 if ($Echo_readline) { 231 $|=1; 232 } 233 print $prompt; 234 last SHELLCOMMAND unless defined ($_ = <> ); 235 if ($Echo_readline) { 236 # backdoor: I could not find a way to record sessions 237 print $_; 238 } 239 chomp; 240 } else { 241 last SHELLCOMMAND unless 242 defined ($_ = $term->readline($prompt, $commandline)); 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; 258 use strict; 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); 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; 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 301 CPAN::Shell->failed($CPAN::CurrentCommandId,1); 302 } 303 soft_chdir_with_alternatives(\@cwd); 304 $CPAN::Frontend->myprint("\n"); 305 $continuation = ""; 306 $CPAN::CurrentCommandId++; 307 $prompt = $oprompt; 308 } 309 } continue { 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 } 358 } 359 soft_chdir_with_alternatives(\@cwd); 360 } 361 362 #-> CPAN::soft_chdir_with_alternatives ; 363 sub soft_chdir_with_alternatives ($) { 364 my($cwd) = @_; 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. 369 }); 370 push @$cwd, $root; 371 } 372 while () { 373 if (chdir $cwd->[0]) { 374 return; 375 } else { 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 } 384 } 385 } 386 } 387 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 401 sub _yaml_module () { 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 } 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 } 424 return $yaml_module; 425 } 426 427 # CPAN::_yaml_loadfile 428 sub _yaml_loadfile { 429 my($self,$local_file) = @_; 430 return +[] unless -s $local_file; 431 my $yaml_module = _yaml_module; 432 if ($CPAN::META->has_inst($yaml_module)) { 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 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; 461 } 462 } else { 463 # this shall not be done by the frontend 464 die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "parse"); 465 } 466 return +[]; 467 } 468 469 # CPAN::_yaml_dumpfile 470 sub _yaml_dumpfile { 471 my($self,$local_file,@what) = @_; 472 my $yaml_module = _yaml_module; 473 if ($CPAN::META->has_inst($yaml_module)) { 474 my $code; 475 if (UNIVERSAL::isa($local_file, "FileHandle")) { 476 $code = UNIVERSAL::can($yaml_module, "Dump"); 477 eval { print $local_file $code->(@what) }; 478 } elsif ($code = UNIVERSAL::can($yaml_module, "DumpFile")) { 479 eval { $code->($local_file,@what); }; 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); 484 } 485 if ($@) { 486 die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"dump",$@); 487 } 488 } else { 489 if (UNIVERSAL::isa($local_file, "FileHandle")) { 490 # I think this case does not justify a warning at all 491 } else { 492 die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "dump"); 493 } 494 } 495 } 496 497 sub _init_sqlite () { 498 unless ($CPAN::META->has_inst("CPAN::SQLite")) { 499 $CPAN::Frontend->mywarn(qq{CPAN::SQLite not installed, trying to work without\n}) 500 unless $Have_warned->{"CPAN::SQLite"}++; 501 return; 502 } 503 require CPAN::SQLite::META; # not needed since CVS version of 2006-12-17 504 $CPAN::SQLite ||= CPAN::SQLite::META->new($CPAN::META); 505 } 506 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 523 package CPAN::CacheMgr; 524 use strict; 525 @CPAN::CacheMgr::ISA = qw(CPAN::InfoObj CPAN); 526 use File::Find; 527 528 package CPAN::FTP; 529 use strict; 530 use Fcntl qw(:flock); 531 use vars qw($connect_to_internet_ok $Ua $Thesite $ThesiteURL $Themethod); 532 @CPAN::FTP::ISA = qw(CPAN::Debug); 533 534 package CPAN::LWP::UserAgent; 535 use strict; 536 use vars qw(@ISA $USER $PASSWD $SETUPDONE); 537 # we delay requiring LWP::UserAgent and setting up inheritance until we need it 538 539 package CPAN::Complete; 540 use strict; 541 @CPAN::Complete::ISA = qw(CPAN::Debug); 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 544 @CPAN::Complete::COMMANDS = sort qw( 545 ? ! a b d h i m o q r u 546 autobundle 547 bye 548 clean 549 cvs_import 550 dump 551 exit 552 failed 553 force 554 fforce 555 hosts 556 install 557 install_tested 558 is_tested 559 look 560 ls 561 make 562 mkmyconfig 563 notest 564 perldoc 565 quit 566 readme 567 recent 568 recompile 569 reload 570 report 571 reports 572 scripts 573 smoke 574 test 575 upgrade 576 ); 577 578 package CPAN::Index; 579 use strict; 580 use vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03 $HAVE_REANIMATED); 581 @CPAN::Index::ISA = qw(CPAN::Debug); 582 $LAST_TIME ||= 0; 583 $DATE_OF_03 ||= 0; 584 # use constant PROTOCOL => "2.0"; # outcommented to avoid warning on upgrade from 1.57 585 sub PROTOCOL { 2.0 } 586 587 package CPAN::InfoObj; 588 use strict; 589 @CPAN::InfoObj::ISA = qw(CPAN::Debug); 590 591 package CPAN::Author; 592 use strict; 593 @CPAN::Author::ISA = qw(CPAN::InfoObj); 594 595 package CPAN::Distribution; 596 use strict; 597 @CPAN::Distribution::ISA = qw(CPAN::InfoObj); 598 599 package CPAN::Bundle; 600 use strict; 601 @CPAN::Bundle::ISA = qw(CPAN::Module); 602 603 package CPAN::Module; 604 use strict; 605 @CPAN::Module::ISA = qw(CPAN::InfoObj); 606 607 package CPAN::Exception::RecursiveDependency; 608 use strict; 609 use overload '""' => "as_string"; 610 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 615 sub new { 616 my($class) = shift; 617 my($deps) = shift; 618 my (@deps,%seen,$loop_starts_with); 619 DCHAIN: for my $dep (@$deps) { 620 push @deps, {name => $dep, display_as => $dep}; 621 if ($seen{$dep}++) { 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 } 667 } 668 bless { deps => \@deps }, $class; 669 } 670 671 sub as_string { 672 my($self) = shift; 673 my $ret = "\nRecursive dependency detected:\n "; 674 $ret .= join("\n => ", map {$_->{display_as}} @{$self->{deps}}); 675 $ret .= ".\nCannot resolve.\n"; 676 $ret; 677 } 678 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 { 698 my($class,$module,$file,$during,$error) = @_; 699 bless { module => $module, 700 file => $file, 701 during => $during, 702 error => $error }, $class; 703 } 704 705 sub as_string { 706 my($self) = shift; 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 } 732 } 733 734 package CPAN::Prompt; use overload '""' => "as_string"; 735 use vars qw($prompt); 736 $prompt = "cpan> "; 737 $CPAN::CurrentCommandId ||= 0; 738 sub new { 739 bless {}, shift; 740 } 741 sub as_string { 742 my $word = "cpan"; 743 unless ($CPAN::META->{LOCK}) { 744 $word = "nolock_cpan"; 745 } 746 if ($CPAN::Config->{commandnumber_in_prompt}) { 747 sprintf "$word\[%d]> ", $CPAN::CurrentCommandId; 748 } else { 749 "$word> "; 750 } 751 } 752 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 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, 783 TIME => time, 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) = @_; 797 $self->text; 798 } 799 800 package CPAN::Shell; 801 use strict; 802 use vars qw( 803 $ADVANCED_QUERY 804 $AUTOLOAD 805 $COLOR_REGISTERED 806 $Help 807 $autoload_recursion 808 $reload 809 @ISA 810 ); 811 @CPAN::Shell::ISA = qw(CPAN::Debug); 812 $COLOR_REGISTERED ||= 0; 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 }; 858 { 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{ 885 Commands starting with "w" require CPAN::WAIT to be installed. 886 Please consider installing CPAN::WAIT to use the fulltext index. 887 For this you just need to type 888 install CPAN::WAIT 889 }); 890 } 891 } else { 892 $CPAN::Frontend->mywarn(qq{Unknown shell command '$l'. }. 893 qq{Type ? for help. 894 }); 895 } 896 $autoload_recursion--; 897 } 898 } 899 900 package CPAN; 901 use strict; 902 903 $META ||= CPAN->new; # In case we re-eval ourselves we need the || 904 905 # from here on only subs. 906 ################################################################################ 907 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 } 915 my $mtime_perl = (-f CPAN::find_perl ? (stat(_))[9] : '-1'); 916 my $this_fingerprint = { 917 '$^X' => CPAN::find_perl, 918 sitearchexp => $Config::Config{sitearchexp}, 919 'mtime_$^X' => $mtime_perl, 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 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"); 940 my $new = CPAN::Shell::colorable_makemaker_prompt("Do you want to create 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 952 #-> sub CPAN::all_objects ; 953 sub all_objects { 954 my($mgr,$class) = @_; 955 CPAN::HandleConfig->load unless $CPAN::Config_loaded++; 956 CPAN->debug("mgr[$mgr] class[$class]") if $CPAN::DEBUG; 957 CPAN::Index->reload; 958 values %{ $META->{readwrite}{$class} }; # unsafe meta access, ok 959 } 960 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 966 #-> sub CPAN::checklock ; 967 sub checklock { 968 my($self) = @_; 969 my $lockfile = File::Spec->catfile($CPAN::Config->{cpan_home},".lock"); 970 if (-f $lockfile && -M _ > 0) { 971 my $fh = FileHandle->new($lockfile) or 972 $CPAN::Frontend->mydie("Could not open lockfile '$lockfile': $!"); 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) { 986 $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile '$lockfile'\n". 987 "reports other host $otherhost and other ". 988 "process $otherpid.\n". 989 "Cannot proceed.\n")); 990 } elsif ($RUN_DEGRADED) { 991 $CPAN::Frontend->mywarn("Running in degraded mode (experimental)\n"); 992 } elsif (defined $otherpid && $otherpid) { 993 return if $$ == $otherpid; # should never happen 994 $CPAN::Frontend->mywarn( 995 qq{ 996 There seems to be running another CPAN process (pid $otherpid). Contacting... 997 }); 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"); 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) { 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? 1014 $_->{use_sqlite} = 0; # better would be a write lock! 1015 } 1016 } else { 1017 $CPAN::Frontend->mydie(" 1018 You may want to kill the other job and delete the lockfile. On UNIX try: 1019 kill $otherpid 1020 rm $lockfile 1021 "); 1022 } 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 { 1040 $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Found invalid lockfile ". 1041 "'$lockfile', please remove. Cannot proceed.\n")); 1042 } 1043 } 1044 my $dotcpan = $CPAN::Config->{cpan_home}; 1045 eval { File::Path::mkpath($dotcpan);}; 1046 if ($@) { 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{ 1059 Working directory $symlinkcpan created. 1060 }); 1061 } 1062 } 1063 unless (-d $dotcpan) { 1064 my $mess = qq{ 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}; 1068 $mess .= qq{ 1069 As "$dotcpan" is a symlink to "$symlinkcpan", 1070 I tried to create that, but I failed with this error: $seconderror 1071 } if $seconderror; 1072 $mess .= qq{ 1073 Please make sure the directory exists and is writable. 1074 }; 1075 $CPAN::Frontend->mywarn($mess); 1076 return suggest_myconfig; 1077 } 1078 } # $@ after eval mkpath $dotcpan 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/) { 1090 $CPAN::Frontend->mywarn(qq{ 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 1102 this variable in either a CPAN/MyConfig.pm or a CPAN/Config.pm in your 1103 \@INC path; 1104 }); 1105 return suggest_myconfig; 1106 } 1107 } 1108 my $sleep = 1; 1109 while (!CPAN::_flock($fh, LOCK_EX|LOCK_NB)) { 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; 1119 $fh->autoflush(1); 1120 $fh->print($$, "\n"); 1121 $fh->print(hostname(), "\n"); 1122 $self->{LOCK} = $lockfile; 1123 $self->{LOCKFH} = $fh; 1124 } 1125 $SIG{TERM} = sub { 1126 my $sig = shift; 1127 &cleanup; 1128 $CPAN::Frontend->mydie("Got SIG$sig, leaving"); 1129 }; 1130 $SIG{INT} = sub { 1131 # no blocks!!! 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++; 1138 }; 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 1160 # global backstop to cleanup if we should really die 1161 $SIG{__DIE__} = \&cleanup; 1162 $self->debug("Signal handler set.") if $CPAN::DEBUG; 1163 } 1164 1165 #-> sub CPAN::DESTROY ; 1166 sub DESTROY { 1167 &cleanup; # need an eval? 1168 } 1169 1170 #-> sub CPAN::anycwd ; 1171 sub anycwd () { 1172 my $getcwd; 1173 $getcwd = $CPAN::Config->{'getcwd'} || 'cwd'; 1174 CPAN->$getcwd(); 1175 } 1176 1177 #-> sub CPAN::cwd ; 1178 sub cwd {Cwd::cwd();} 1179 1180 #-> sub CPAN::getcwd ; 1181 sub getcwd {Cwd::getcwd();} 1182 1183 #-> sub CPAN::fastcwd ; 1184 sub fastcwd {Cwd::fastcwd();} 1185 1186 #-> sub CPAN::backtickcwd ; 1187 sub backtickcwd {my $cwd = `cwd`; chomp $cwd; $cwd} 1188 1189 #-> sub CPAN::find_perl ; 1190 sub find_perl () { 1191 my($perl) = File::Spec->file_name_is_absolute($^X) ? $^X : ""; 1192 my $pwd = $CPAN::iCwd = CPAN::anycwd(); 1193 my $candidate = File::Spec->catfile($pwd,$^X); 1194 $perl ||= $candidate if MM->maybe_command($candidate); 1195 1196 unless ($perl) { 1197 my ($component,$perl_name); 1198 DIST_PERLNAME: foreach $perl_name ($^X, 'perl', 'perl5', "perl$]") { 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 } 1209 } 1210 1211 return $perl; 1212 } 1213 1214 1215 #-> sub CPAN::exists ; 1216 sub exists { 1217 my($mgr,$class,$id) = @_; 1218 CPAN::HandleConfig->load unless $CPAN::Config_loaded++; 1219 CPAN::Index->reload; 1220 ### Carp::croak "exists called without class argument" unless $class; 1221 $id ||= ""; 1222 $id =~ s/:+/::/g if $class eq "CPAN::Module"; 1223 my $exists; 1224 if (CPAN::_sqlite_running) { 1225 $exists = (exists $META->{readonly}{$class}{$id} or 1226 $CPAN::SQLite->set($class, $id)); 1227 } else { 1228 $exists = exists $META->{readonly}{$class}{$id}; 1229 } 1230 $exists ||= exists $META->{readwrite}{$class}{$id}; # unsafe meta access, ok 1231 } 1232 1233 #-> sub CPAN::delete ; 1234 sub delete { 1235 my($mgr,$class,$id) = @_; 1236 delete $META->{readonly}{$class}{$id}; # unsafe meta access, ok 1237 delete $META->{readwrite}{$class}{$id}; # unsafe meta access, ok 1238 } 1239 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; 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 ], 1258 'Net::FTP' => [ 1259 sub {require Net::FTP}, 1260 sub {require Net::Config}, 1261 ], 1262 'File::HomeDir' => [ 1263 sub {require File::HomeDir; 1264 unless (CPAN::Version->vge(File::HomeDir::->VERSION, 0.52)) { 1265 for ("Will not use File::HomeDir, need 0.52\n") { 1266 $CPAN::Frontend->mywarn($_); 1267 die $_; 1268 } 1269 } 1270 }, 1271 ], 1272 'Archive::Tar' => [ 1273 sub {require Archive::Tar; 1274 unless (CPAN::Version->vge(Archive::Tar::->VERSION, 1.00)) { 1275 for ("Will not use Archive::Tar, need 1.00\n") { 1276 $CPAN::Frontend->mywarn($_); 1277 die $_; 1278 } 1279 } 1280 }, 1281 ], 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 ] 1296 }; 1297 if ($usable->{$mod}) { 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 } 1306 } 1307 } 1308 return $HAS_USABLE->{$mod} = 1; 1309 } 1310 1311 #-> sub CPAN::has_inst 1312 sub has_inst { 1313 my($self,$mod,$message) = @_; 1314 Carp::croak("CPAN->has_inst() called without an argument") 1315 unless defined $mod; 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 1320 || 1321 $dont{$mod} 1322 ) { 1323 $CPAN::META->{dontload_hash}{$mod}||=1; # unsafe meta access, ok 1324 return 0; 1325 } 1326 my $file = $mod; 1327 my $obj; 1328 $file =~ s|::|/|g; 1329 $file .= ".pm"; 1330 if ($INC{$file}) { 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; 1337 } elsif (eval { require $file }) { 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; 1347 my $v = eval "\$$mod\::VERSION"; 1348 $v = $v ? " (v$v)" : ""; 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; 1354 } elsif ($mod eq "Net::FTP") { 1355 $CPAN::Frontend->mywarn(qq{ 1356 Please, install Net::FTP as soon as possible. CPAN.pm installs it for you 1357 if you just type 1358 install Bundle::libnet 1359 1360 }) unless $Have_warned->{"Net::FTP"}++; 1361 $CPAN::Frontend->mysleep(3); 1362 } elsif ($mod eq "Digest::SHA") { 1363 if ($Have_warned->{"Digest::SHA"}++) { 1364 $CPAN::Frontend->mywarn(qq{CPAN: checksum security checks disabled }. 1365 qq{because Digest::SHA not installed.\n}); 1366 } else { 1367 $CPAN::Frontend->mywarn(qq{ 1368 CPAN: checksum security checks disabled because Digest::SHA not installed. 1369 Please consider installing the Digest::SHA module. 1370 1371 }); 1372 $CPAN::Frontend->mysleep(2); 1373 } 1374 } elsif ($mod eq "Module::Signature") { 1375 # NOT prefs_lookup, we are not a distro 1376 my $check_sigs = $CPAN::Config->{check_sigs}; 1377 if (not $check_sigs) { 1378 # they do not want us:-( 1379 } elsif (not $Have_warned->{"Module::Signature"}++) { 1380 # No point in complaining unless the user can 1381 # reasonably install and use it. 1382 if (eval { require Crypt::OpenPGP; 1 } || 1383 ( 1384 defined $CPAN::Config->{'gpg'} 1385 && 1386 $CPAN::Config->{'gpg'} =~ /\S/ 1387 ) 1388 ) { 1389 $CPAN::Frontend->mywarn(qq{ 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 }); 1396 $CPAN::Frontend->mysleep(2); 1397 } 1398 } 1399 } else { 1400 delete $INC{$file}; # if it inc'd LWP but failed during, say, URI 1401 } 1402 return 0; 1403 } 1404 1405 #-> sub CPAN::instance ; 1406 sub instance { 1407 my($mgr,$class,$id) = @_; 1408 CPAN::Index->reload; 1409 $id ||= ""; 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); 1413 } 1414 1415 #-> sub CPAN::new ; 1416 sub new { 1417 bless {}, shift; 1418 } 1419 1420 #-> sub CPAN::cleanup ; 1421 sub cleanup { 1422 # warn "cleanup called with arg[@_] End[$CPAN::End] Signal[$Signal]"; 1423 local $SIG{__DIE__} = ''; 1424 my($message) = @_; 1425 my $i = 0; 1426 my $ineval = 0; 1427 my($subroutine); 1428 while ((undef,undef,undef,$subroutine) = caller(++$i)) { 1429 $ineval = 1, last if 1430 $subroutine eq '(eval)'; 1431 } 1432 return if $ineval && !$CPAN::End; 1433 return unless defined $META->{LOCK}; 1434 return unless -f $META->{LOCK}; 1435 $META->savehist; 1436 close $META->{LOCKFH}; 1437 unlink $META->{LOCK}; 1438 # require Carp; 1439 # Carp::cluck("DEBUGGING"); 1440 if ( $CPAN::CONFIG_DIRTY ) { 1441 $CPAN::Frontend->mywarn("Warning: Configuration not saved.\n"); 1442 } 1443 $CPAN::Frontend->myprint("Lockfile removed.\n"); 1444 } 1445 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 1459 #-> sub CPAN::savehist 1460 sub savehist { 1461 my($self) = @_; 1462 my($histfile,$histsize); 1463 unless ($histfile = $CPAN::Config->{'histfile'}) { 1464 $CPAN::Frontend->mywarn("No history written (no histfile specified).\n"); 1465 return; 1466 } 1467 $histsize = $CPAN::Config->{'histsize'} || 100; 1468 if ($CPAN::term) { 1469 unless ($CPAN::term->can("GetHistory")) { 1470 $CPAN::Frontend->mywarn("Terminal does not support GetHistory.\n"); 1471 return; 1472 } 1473 } else { 1474 return; 1475 } 1476 my @h = $CPAN::term->GetHistory; 1477 splice @h, 0, @h-$histsize if @h>$histsize; 1478 my($fh) = FileHandle->new; 1479 open $fh, ">$histfile" or $CPAN::Frontend->mydie("Couldn't open >$histfile: $!"); 1480 local $\ = local $, = "\n"; 1481 print $fh @h; 1482 close $fh; 1483 } 1484 1485 #-> sub CPAN::is_tested 1486 sub is_tested { 1487 my($self,$what,$when) = @_; 1488 unless ($what) { 1489 Carp::cluck("DEBUG: empty what"); 1490 return; 1491 } 1492 $self->{is_tested}{$what} = $when; 1493 } 1494 1495 #-> sub CPAN::is_installed 1496 # unsets the is_tested flag: as soon as the thing is installed, it is 1497 # not needed in set_perl5lib anymore 1498 sub is_installed { 1499 my($self,$what) = @_; 1500 delete $self->{is_tested}{$what}; 1501 } 1502 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 1510 #-> sub CPAN::set_perl5lib 1511 sub set_perl5lib { 1512 my($self,$for) = @_; 1513 unless ($for) { 1514 (undef,undef,undef,$for) = caller(1); 1515 $for =~ s/.*://; 1516 } 1517 $self->{is_tested} ||= {}; 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; 1523 #my @dirs = map {("$_/blib/arch", "$_/blib/lib")} keys %{$self->{is_tested}}; 1524 #$CPAN::Frontend->myprint("Prepending @dirs to PERL5LIB.\n"); 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 ); 1538 } else { 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" 1543 ); 1544 } 1545 1546 $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env; 1547 } 1548 1549 package CPAN::CacheMgr; 1550 use strict; 1551 1552 #-> sub CPAN::CacheMgr::as_string ; 1553 sub as_string { 1554 eval { require Data::Dumper }; 1555 if ($@) { 1556 return shift->SUPER::as_string; 1557 } else { 1558 return Data::Dumper::Dumper(shift); 1559 } 1560 } 1561 1562 #-> sub CPAN::CacheMgr::cachesize ; 1563 sub cachesize { 1564 shift->{DU}; 1565 } 1566 1567 #-> sub CPAN::CacheMgr::tidyup ; 1568 sub tidyup { 1569 my($self) = @_; 1570 return unless $CPAN::META->{LOCK}; 1571 return unless -d $self->{ID}; 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 ); 1582 return if $CPAN::Signal; 1583 $self->_clean_cache($toremove); 1584 return if $CPAN::Signal; 1585 } 1586 } 1587 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) = @_; 1596 return unless defined $dir; 1597 $self->debug("reading dir[$dir]") if $CPAN::DEBUG; 1598 $dir ||= $self->{ID}; 1599 my($cwd) = CPAN::anycwd(); 1600 chdir $dir or Carp::croak("Can't chdir to $dir: $!"); 1601 my $dh = DirHandle->new(File::Spec->curdir) 1602 or Carp::croak("Couldn't opendir $dir: $!"); 1603 my(@entries); 1604 for ($dh->read) { 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 } 1613 } 1614 chdir $cwd or Carp::croak("Can't chdir to $cwd: $!"); 1615 sort { -M $a <=> -M $b} @entries; 1616 } 1617 1618 #-> sub CPAN::CacheMgr::disk_usage ; 1619 sub disk_usage { 1620 my($self,$dir,$fast) = @_; 1621 return if exists $self->{SIZE}{$dir}; 1622 return if $CPAN::Signal; 1623 my($Du) = 0; 1624 if (-e $dir) { 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 } 1634 } 1635 } elsif (-f $dir) { 1636 # nothing to say, no matter what the permissions 1637 } 1638 } else { 1639 $CPAN::Frontend->mywarn("File or directory '$dir' has gone, ignoring\n"); 1640 return; 1641 } 1642 if ($fast) { 1643 $Du = 0; # placeholder 1644 } else { 1645 find( 1646 sub { 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"); 1661 $CPAN::Frontend->mysleep(5); 1662 return; 1663 } 1664 } 1665 } else { 1666 $Du += (-s _); 1667 } 1668 } 1669 }, 1670 $dir 1671 ); 1672 } 1673 return if $CPAN::Signal; 1674 $self->{SIZE}{$dir} = $Du/1024/1024; 1675 unshift @{$self->{FIFO}}, $dir; 1676 $self->debug("measured $dir is $Du") if $CPAN::DEBUG; 1677 $self->{DU} += $Du/1024/1024; 1678 $self->{DU}; 1679 } 1680 1681 #-> sub CPAN::CacheMgr::_clean_cache ; 1682 sub _clean_cache { 1683 my($self,$dir) = @_; 1684 return unless -e $dir; 1685 unless (File::Spec->canonpath(File::Basename::dirname($dir)) 1686 eq File::Spec->canonpath($CPAN::Config->{build_dir})) { 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 } 1692 $self->debug("have to rmtree $dir, will free $self->{SIZE}{$dir}") 1693 if $CPAN::DEBUG; 1694 File::Path::rmtree($dir); 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)) { 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}) { 1706 $CPAN::META->delete("CPAN::Distribution", $id); 1707 1708 # XXX we should restore the state NOW, otherise this 1709 # distro does not exist until we read an index. BUG ALERT(?) 1710 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 } 1720 $self->{DU} -= $self->{SIZE}{$dir}; 1721 delete $self->{SIZE}{$dir}; 1722 } 1723 1724 #-> sub CPAN::CacheMgr::new ; 1725 sub new { 1726 my $class = shift; 1727 my $time = time; 1728 my($debug,$t2); 1729 $debug = ""; 1730 my $self = { 1731 ID => $CPAN::Config->{build_dir}, 1732 MAX => $CPAN::Config->{'build_cache'}, 1733 SCAN => $CPAN::Config->{'scan_cache'} || 'atstart', 1734 DU => 0 1735 }; 1736 File::Path::mkpath($self->{ID}); 1737 my $dh = DirHandle->new($self->{ID}); 1738 bless $self, $class; 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}") 1752 unless $self->{SCAN} eq 'atstart'; 1753 return unless $CPAN::META->{LOCK}; 1754 $CPAN::Frontend->myprint( 1755 sprintf("Scanning cache %s for sizes\n", 1756 $self->{ID})); 1757 my $e; 1758 my @entries = $self->entries($self->{ID}); 1759 my $i = 0; 1760 my $painted = 0; 1761 for $e (@entries) { 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 } 1769 $i++; 1770 while (($painted/76) < ($i/@entries)) { 1771 $CPAN::Frontend->myprint($symbol); 1772 $painted++; 1773 } 1774 return if $CPAN::Signal; 1775 } 1776 $CPAN::Frontend->myprint("DONE\n"); 1777 $self->tidyup; 1778 } 1779 1780 package CPAN::Shell; 1781 use strict; 1782 1783 #-> sub CPAN::Shell::h ; 1784 sub h { 1785 my($class,$about) = @_; 1786 if (defined $about) { 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"); 1797 } else { 1798 my $filler = " " x (80 - 28 - length($CPAN::VERSION)); 1799 $CPAN::Frontend->myprint(qq{ 1800 Display Information $filler (ver $CPAN::VERSION) 1801 command argument description 1802 a,b,d,m WORD or /REGEXP/ about authors, bundles, distributions, modules 1803 i WORD or /REGEXP/ about any of the above 1804 ls AUTHOR or GLOB about files in the author's directory 1805 (with WORD being a module, bundle or author name or a distribution 1806 name of the form AUTHOR/DISTRIBUTION) 1807 1808 Download, Test, Make, Install... 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 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 1818 Pragmas 1819 force CMD try hard to do command fforce CMD try harder 1820 notest CMD skip testing 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 1826 autobundle Snapshot recent latest CPAN uploads}); 1827 } 1828 } 1829 1830 *help = \&h; 1831 1832 #-> sub CPAN::Shell::a ; 1833 sub a { 1834 my($self,@arg) = @_; 1835 # authors are always UPPERCASE 1836 for (@arg) { 1837 $_ = uc $_ unless /=/; 1838 } 1839 $CPAN::Frontend->myprint($self->format_result('Author',@arg)); 1840 } 1841 1842 #-> sub CPAN::Shell::globls ; 1843 sub globls { 1844 my($self,$s,$pragmas) = @_; 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 1848 my(@accept,@preexpand); 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/"); 1857 } else { 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/"); 1863 } 1864 } else { 1865 $CPAN::Frontend->mydie("Text::Glob not installed, cannot proceed"); 1866 } 1867 } else { 1868 push @preexpand, uc $s; 1869 } 1870 for (@preexpand) { 1871 unless (/^[A-Z0-9\-]+(\/|$)/i) { 1872 $CPAN::Frontend->mywarn("ls command rejects argument $_: not an author\n"); 1873 next; 1874 } 1875 push @accept, $_; 1876 } 1877 my $silent = @accept>1; 1878 my $last_alpha = ""; 1879 my @results; 1880 for my $a (@accept) { 1881 my($author,$pathglob); 1882 if ($a =~ m|(.*?)/(.*)|) { 1883 my $a2 = $1; 1884 $pathglob = $2; 1885 $author = CPAN::Shell->expand_by_method('CPAN::Author', 1886 ['id'], 1887 $a2) 1888 or $CPAN::Frontend->mydie("No author found for $a2\n"); 1889 } else { 1890 $author = CPAN::Shell->expand_by_method('CPAN::Author', 1891 ['id'], 1892 $a) 1893 or $CPAN::Frontend->mydie("No author found for $a\n"); 1894 } 1895 if ($silent) { 1896 my $alpha = substr $author->id, 0, 1; 1897 my $ad; 1898 if ($alpha eq $last_alpha) { 1899 $ad = ""; 1900 } else { 1901 $ad = "[$alpha]"; 1902 $last_alpha = $alpha; 1903 } 1904 $CPAN::Frontend->myprint($ad); 1905 } 1906 for my $pragma (@$pragmas) { 1907 if ($author->can($pragma)) { 1908 $author->$pragma(); 1909 } 1910 } 1911 push @results, $author->ls($pathglob,$silent); # silent if 1912 # more than one 1913 # author 1914 for my $pragma (@$pragmas) { 1915 my $unpragma = "un$pragma"; 1916 if ($author->can($unpragma)) { 1917 $author->$unpragma(); 1918 } 1919 } 1920 } 1921 @results; 1922 } 1923 1924 #-> sub CPAN::Shell::local_bundles ; 1925 sub local_bundles { 1926 my($self,@which) = @_; 1927 my($incdir,$bdir,$dh); 1928 foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) { 1929 my @bbase = "Bundle"; 1930 while (my $bbase = shift @bbase) { 1931 $bdir = File::Spec->catdir($incdir,split /::/, $bbase); 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) { 1936 next if $entry =~ /^\./; 1937 next unless $entry =~ /^\w+(\.pm)?(?!\n)\Z/; 1938 if (-d File::Spec->catdir($bdir,$entry)) { 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 } 1947 } 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; 1955 $CPAN::Frontend->myprint($self->format_result('Bundle',@which)); 1956 } 1957 1958 #-> sub CPAN::Shell::d ; 1959 sub d { $CPAN::Frontend->myprint(shift->format_result('Distribution',@_));} 1960 1961 #-> sub CPAN::Shell::m ; 1962 sub m { # emacs confused here }; sub mimimimimi { # emacs in sync here 1963 my $self = shift; 1964 $CPAN::Frontend->myprint($self->format_result('Module',@_)); 1965 } 1966 1967 #-> sub CPAN::Shell::i ; 1968 sub i { 1969 my($self) = shift; 1970 my(@args) = @_; 1971 @args = '/./' unless @args; 1972 my(@result); 1973 for my $type (qw/Bundle Distribution Module/) { 1974 push @result, $self->expand($type,@args); 1975 } 1976 # Authors are always uppercase. 1977 push @result, $self->expand("Author", map { uc $_ } @args); 1978 1979 my $result = @result == 1 ? 1980 $result[0]->as_string : 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 ); 1987 $CPAN::Frontend->myprint($result); 1988 } 1989 1990 #-> sub CPAN::Shell::o ; 1991 1992 # CPAN::Shell::o and CPAN::HandleConfig::edit are closely related. 'o 1993 # conf' calls through to CPAN::HandleConfig::edit. 'o conf' should 1994 # probably have been called 'set' and 'o debug' maybe 'set debug' or 1995 # 'debug'; 'o conf ARGS' calls ->edit in CPAN/HandleConfig.pm 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') { 2001 my($cfilter); 2002 ($cfilter) = $o_what[0] =~ m|^/(.*)/$| if @o_what; 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 "); 2008 my @from; 2009 if (exists $INC{'CPAN/Config.pm'}) { 2010 push @from, $INC{'CPAN/Config.pm'}; 2011 } 2012 if (exists $INC{'CPAN/MyConfig.pm'}) { 2013 push @from, $INC{'CPAN/MyConfig.pm'}; 2014 } 2015 $CPAN::Frontend->myprint(join " and ", map {"'$_'"} @from); 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/; 2025 CPAN::HandleConfig->prettyprint($k); 2026 } 2027 $CPAN::Frontend->myprint("\n"); 2028 } else { 2029 if (CPAN::HandleConfig->edit(@o_what)) { 2030 } else { 2031 $CPAN::Frontend->myprint(qq{Type 'o conf' to view all configuration }. 2032 qq{items\n\n}); 2033 } 2034 } 2035 } elsif ($o_type eq 'debug') { 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; 2041 if ($what =~ s/^-// && exists $CPAN::DEBUG{$what}) { 2042 $CPAN::DEBUG &= $CPAN::DEBUG ^ $CPAN::DEBUG{$what}; 2043 next; 2044 } 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) 2081 if $v & $CPAN::DEBUG; 2082 } 2083 } else { 2084 $CPAN::Frontend->myprint("Debugging turned off completely.\n"); 2085 } 2086 } else { 2087 $CPAN::Frontend->myprint(qq{ 2088 Known options: 2089 conf set or get configuration variables 2090 debug set or get debugging options 2091 }); 2092 } 2093 } 2094 2095 # CPAN::Shell::paintdots_onreload 2096 sub paintdots_onreload { 2097 my($ref) = shift; 2098 sub { 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("."); 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 } 2114 return; 2115 } 2116 warn @_; 2117 }; 2118 } 2119 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; 2145 my $url = ref $last->{thesiteurl} ? $last->{thesiteurl}->text : $last->{thesiteurl}; 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 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 } 2171 if ($res->{ok} && @{$res->{ok}}) { 2172 $R .= sprintf "\nSuccessful downloads: 2173 N kB secs kB/s url\n"; 2174 my $i = 20; 2175 for (sort { $b->[3] <=> $a->[3] } @{$res->{ok}}) { 2176 $R .= sprintf "%4d %8d %5d %9.1f %s\n", @$_; 2177 last if --$i<=0; 2178 } 2179 } 2180 if ($res->{no} && @{$res->{no}}) { 2181 $R .= sprintf "\nUnsuccessful downloads:\n"; 2182 my $i = 20; 2183 for (sort { $b->[0] <=> $a->[0] } @{$res->{no}}) { 2184 $R .= sprintf "%4d %s\n", @$_; 2185 last if --$i<=0; 2186 } 2187 } 2188 $CPAN::Frontend->myprint($R); 2189 } 2190 2191 #-> sub CPAN::Shell::reload ; 2192 sub reload { 2193 my($self,$command,@arg) = @_; 2194 $command ||= ""; 2195 $self->debug("self[$self]command[$command]arg[@arg]") if $CPAN::DEBUG; 2196 if ($command =~ /^cpan$/i) { 2197 my $redef = 0; 2198 chdir $CPAN::iCwd if $CPAN::iCwd; # may fail 2199 my $failed; 2200 my @relo = ( 2201 "CPAN.pm", 2202 "CPAN/Debug.pm", 2203 "CPAN/FirstTime.pm", 2204 "CPAN/HandleConfig.pm", 2205 "CPAN/Kwalify.pm", 2206 "CPAN/Queue.pm", 2207 "CPAN/Reporter/Config.pm", 2208 "CPAN/Reporter/History.pm", 2209 "CPAN/Reporter.pm", 2210 "CPAN/SQLite.pm", 2211 "CPAN/Tarzip.pm", 2212 "CPAN/Version.pm", 2213 ); 2214 MFILE: for my $f (@relo) { 2215 next unless exists $INC{$f}; 2216 my $p = $f; 2217 $p =~ s/\.pm$//; 2218 $p =~ s|/|::|g; 2219 $CPAN::Frontend->myprint("($p"); 2220 local($SIG{__WARN__}) = paintdots_onreload(\$redef); 2221 $self->_reload_this($f) or $failed++; 2222 my $v = eval "$p\::->VERSION"; 2223 $CPAN::Frontend->myprint("v$v)"); 2224 } 2225 $CPAN::Frontend->myprint("\n$redef subroutines redefined\n"); 2226 if ($failed) { 2227 my $errors = $failed == 1 ? "error" : "errors"; 2228 $CPAN::Frontend->mywarn("\n$failed $errors during reload. You better quit ". 2229 "this session.\n"); 2230 } 2231 } elsif ($command =~ /^index$/i) { 2232 CPAN::Index->force_reload; 2233 } else { 2234 $CPAN::Frontend->myprint(qq{cpan re-evals the CPAN modules 2235 index re-reads the index files\n}); 2236 } 2237 } 2238 2239 # reload means only load again what we have loaded before 2240 #-> sub CPAN::Shell::_reload_this ; 2241 sub _reload_this { 2242 my($self,$f,$args) = @_; 2243 CPAN->debug("f[$f]") if $CPAN::DEBUG; 2244 return 1 unless $INC{$f}; # we never loaded this, so we do not 2245 # reload but say OK 2246 my $pwd = CPAN::anycwd(); 2247 CPAN->debug("pwd[$pwd]") if $CPAN::DEBUG; 2248 my($file); 2249 for my $inc (@INC) { 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}; 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 } 2265 } 2266 CPAN->debug("file[$file]inc[@inc]") if $CPAN::DEBUG; 2267 unless (-f $file) { 2268 $CPAN::Frontend->mywarn("Found no file to reload for '$f'\n"); 2269 return; 2270 } 2271 my $mtime = (stat $file)[9]; 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}; 2280 $args ||= {}; 2281 $must_reload ||= $args->{reloforce}; # o conf defaults needs this 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'"; 2293 if ($@) { 2294 warn $@; 2295 return; 2296 } 2297 $reload->{$f} = $mtime; 2298 } else { 2299 $CPAN::Frontend->myprint("__unchanged__"); 2300 } 2301 return 1; 2302 } 2303 2304 #-> sub CPAN::Shell::mkmyconfig ; 2305 sub mkmyconfig { 2306 my($self, $cpanpm, %args) = @_; 2307 require CPAN::FirstTime; 2308 my $home = CPAN::HandleConfig::home; 2309 $cpanpm = $INC{'CPAN/MyConfig.pm'} || 2310 File::Spec->catfile(split /\//, "$home/.cpan/CPAN/MyConfig.pm"); 2311 File::Path::mkpath(File::Basename::dirname($cpanpm)) unless -e $cpanpm; 2312 CPAN::HandleConfig::require_myconfig_or_config; 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 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','/./')) { 2329 my $file = $module->cpan_file; 2330 next if $file eq "N/A"; 2331 next if $file =~ /^Contact Author/; 2332 my $dist = $CPAN::META->instance('CPAN::Distribution',$file); 2333 next if $dist->isa_perl; 2334 next unless $module->xs_file; 2335 local($|) = 1; 2336 $CPAN::Frontend->myprint("."); 2337 push @result, $module; 2338 } 2339 # print join " | ", @result; 2340 $CPAN::Frontend->myprint("\n"); 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(); 2349 for $module (@module) { # we force now and compile later, so we 2350 # don't do it twice 2351 $cpan_file = $module->cpan_file; 2352 my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); 2353 $pack->force; 2354 $dist{$cpan_file}++; 2355 } 2356 for $cpan_file (sort keys %dist) { 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 2361 # stop a package from recompiling, 2362 # e.g. IO-1.12 when we have perl5.003_10 2363 } 2364 } 2365 2366 #-> sub CPAN::Shell::scripts ; 2367 sub scripts { 2368 my($self, $arg) = @_; 2369 $CPAN::Frontend->mywarn(">>>> experimental command, currently unsupported <<<<\n\n"); 2370 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 } 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|) { 2385 $qrarg = eval 'qr/$arg/'; # hide construct from 5.004 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 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; 2436 $self->force("test",@args); # force is there so that the test be 2437 # re-run (as documented) 2438 } 2439 2440 # compare with is_tested 2441 #-> sub CPAN::Shell::install_tested 2442 sub install_tested { 2443 my($self,@some) = @_; 2444 $CPAN::Frontend->mywarn("install_tested() must not be called with arguments.\n"), 2445 return if @some; 2446 CPAN::Index->reload; 2447 2448 for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { 2449 my $yaml = "$b.yml"; 2450 unless (-f $yaml) { 2451 $CPAN::Frontend->mywarn("No YAML file for $b available, skipping\n"); 2452 next; 2453 } 2454 my $yaml_content = CPAN->_yaml_loadfile($yaml); 2455 my $id = $yaml_content->[0]{distribution}{ID}; 2456 unless ($id) { 2457 $CPAN::Frontend->mywarn("No ID found in '$yaml', skipping\n"); 2458 next; 2459 } 2460 my $do = CPAN::Shell->expandany($id); 2461 unless ($do) { 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 } 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 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; 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"); 2491 $CPAN::Frontend->mysleep(1); 2492 $self->install($d); 2493 } 2494 } 2495 2496 #-> sub CPAN::Shell::upgrade ; 2497 sub upgrade { 2498 my($self,@args) = @_; 2499 $self->install($self->r(@args)); 2500 } 2501 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; 2507 Carp::croak "Usage: \$obj->_u_r_common(a|r|u)" unless 2508 $what && $what =~ /^[aru]$/; 2509 my(@args) = @_; 2510 @args = '/./' unless @args; 2511 my(@result,$module,%seen,%need,$headerdone, 2512 $version_undefs,$version_zeroes, 2513 @version_undefs,@version_zeroes); 2514 $version_undefs = $version_zeroes = 0; 2515 my $sprintf = "%s%-25s%s %9s %9s %s\n"; 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 } 2522 MODULE: for $module (@expand) { 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); 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 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( 2574 $sprintf, 2575 "", 2576 "Package namespace", 2577 "", 2578 "installed", 2579 "latest", 2580 "in CPAN file" 2581 )); 2582 } 2583 my $color_on = ""; 2584 my $color_off = ""; 2585 if ( 2586 $COLOR_REGISTERED 2587 && 2588 $CPAN::META->has_inst("Term::ANSIColor") 2589 && 2590 $module->description 2591 ) { 2592 $color_on = Term::ANSIColor::color("green"); 2593 $color_off = Term::ANSIColor::color("reset"); 2594 } 2595 $CPAN::Frontend->myprint(sprintf $sprintf, 2596 $color_on, 2597 $module->id, 2598 $color_off, 2599 $have, 2600 $latest, 2601 $file); 2602 $need{$module->id}++; 2603 } 2604 unless (%need) { 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 } 2610 } 2611 if ($what eq "r") { 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 } 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 2654 #-> sub CPAN::Shell::failed ; 2655 sub failed { 2656 my($self,$only_id,$silent) = @_; 2657 my @failed; 2658 DIST: for my $d ($CPAN::META->all_objects("CPAN::Distribution")) { 2659 my $failed = ""; 2660 NAY: for my $nosayer ( # order matters! 2661 "unwrapped", 2662 "writemakefile", 2663 "signature_verify", 2664 "make", 2665 "make_test", 2666 "install", 2667 "make_clean", 2668 ) { 2669 next unless exists $d->{$nosayer}; 2670 next unless defined $d->{$nosayer}; 2671 next unless ( 2672 UNIVERSAL::can($d->{$nosayer},"failed") ? 2673 $d->{$nosayer}->failed : 2674 $d->{$nosayer} =~ /^NO/ 2675 ); 2676 next NAY if $only_id && $only_id != ( 2677 UNIVERSAL::can($d->{$nosayer},"commandid") 2678 ? 2679 $d->{$nosayer}->commandid 2680 : 2681 $CPAN::CurrentCommandId 2682 ); 2683 $failed = $nosayer; 2684 last; 2685 } 2686 next DIST unless $failed; 2687 my $id = $d->id; 2688 $id =~ s|^./../||; 2689 #$print .= sprintf( 2690 # " %-45s: %s %s\n", 2691 push @failed, 2692 ( 2693 UNIVERSAL::can($d->{$failed},"failed") ? 2694 [ 2695 $d->{$failed}->commandid, 2696 $id, 2697 $failed, 2698 $d->{$failed}->text, 2699 $d->{$failed}{TIME}||0, 2700 ] : 2701 [ 2702 1, 2703 $id, 2704 $failed, 2705 $d->{$failed}, 2706 0, 2707 ] 2708 ); 2709 } 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 } 2720 if (@failed) { 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"); 2737 } elsif (!$only_id || !$silent) { 2738 $CPAN::Frontend->myprint("Nothing failed in $scope\n"); 2739 } 2740 } 2741 2742 # XXX intentionally undocumented because completely bogus, unportable, 2743 # useless, etc. 2744 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}}) { 2768 warn sprintf " %-25s %6d (keys: %6d)\n", 2769 $k2, 2770 Devel::Size::total_size($CPAN::META->{$k}{$k2})/1024, 2771 scalar keys %{$CPAN::META->{$k}{$k2}}; 2772 } 2773 } 2774 } 2775 2776 # compare with install_tested 2777 #-> sub CPAN::Shell::is_tested 2778 sub is_tested { 2779 my($self) = @_; 2780 CPAN::Index->reload; 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 2793 #-> sub CPAN::Shell::autobundle ; 2794 sub autobundle { 2795 my($self) = shift; 2796 CPAN::HandleConfig->load unless $CPAN::Config_loaded++; 2797 my(@bundle) = $self->_u_r_common("a",@_); 2798 my($todir) = File::Spec->catdir($CPAN::Config->{'cpan_home'},"Bundle"); 2799 File::Path::mkpath($todir); 2800 unless (-d $todir) { 2801 $CPAN::Frontend->myprint("Couldn't mkdir $todir for some reason\n"); 2802 return; 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; 2809 my($to) = File::Spec->catfile($todir,"$me.pm"); 2810 while (-f $to) { 2811 $me = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, ++$c; 2812 $to = File::Spec->catfile($todir,"$me.pm"); 2813 } 2814 my($fh) = FileHandle->new(">$to") or Carp::croak "Can't open >$to: $!"; 2815 $fh->print( 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 ); 2835 $fh->close; 2836 $CPAN::Frontend->myprint("\nWrote bundle file 2837 $to\n\n"); 2838 } 2839 2840 #-> sub CPAN::Shell::expandany ; 2841 sub expandany { 2842 my($self,$s) = @_; 2843 CPAN->debug("s[$s]") if $CPAN::DEBUG; 2844 if ($s =~ m|/| or substr($s,-1,1) eq ".") { # looks like a file or a directory 2845 $s = CPAN::Distribution->normalize($s); 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 2861 #-> sub CPAN::Shell::expand ; 2862 sub expand { 2863 my $self = shift; 2864 my($type,@args) = @_; 2865 CPAN->debug("type[$type]args[@args]") if $CPAN::DEBUG; 2866 my $class = "CPAN::$type"; 2867 my $methods = ['id']; 2868 for my $meth (qw(name)) { 2869 next unless $class->can($meth); 2870 push @$methods, $meth; 2871 } 2872 $self->expand_by_method($class,$methods,@args); 2873 } 2874 2875 #-> sub CPAN::Shell::expand_by_method ; 2876 sub expand_by_method { 2877 my $self = shift; 2878 my($class,$methods,@args) = @_; 2879 my($arg,@m); 2880 for $arg (@args) { 2881 my($regex,$command); 2882 if ($arg =~ m|^/(.*)/$|) { 2883 $regex = $1; 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; 2888 } 2889 my $obj; 2890 CPAN->debug(sprintf "class[%s]regex[%s]command[%s]", 2891 $class, 2892 defined $regex ? $regex : "UNDEFINED", 2893 defined $command ? $command : "UNDEFINED", 2894 ) if $CPAN::DEBUG; 2895 if (defined $regex) { 2896 if (CPAN::_sqlite_running) { 2897 $CPAN::SQLite->search($class, $regex); 2898 } 2899 for $obj ( 2900 $CPAN::META->all_objects($class) 2901 ) { 2902 unless ($obj && UNIVERSAL::can($obj,"id") && $obj->id) { 2903 # BUG, we got an empty object somewhere 2904 require Data::Dumper; 2905 CPAN->debug(sprintf( 2906 "Bug in CPAN: Empty id on obj[%s][%s]", 2907 $obj, 2908 Data::Dumper::Dumper($obj) 2909 )) if $CPAN::DEBUG; 2910 next; 2911 } 2912 for my $method (@$methods) { 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) { 2919 push @m, $obj; 2920 last; 2921 } 2922 } 2923 } 2924 } elsif ($command) { 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/^~(.+)/; 2933 for my $self ( 2934 sort 2935 {$a->id cmp $b->id} 2936 $CPAN::META->all_objects($class) 2937 ) { 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 } 2944 } 2945 } else { 2946 my($xarg) = $arg; 2947 if ( $class eq 'CPAN::Bundle' ) { 2948 $xarg =~ s/^(Bundle::)?(.*)/Bundle::$2/; 2949 } elsif ($class eq "CPAN::Distribution") { 2950 $xarg = CPAN::Distribution->normalize($arg); 2951 } else { 2952 $xarg =~ s/:+/::/g; 2953 } 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 } 2963 } 2964 @m = sort {$a->id cmp $b->id} @m; 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 } 2970 return wantarray ? @m : $m[0]; 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); 2979 my $result = @result == 1 ? 2980 $result[0]->as_string : 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 ); 2987 $result; 2988 } 2989 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; 2997 if ($CPAN::META->has_usable("File::Temp")) { 2998 $installation_report_fh 2999 = File::Temp->new( 3000 dir => File::Spec->tmpdir, 3001 template => 'cpan_install_XXXX', 3002 suffix => '.txt', 3003 unlink => 0, 3004 ); 3005 } 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 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 ;-) 3018 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 3040 #-> sub CPAN::Shell::print_ornamented ; 3041 sub print_ornamented { 3042 my($self,$what,$ornament) = @_; 3043 return unless defined $what; 3044 3045 local $| = 1; # Flush immediately 3046 if ( $CPAN::Be_Silent ) { 3047 print {report_fh()} $what; 3048 return; 3049 } 3050 my $swhat = "$what"; # stringify if it is an object 3051 if ($CPAN::Config->{term_is_latin}) { 3052 # note: deprecated, need to switch to $LANG and $LC_* 3053 # courtesy jhi: 3054 $swhat 3055 =~ s{([\xC0-\xDF])([\x80-\xBF])}{chr(ord($1)<<6&0xC0|ord($2)&0x3F)}eg; #}; 3056 } 3057 if ($self->colorize_output) { 3058 if ( $CPAN::DEBUG && $swhat =~ /^Debug\(/ ) { 3059 # if you want to have this configurable, please file a bugreport 3060 $ornament = $CPAN::Config->{colorize_debug} || "black on_cyan"; 3061 } 3062 my $color_on = eval { Term::ANSIColor::color($ornament) } || ""; 3063 if ($@) { 3064 print "Term::ANSIColor rejects color[$ornament]: $@\n 3065 Please choose a different color (Hint: try 'o conf init /color/')\n"; 3066 } 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//; 3073 print $color_on, 3074 $swhat, 3075 Term::ANSIColor::color("reset"), 3076 $trailer; 3077 } else { 3078 print $swhat; 3079 } 3080 } 3081 3082 #-> sub CPAN::Shell::myprint ; 3083 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. 3088 sub myprint { 3089 my($self,$what) = @_; 3090 $self->print_ornamented($what, 3091 $CPAN::Config->{colorize_print}||'bold blue on_white', 3092 ); 3093 } 3094 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 } 3104 } 3105 3106 #-> sub CPAN::Shell::myexit ; 3107 sub myexit { 3108 my($self,$what) = @_; 3109 $self->myprint($what); 3110 exit; 3111 } 3112 3113 #-> sub CPAN::Shell::mywarn ; 3114 sub mywarn { 3115 my($self,$what) = @_; 3116 $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white'); 3117 } 3118 3119 # only to be used for shell commands 3120 #-> sub CPAN::Shell::mydie ; 3121 sub mydie { 3122 my($self,$what) = @_; 3123 $self->mywarn($what); 3124 3125 # If it is the shell, we want the following die to be silent, 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 3130 die "\n"; 3131 } 3132 3133 # sub CPAN::Shell::colorable_makemaker_prompt ; 3134 sub colorable_makemaker_prompt { 3135 my($foo,$bar) = @_; 3136 if (CPAN::Shell->colorize_output) { 3137 my $ornament = $CPAN::Config->{colorize_print}||'bold blue on_white'; 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 3148 # use this only for unrecoverable errors! 3149 #-> sub CPAN::Shell::unrecoverable_error ; 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) { 3159 if ($l =~ /^\s*$/) { 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); 3171 } 3172 3173 #-> sub CPAN::Shell::mysleep ; 3174 sub mysleep { 3175 my($self, $sleep) = @_; 3176 if (CPAN->has_inst("Time::HiRes")) { 3177 Time::HiRes::sleep($sleep); 3178 } else { 3179 sleep($sleep < 1 ? 1 : int($sleep + 0.5)); 3180 } 3181 } 3182 3183 #-> sub CPAN::Shell::setup_output ; 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 3193 #-> sub CPAN::Shell::rematein ; 3194 # RE-adme||MA-ke||TE-st||IN-stall : nearly everything runs through here 3195 sub rematein { 3196 my $self = shift; 3197 my($meth,@some) = @_; 3198 my @pragma; 3199 while($meth =~ /^(ff?orce|notest)$/) { 3200 push @pragma, $meth; 3201 $meth = shift @some or 3202 $CPAN::Frontend->mydie("Pragma $pragma[-1] used without method: ". 3203 "cannot continue"); 3204 } 3205 setup_output(); 3206 CPAN->debug("pragma[@pragma]meth[$meth]some[@some]") if $CPAN::DEBUG; 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 3222 # With configure_requires, "get" is vulnerable in recursion. 3223 3224 my $needs_recursion_protection = "get|make|test|install"; 3225 3226 # construct the queue 3227 my($s,@s,@qcopy); 3228 STHING: foreach $s (@some) { 3229 my $obj; 3230 if (ref $s) { 3231 CPAN->debug("s is an object[$s]") if $CPAN::DEBUG; 3232 $obj = $s; 3233 } elsif ($s =~ m|[\$\@\%]|) { # looks like a perl variable 3234 } elsif ($s =~ m|^/|) { # looks like a regexp 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 } 3243 } elsif ($meth eq "ls") { 3244 $self->globls($s,\@pragma); 3245 next STHING; 3246 } else { 3247 CPAN->debug("calling expandany [$s]") if $CPAN::DEBUG; 3248 $obj = CPAN::Shell->expandany($s); 3249 } 3250 if (0) { 3251 } elsif (ref $obj) { 3252 if ($meth =~ /^($needs_recursion_protection)$/) { 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); }; 3257 if ($@) { 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 } 3269 } 3270 CPAN::Queue->queue_item(qmod => $obj->id, reqtype => "c"); 3271 push @qcopy, $obj; 3272 } elsif ($CPAN::META->exists('CPAN::Author',uc($s))) { 3273 $obj = $CPAN::META->instance('CPAN::Author',uc($s)); 3274 if ($meth =~ /^(dump|ls|reports)$/) { 3275 $obj->$meth(); 3276 } else { 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); 3284 } 3285 } elsif ($s =~ m|[\$\@\%]| && $meth eq "dump") { 3286 CPAN::InfoObj->dump($s); 3287 } else { 3288 $CPAN::Frontend 3289 ->mywarn(qq{Warning: Cannot $meth $s, }. 3290 qq{don't know what it is. 3291 Try the command 3292 3293 i /$s/ 3294 3295 to find objects with matching identifiers. 3296 }); 3297 $CPAN::Frontend->mysleep(2); 3298 } 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) 3304 while (my $q = CPAN::Queue->first) { 3305 my $obj; 3306 my $s = $q->as_string; 3307 my $reqtype = $q->reqtype || ""; 3308 $obj = CPAN::Shell->expandany($s); 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' ". 3314 "to an object. Skipping.\n"); 3315 $CPAN::Frontend->mysleep(5); 3316 CPAN::Queue->delete_first($s); 3317 next; 3318 } 3319 $obj->{reqtype} ||= ""; 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 } 3329 if ($obj->{reqtype}) { 3330 if ($obj->{reqtype} eq "b" && $reqtype =~ /^[rc]$/) { 3331 $obj->{reqtype} = $reqtype; 3332 if ( 3333 exists $obj->{install} 3334 && 3335 ( 3336 UNIVERSAL::can($obj->{install},"failed") ? 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 3350 for my $pragma (@pragma) { 3351 if ($pragma 3352 && 3353 $obj->can($pragma)) { 3354 $obj->$pragma($meth); 3355 } 3356 } 3357 if (UNIVERSAL::can($obj, 'called_for')) { 3358 $obj->called_for($s); 3359 } 3360 CPAN->debug(qq{pragma[@pragma]meth[$meth]}. 3361 qq{ID[$obj->{ID}]}) if $CPAN::DEBUG; 3362 3363 push @qcopy, $obj; 3364 if ($meth =~ /^(report)$/) { # they came here with a pragma? 3365 $self->$meth($obj); 3366 } elsif (! UNIVERSAL::can($obj,$meth)) { 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 } 3380 CPAN->debug("Going to panic. meth[$meth]s[$s]") if $CPAN::DEBUG; 3381 $CPAN::Frontend->mydie("Panic: obj[$serialized] cannot meth[$meth]"); 3382 } elsif ($obj->$meth()) { 3383 CPAN::Queue->delete($s); 3384 CPAN->debug("From queue deleted. meth[$meth]s[$s]") if $CPAN::DEBUG; 3385 } else { 3386 CPAN->debug("Failed. pragma[@pragma]meth[$meth]") if $CPAN::DEBUG; 3387 } 3388 3389 $obj->undelay; 3390 for my $pragma (@pragma) { 3391 my $unpragma = "un$pragma"; 3392 if ($obj->can($unpragma)) { 3393 $obj->$unpragma(); 3394 } 3395 } 3396 CPAN::Queue->delete_first($s); 3397 } 3398 if ($meth =~ /^($needs_recursion_protection)$/) { 3399 for my $obj (@qcopy) { 3400 $obj->color_cmd_tmps(0,0); 3401 } 3402 } 3403 } 3404 3405 #-> sub CPAN::Shell::recent ; 3406 sub recent { 3407 my($self) = @_; 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 $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 } 3491 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 } 3513 } 3514 3515 { 3516 # set up the dispatching methods 3517 no strict "refs"; 3518 for my $command (qw( 3519 clean 3520 cvs_import 3521 dump 3522 force 3523 fforce 3524 get 3525 install 3526 look 3527 ls 3528 make 3529 notest 3530 perldoc 3531 readme 3532 reports 3533 test 3534 )) { 3535 *$command = sub { shift->rematein($command, @_); }; 3536 } 3537 } 3538 3539 package CPAN::LWP::UserAgent; 3540 use strict; 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 { 3549 $CPAN::Frontend->mywarn(" LWP::UserAgent not available\n"); 3550 } 3551 } 3552 3553 sub get_basic_credentials { 3554 my($self, $realm, $uri, $proxy) = @_; 3555 if ($USER && $PASSWD) { 3556 return ($USER, $PASSWD); 3557 } 3558 if ( $proxy ) { 3559 ($USER,$PASSWD) = $self->get_proxy_credentials(); 3560 } else { 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! 3576 (Note: to permanently configure username and password run 3577 o conf proxy_user your_username 3578 o conf proxy_pass your_password 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:"; 3599 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 { 3606 my $username_message = shift; 3607 my ($username,$password); 3608 3609 ExtUtils::MakeMaker->import(qw(prompt)); 3610 $username = prompt($username_message); 3611 if ($CPAN::META->has_inst("Term::ReadKey")) { 3612 Term::ReadKey::ReadMode("noecho"); 3613 } 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 3621 if ($CPAN::META->has_inst("Term::ReadKey")) { 3622 Term::ReadKey::ReadMode("restore"); 3623 } 3624 $CPAN::Frontend->myprint("\n\n"); 3625 return ($username,$password); 3626 } 3627 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 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. 3657 3658 sub mirror { 3659 my($self,$url,$aslocal) = @_; 3660 my $result = $self->SUPER::mirror($url,$aslocal); 3661 if ($result->code == 407) { 3662 undef $USER; 3663 undef $PASSWD; 3664 $result = $self->SUPER::mirror($url,$aslocal); 3665 } 3666 $result; 3667 } 3668 3669 package CPAN::FTP; 3670 use strict; 3671 3672 #-> sub CPAN::FTP::ftp_statistics 3673 # if they want to rewrite, they need to pass in a filehandle 3674 sub _ftp_statistics { 3675 my($self,$fh) = @_; 3676 my $locktype = $fh ? LOCK_EX : LOCK_SH; 3677 $fh ||= FileHandle->new; 3678 my $file = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); 3679 open $fh, "+>>$file" or $CPAN::Frontend->mydie("Could not open '$file': $!"); 3680 my $sleep = 1; 3681 my $waitstart; 3682 while (!CPAN::_flock($fh, $locktype|LOCK_NB)) { 3683 $waitstart ||= localtime(); 3684 if ($sleep>3) { 3685 $CPAN::Frontend->mywarn("Waiting for a read lock on '$file' (since $waitstart)\n"); 3686 } 3687 $CPAN::Frontend->mysleep($sleep); 3688 if ($sleep <= 3) { 3689 $sleep+=0.33; 3690 } elsif ($sleep <=6) { 3691 $sleep+=0.11; 3692 } 3693 } 3694 my $stats = eval { CPAN->_yaml_loadfile($file); }; 3695 if ($@) { 3696 if (ref $@) { 3697 if (ref $@ eq "CPAN::Exception::yaml_not_installed") { 3698 $CPAN::Frontend->myprint("Warning (usually harmless): $@"); 3699 return; 3700 } elsif (ref $@ eq "CPAN::Exception::yaml_process_error") { 3701 $CPAN::Frontend->mydie($@); 3702 } 3703 } else { 3704 $CPAN::Frontend->mydie($@); 3705 } 3706 } 3707 return $stats->[0]; 3708 } 3709 3710 #-> sub CPAN::FTP::_mytime 3711 sub _mytime () { 3712 if (CPAN->has_inst("Time::HiRes")) { 3713 return Time::HiRes::time(); 3714 } else { 3715 return time; 3716 } 3717 } 3718 3719 #-> sub CPAN::FTP::_new_stats 3720 sub _new_stats { 3721 my($self,$file) = @_; 3722 my $ret = { 3723 file => $file, 3724 attempts => [], 3725 start => _mytime, 3726 }; 3727 $ret; 3728 } 3729 3730 #-> sub CPAN::FTP::_add_to_statistics 3731 sub _add_to_statistics { 3732 my($self,$stats) = @_; 3733 my $yaml_module = CPAN::_yaml_module; 3734 $self->debug("yaml_module[$yaml_module]") if $CPAN::DEBUG; 3735 if ($CPAN::META->has_inst($yaml_module)) { 3736 $stats->{thesiteurl} = $ThesiteURL; 3737 if (CPAN->has_inst("Time::HiRes")) { 3738 $stats->{end} = Time::HiRes::time(); 3739 } else { 3740 $stats->{end} = time; 3741 } 3742 my $fh = FileHandle->new; 3743 my $time = time; 3744 my $sdebug = 0; 3745 my @debug; 3746 @debug = $time if $sdebug; 3747 my $fullstats = $self->_ftp_statistics($fh); 3748 close $fh; 3749 $fullstats->{history} ||= []; 3750 push @debug, scalar @{$fullstats->{history}} if $sdebug; 3751 push @debug, time if $sdebug; 3752 push @{$fullstats->{history}}, $stats; 3753 # arbitrary hardcoded constants until somebody demands to have 3754 # them settable; YAML.pm 0.62 is unacceptably slow with 999; 3755 # YAML::Syck 0.82 has no noticable performance problem with 999; 3756 while ( 3757 @{$fullstats->{history}} > 99 3758 || $time - $fullstats->{history}[0]{start} > 14*86400 3759 ) { 3760 shift @{$fullstats->{history}} 3761 } 3762 push @debug, scalar @{$fullstats->{history}} if $sdebug; 3763 push @debug, time if $sdebug; 3764 push @debug, scalar localtime($fullstats->{history}[0]{start}) if $sdebug; 3765 # need no eval because if this fails, it is serious 3766 my $sfile = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); 3767 CPAN->_yaml_dumpfile("$sfile.$$",$fullstats); 3768 if ( $sdebug ) { 3769 local $CPAN::DEBUG = 512; # FTP 3770 push @debug, time; 3771 CPAN->debug(sprintf("DEBUG history: before_read[%d]before[%d]at[%d]". 3772 "after[%d]at[%d]oldest[%s]dumped backat[%d]", 3773 @debug, 3774 )); 3775 } 3776 # Win32 cannot rename a file to an existing filename 3777 unlink($sfile) if ($^O eq 'MSWin32'); 3778 rename "$sfile.$$", $sfile 3779 or $CPAN::Frontend->mydie("Could not rename '$sfile.$$' to '$sfile': $!\n"); 3780 } 3781 } 3782 3783 # if file is CHECKSUMS, suggest the place where we got the file to be 3784 # checked from, maybe only for young files? 3785 #-> sub CPAN::FTP::_recommend_url_for 3786 sub _recommend_url_for { 3787 my($self, $file) = @_; 3788 my $urllist = $self->_get_urllist; 3789 if ($file =~ s|/CHECKSUMS(.gz)?$||) { 3790 my $fullstats = $self->_ftp_statistics(); 3791 my $history = $fullstats->{history} || []; 3792 while (my $last = pop @$history) { 3793 last if $last->{end} - time > 3600; # only young results are interesting 3794 next unless $last->{file}; # dirname of nothing dies! 3795 next unless $file eq File::Basename::dirname($last->{file}); 3796 return $last->{thesiteurl}; 3797 } 3798 } 3799 if ($CPAN::Config->{randomize_urllist} 3800 && 3801 rand(1) < $CPAN::Config->{randomize_urllist} 3802 ) { 3803 $urllist->[int rand scalar @$urllist]; 3804 } else { 3805 return (); 3806 } 3807 } 3808 3809 #-> sub CPAN::FTP::_get_urllist 3810 sub _get_urllist { 3811 my($self) = @_; 3812 $CPAN::Config->{urllist} ||= []; 3813 unless (ref $CPAN::Config->{urllist} eq 'ARRAY') { 3814 $CPAN::Frontend->mywarn("Malformed urllist; ignoring. Configuration file corrupt?\n"); 3815 $CPAN::Config->{urllist} = []; 3816 } 3817 my @urllist = grep { defined $_ and length $_ } @{$CPAN::Config->{urllist}}; 3818 for my $u (@urllist) { 3819 CPAN->debug("u[$u]") if $CPAN::DEBUG; 3820 if (UNIVERSAL::can($u,"text")) { 3821 $u->{TEXT} .= "/" unless substr($u->{TEXT},-1) eq "/"; 3822 } else { 3823 $u .= "/" unless substr($u,-1) eq "/"; 3824 $u = CPAN::URL->new(TEXT => $u, FROM => "USER"); 3825 } 3826 } 3827 \@urllist; 3828 } 3829 3830 #-> sub CPAN::FTP::ftp_get ; 3831 sub ftp_get { 3832 my($class,$host,$dir,$file,$target) = @_; 3833 $class->debug( 3834 qq[Going to fetch file [$file] from dir [$dir] 3835 on host [$host] as local [$target]\n] 3836 ) if $CPAN::DEBUG; 3837 my $ftp = Net::FTP->new($host); 3838 unless ($ftp) { 3839 $CPAN::Frontend->mywarn(" Could not connect to host '$host' with Net::FTP\n"); 3840 return; 3841 } 3842 return 0 unless defined $ftp; 3843 $ftp->debug(1) if $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG; 3844 $class->debug(qq[Going to login("anonymous","$Config::Config{cf_email}")]); 3845 unless ( $ftp->login("anonymous",$Config::Config{'cf_email'}) ) { 3846 my $msg = $ftp->message; 3847 $CPAN::Frontend->mywarn(" Couldn't login on $host: $msg"); 3848 return; 3849 } 3850 unless ( $ftp->cwd($dir) ) { 3851 my $msg = $ftp->message; 3852 $CPAN::Frontend->mywarn(" Couldn't cwd $dir: $msg"); 3853 return; 3854 } 3855 $ftp->binary; 3856 $class->debug(qq[Going to ->get("$file","$target")\n]) if $CPAN::DEBUG; 3857 unless ( $ftp->get($file,$target) ) { 3858 my $msg = $ftp->message; 3859 $CPAN::Frontend->mywarn(" Couldn't fetch $file from $host: $msg"); 3860 return; 3861 } 3862 $ftp->quit; # it's ok if this fails 3863 return 1; 3864 } 3865 3866 # If more accuracy is wanted/needed, Chris Leach sent me this patch... 3867 3868 # > *** /install/perl/live/lib/CPAN.pm- Wed Sep 24 13:08:48 1997 3869 # > --- /tmp/cp Wed Sep 24 13:26:40 1997 3870 # > *************** 3871 # > *** 1562,1567 **** 3872 # > --- 1562,1580 ---- 3873 # > return 1 if substr($url,0,4) eq "file"; 3874 # > return 1 unless $url =~ m|://([^/]+)|; 3875 # > my $host = $1; 3876 # > + my $proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'}; 3877 # > + if ($proxy) { 3878 # > + $proxy =~ m|://([^/:]+)|; 3879 # > + $proxy = $1; 3880 # > + my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'}; 3881 # > + if ($noproxy) { 3882 # > + if ($host !~ /$noproxy$/) { 3883 # > + $host = $proxy; 3884 # > + } 3885 # > + } else { 3886 # > + $host = $proxy; 3887 # > + } 3888 # > + } 3889 # > require Net::Ping; 3890 # > return 1 unless $Net::Ping::VERSION >= 2; 3891 # > my $p; 3892 3893 3894 #-> sub CPAN::FTP::localize ; 3895 sub localize { 3896 my($self,$file,$aslocal,$force) = @_; 3897 $force ||= 0; 3898 Carp::croak "Usage: ->localize(cpan_file,as_local_file[,$force])" 3899 unless defined $aslocal; 3900 $self->debug("file[$file] aslocal[$aslocal] force[$force]") 3901 if $CPAN::DEBUG; 3902 3903 if ($^O eq 'MacOS') { 3904 # Comment by AK on 2000-09-03: Uniq short filenames would be 3905 # available in CHECKSUMS file 3906 my($name, $path) = File::Basename::fileparse($aslocal, ''); 3907 if (length($name) > 31) { 3908 $name =~ s/( 3909 \.( 3910 readme(\.(gz|Z))? | 3911 (tar\.)?(gz|Z) | 3912 tgz | 3913 zip | 3914 pm\.(gz|Z) 3915 ) 3916 )$//x; 3917 my $suf = $1; 3918 my $size = 31 - length($suf); 3919 while (length($name) > $size) { 3920 chop $name; 3921 } 3922 $name .= $suf; 3923 $aslocal = File::Spec->catfile($path, $name); 3924 } 3925 } 3926 3927 if (-f $aslocal && -r _ && !($force & 1)) { 3928 my $size; 3929 if ($size = -s $aslocal) { 3930 $self->debug("aslocal[$aslocal]size[$size]") if $CPAN::DEBUG; 3931 return $aslocal; 3932 } else { 3933 # empty file from a previous unsuccessful attempt to download it 3934 unlink $aslocal or 3935 $CPAN::Frontend->mydie("Found a zero-length '$aslocal' that I ". 3936 "could not remove."); 3937 } 3938 } 3939 my($maybe_restore) = 0; 3940 if (-f $aslocal) { 3941 rename $aslocal, "$aslocal.bak$$"; 3942 $maybe_restore++; 3943 } 3944 3945 my($aslocal_dir) = File::Basename::dirname($aslocal); 3946 $self->mymkpath($aslocal_dir); # too early for file URLs / RT #28438 3947 # Inheritance is not easier to manage than a few if/else branches 3948 if ($CPAN::META->has_usable('LWP::UserAgent')) { 3949 unless ($Ua) { 3950 CPAN::LWP::UserAgent->config; 3951 eval {$Ua = CPAN::LWP::UserAgent->new;}; # Why is has_usable still not fit enough? 3952 if ($@) { 3953 $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n") 3954 if $CPAN::DEBUG; 3955 } else { 3956 my($var); 3957 $Ua->proxy('ftp', $var) 3958 if $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy}; 3959 $Ua->proxy('http', $var) 3960 if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy}; 3961 $Ua->no_proxy($var) 3962 if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy}; 3963 } 3964 } 3965 } 3966 for my $prx (qw(ftp_proxy http_proxy no_proxy)) { 3967 $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; 3968 } 3969 3970 # Try the list of urls for each single object. We keep a record 3971 # where we did get a file from 3972 my(@reordered,$last); 3973 my $ccurllist = $self->_get_urllist; 3974 $last = $#$ccurllist; 3975 if ($force & 2) { # local cpans probably out of date, don't reorder 3976 @reordered = (0..$last); 3977 } else { 3978 @reordered = 3979 sort { 3980 (substr($ccurllist->[$b],0,4) eq "file") 3981 <=> 3982 (substr($ccurllist->[$a],0,4) eq "file") 3983 or 3984 defined($ThesiteURL) 3985 and 3986 ($ccurllist->[$b] eq $ThesiteURL) 3987 <=> 3988 ($ccurllist->[$a] eq $ThesiteURL) 3989 } 0..$last; 3990 } 3991 my(@levels); 3992 $Themethod ||= ""; 3993 $self->debug("Themethod[$Themethod]reordered[@reordered]") if $CPAN::DEBUG; 3994 my @all_levels = ( 3995 ["dleasy", "file"], 3996 ["dleasy"], 3997 ["dlhard"], 3998 ["dlhardest"], 3999 ["dleasy", "http","defaultsites"], 4000 ["dlhard", "http","defaultsites"], 4001 ["dleasy", "ftp", "defaultsites"], 4002 ["dlhard", "ftp", "defaultsites"], 4003 ["dlhardest","", "defaultsites"], 4004 ); 4005 if ($Themethod) { 4006 @levels = grep {$_->[0] eq $Themethod} @all_levels; 4007 push @levels, grep {$_->[0] ne $Themethod} @all_levels; 4008 } else { 4009 @levels = @all_levels; 4010 } 4011 @levels = qw/dleasy/ if $^O eq 'MacOS'; 4012 my($levelno); 4013 local $ENV{FTP_PASSIVE} = 4014 exists $CPAN::Config->{ftp_passive} ? 4015 $CPAN::Config->{ftp_passive} : 1; 4016 my $ret; 4017 my $stats = $self->_new_stats($file); 4018 LEVEL: for $levelno (0..$#levels) { 4019 my $level_tuple = $levels[$levelno]; 4020 my($level,$scheme,$sitetag) = @$level_tuple; 4021 my $defaultsites = $sitetag && $sitetag eq "defaultsites"; 4022 my @urllist; 4023 if ($defaultsites) { 4024 unless (defined $connect_to_internet_ok) { 4025 $CPAN::Frontend->myprint(sprintf qq{ 4026 I would like to connect to one of the following sites to get '%s': 4027 4028 %s 4029 }, 4030 $file, 4031 join("",map { " ".$_->text."\n" } @CPAN::Defaultsites), 4032 ); 4033 my $answer = CPAN::Shell::colorable_makemaker_prompt("Is it OK to try to connect to the Internet?", "yes"); 4034 if ($answer =~ /^y/i) { 4035 $connect_to_internet_ok = 1; 4036 } else { 4037 $connect_to_internet_ok = 0; 4038 } 4039 } 4040 if ($connect_to_internet_ok) { 4041 @urllist = @CPAN::Defaultsites; 4042 } else { 4043 @urllist = (); 4044 } 4045 } else { 4046 my @host_seq = $level =~ /dleasy/ ? 4047 @reordered : 0..$last; # reordered has file and $Thesiteurl first 4048 @urllist = map { $ccurllist->[$_] } @host_seq; 4049 } 4050 $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; 4051 my $aslocal_tempfile = $aslocal . ".tmp" . $$; 4052 if (my $recommend = $self->_recommend_url_for($file)) { 4053 @urllist = grep { $_ ne $recommend } @urllist; 4054 unshift @urllist, $recommend; 4055 } 4056 $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; 4057 $ret = $self->hostdlxxx($level,$scheme,\@urllist,$file,$aslocal_tempfile,$stats); 4058 if ($ret) { 4059 CPAN->debug("ret[$ret]aslocal[$aslocal]") if $CPAN::DEBUG; 4060 if ($ret eq $aslocal_tempfile) { 4061 # if we got it exactly as we asked for, only then we 4062 # want to rename 4063 rename $aslocal_tempfile, $aslocal 4064 or $CPAN::Frontend->mydie("Error while trying to rename ". 4065 "'$ret' to '$aslocal': $!"); 4066 $ret = $aslocal; 4067 } 4068 $Themethod = $level; 4069 my $now = time; 4070 # utime $now, $now, $aslocal; # too bad, if we do that, we 4071 # might alter a local mirror 4072 $self->debug("level[$level]") if $CPAN::DEBUG; 4073 last LEVEL; 4074 } else { 4075 unlink $aslocal_tempfile; 4076 last if $CPAN::Signal; # need to cleanup 4077 } 4078 } 4079 if ($ret) { 4080 $stats->{filesize} = -s $ret; 4081 } 4082 $self->debug("before _add_to_statistics") if $CPAN::DEBUG; 4083 $self->_add_to_statistics($stats); 4084 $self->debug("after _add_to_statistics") if $CPAN::DEBUG; 4085 if ($ret) { 4086 unlink "$aslocal.bak$$"; 4087 return $ret; 4088 } 4089 unless ($CPAN::Signal) { 4090 my(@mess); 4091 local $" = " "; 4092 if (@{$CPAN::Config->{urllist}}) { 4093 push @mess, 4094 qq{Please check, if the URLs I found in your configuration file \(}. 4095 join(", ", @{$CPAN::Config->{urllist}}). 4096 qq{\) are valid.}; 4097 } else { 4098 push @mess, qq{Your urllist is empty!}; 4099 } 4100 push @mess, qq{The urllist can be edited.}, 4101 qq{E.g. with 'o conf urllist push ftp://myurl/'}; 4102 $CPAN::Frontend->mywarn(Text::Wrap::wrap("","","@mess"). "\n\n"); 4103 $CPAN::Frontend->mywarn("Could not fetch $file\n"); 4104 $CPAN::Frontend->mysleep(2); 4105 } 4106 if ($maybe_restore) { 4107 rename "$aslocal.bak$$", $aslocal; 4108 $CPAN::Frontend->myprint("Trying to get away with old file:\n" . 4109 $self->ls($aslocal)); 4110 return $aslocal; 4111 } 4112 return; 4113 } 4114 4115 sub mymkpath { 4116 my($self, $aslocal_dir) = @_; 4117 File::Path::mkpath($aslocal_dir); 4118 $CPAN::Frontend->mywarn(qq{Warning: You are not allowed to write into }. 4119 qq{directory "$aslocal_dir". 4120 I\'ll continue, but if you encounter problems, they may be due 4121 to insufficient permissions.\n}) unless -w $aslocal_dir; 4122 } 4123 4124 sub hostdlxxx { 4125 my $self = shift; 4126 my $level = shift; 4127 my $scheme = shift; 4128 my $h = shift; 4129 $h = [ grep /^\Q$scheme\E:/, @$h ] if $scheme; 4130 my $method = "host$level"; 4131 $self->$method($h, @_); 4132 } 4133 4134 sub _set_attempt { 4135 my($self,$stats,$method,$url) = @_; 4136 push @{$stats->{attempts}}, { 4137 method => $method, 4138 start => _mytime, 4139 url => $url, 4140 }; 4141 } 4142 4143 # package CPAN::FTP; 4144 sub hostdleasy { 4145 my($self,$host_seq,$file,$aslocal,$stats) = @_; 4146 my($ro_url); 4147 HOSTEASY: for $ro_url (@$host_seq) { 4148 $self->_set_attempt($stats,"dleasy",$ro_url); 4149 my $url .= "$ro_url$file"; 4150 $self->debug("localizing perlish[$url]") if $CPAN::DEBUG; 4151 if ($url =~ /^file:/) { 4152 my $l; 4153 if ($CPAN::META->has_inst('URI::URL')) { 4154 my $u = URI::URL->new($url); 4155 $l = $u->path; 4156 } else { # works only on Unix, is poorly constructed, but 4157 # hopefully better than nothing. 4158 # RFC 1738 says fileurl BNF is 4159 # fileurl = "file://" [ host | "localhost" ] "/" fpath 4160 # Thanks to "Mark D. Baushke" <mdb@cisco.com> for 4161 # the code 4162 ($l = $url) =~ s|^file://[^/]*/|/|; # discard the host part 4163 $l =~ s|^file:||; # assume they 4164 # meant 4165 # file://localhost 4166 $l =~ s|^/||s 4167 if ! -f $l && $l =~ m|^/\w:|; # e.g. /P: 4168 } 4169 $self->debug("local file[$l]") if $CPAN::DEBUG; 4170 if ( -f $l && -r _) { 4171 $ThesiteURL = $ro_url; 4172 return $l; 4173 } 4174 if ($l =~ /(.+)\.gz$/) { 4175 my $ungz = $1; 4176 if ( -f $ungz && -r _) { 4177 $ThesiteURL = $ro_url; 4178 return $ungz; 4179 } 4180 } 4181 # Maybe mirror has compressed it? 4182 if (-f "$l.gz") { 4183 $self->debug("found compressed $l.gz") if $CPAN::DEBUG; 4184 eval { CPAN::Tarzip->new("$l.gz")->gunzip($aslocal) }; 4185 if ( -f $aslocal) { 4186 $ThesiteURL = $ro_url; 4187 return $aslocal; 4188 } 4189 } 4190 $CPAN::Frontend->mywarn("Could not find '$l'\n"); 4191 } 4192 $self->debug("it was not a file URL") if $CPAN::DEBUG; 4193 if ($CPAN::META->has_usable('LWP')) { 4194 $CPAN::Frontend->myprint("Fetching with LWP: 4195 $url 4196 "); 4197 unless ($Ua) { 4198 CPAN::LWP::UserAgent->config; 4199 eval { $Ua = CPAN::LWP::UserAgent->new; }; 4200 if ($@) { 4201 $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n"); 4202 } 4203 } 4204 my $res = $Ua->mirror($url, $aslocal); 4205 if ($res->is_success) { 4206 $ThesiteURL = $ro_url; 4207 my $now = time; 4208 utime $now, $now, $aslocal; # download time is more 4209 # important than upload 4210 # time 4211 return $aslocal; 4212 } elsif ($url !~ /\.gz(?!\n)\Z/) { 4213 my $gzurl = "$url.gz"; 4214 $CPAN::Frontend->myprint("Fetching with LWP: 4215 $gzurl 4216 "); 4217 $res = $Ua->mirror($gzurl, "$aslocal.gz"); 4218 if ($res->is_success) { 4219 if (eval {CPAN::Tarzip->new("$aslocal.gz")->gunzip($aslocal)}) { 4220 $ThesiteURL = $ro_url; 4221 return $aslocal; 4222 } 4223 } 4224 } else { 4225 $CPAN::Frontend->myprint(sprintf( 4226 "LWP failed with code[%s] message[%s]\n", 4227 $res->code, 4228 $res->message, 4229 )); 4230 # Alan Burlison informed me that in firewall environments 4231 # Net::FTP can still succeed where LWP fails. So we do not 4232 # skip Net::FTP anymore when LWP is available. 4233 } 4234 } else { 4235 $CPAN::Frontend->mywarn(" LWP not available\n"); 4236 } 4237 return if $CPAN::Signal; 4238 if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { 4239 # that's the nice and easy way thanks to Graham 4240 $self->debug("recognized ftp") if $CPAN::DEBUG; 4241 my($host,$dir,$getfile) = ($1,$2,$3); 4242 if ($CPAN::META->has_usable('Net::FTP')) { 4243 $dir =~ s|/+|/|g; 4244 $CPAN::Frontend->myprint("Fetching with Net::FTP: 4245 $url 4246 "); 4247 $self->debug("getfile[$getfile]dir[$dir]host[$host]" . 4248 "aslocal[$aslocal]") if $CPAN::DEBUG; 4249 if (CPAN::FTP->ftp_get($host,$dir,$getfile,$aslocal)) { 4250 $ThesiteURL = $ro_url; 4251 return $aslocal; 4252 } 4253 if ($aslocal !~ /\.gz(?!\n)\Z/) { 4254 my $gz = "$aslocal.gz"; 4255 $CPAN::Frontend->myprint("Fetching with Net::FTP 4256 $url.gz 4257 "); 4258 if (CPAN::FTP->ftp_get($host, 4259 $dir, 4260 "$getfile.gz", 4261 $gz) && 4262 eval{CPAN::Tarzip->new($gz)->gunzip($aslocal)} 4263 ) { 4264 $ThesiteURL = $ro_url; 4265 return $aslocal; 4266 } 4267 } 4268 # next HOSTEASY; 4269 } else { 4270 CPAN->debug("Net::FTP does not count as usable atm") if $CPAN::DEBUG; 4271 } 4272 } 4273 if ( 4274 UNIVERSAL::can($ro_url,"text") 4275 and 4276 $ro_url->{FROM} eq "USER" 4277 ) { 4278 ##address #17973: default URLs should not try to override 4279 ##user-defined URLs just because LWP is not available 4280 my $ret = $self->hostdlhard([$ro_url],$file,$aslocal,$stats); 4281 return $ret if $ret; 4282 } 4283 return if $CPAN::Signal; 4284 } 4285 } 4286 4287 # package CPAN::FTP; 4288 sub hostdlhard { 4289 my($self,$host_seq,$file,$aslocal,$stats) = @_; 4290 4291 # Came back if Net::FTP couldn't establish connection (or 4292 # failed otherwise) Maybe they are behind a firewall, but they 4293 # gave us a socksified (or other) ftp program... 4294 4295 my($ro_url); 4296 my($devnull) = $CPAN::Config->{devnull} || ""; 4297 # < /dev/null "; 4298 my($aslocal_dir) = File::Basename::dirname($aslocal); 4299 File::Path::mkpath($aslocal_dir); 4300 HOSTHARD: for $ro_url (@$host_seq) { 4301 $self->_set_attempt($stats,"dlhard",$ro_url); 4302 my $url = "$ro_url$file"; 4303 my($proto,$host,$dir,$getfile); 4304 4305 # Courtesy Mark Conty mark_conty@cargill.com change from 4306 # if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { 4307 # to 4308 if ($url =~ m|^([^:]+)://(.*?)/(.*)/(.*)|) { 4309 # proto not yet used 4310 ($proto,$host,$dir,$getfile) = ($1,$2,$3,$4); 4311 } else { 4312 next HOSTHARD; # who said, we could ftp anything except ftp? 4313 } 4314 next HOSTHARD if $proto eq "file"; # file URLs would have had 4315 # success above. Likely a bogus URL 4316 4317 $self->debug("localizing funkyftpwise[$url]") if $CPAN::DEBUG; 4318 4319 # Try the most capable first and leave ncftp* for last as it only 4320 # does FTP. 4321 DLPRG: for my $f (qw(curl wget lynx ncftpget ncftp)) { 4322 my $funkyftp = CPAN::HandleConfig->safe_quote($CPAN::Config->{$f}); 4323 next unless defined $funkyftp; 4324 next if $funkyftp =~ /^\s*$/; 4325 4326 my($asl_ungz, $asl_gz); 4327 ($asl_ungz = $aslocal) =~ s/\.gz//; 4328 $asl_gz = "$asl_ungz.gz"; 4329 4330 my($src_switch) = ""; 4331 my($chdir) = ""; 4332 my($stdout_redir) = " > $asl_ungz"; 4333 if ($f eq "lynx") { 4334 $src_switch = " -source"; 4335 } elsif ($f eq "ncftp") { 4336 $src_switch = " -c"; 4337 } elsif ($f eq "wget") { 4338 $src_switch = " -O $asl_ungz"; 4339 $stdout_redir = ""; 4340 } elsif ($f eq 'curl') { 4341 $src_switch = ' -L -f -s -S --netrc-optional'; 4342 } 4343 4344 if ($f eq "ncftpget") { 4345 $chdir = "cd $aslocal_dir && "; 4346 $stdout_redir = ""; 4347 } 4348 $CPAN::Frontend->myprint( 4349 qq[ 4350 Trying with "$funkyftp$src_switch" to get 4351 $url 4352 ]); 4353 my($system) = 4354 "$chdir$funkyftp$src_switch \"$url\" $devnull$stdout_redir"; 4355 $self->debug("system[$system]") if $CPAN::DEBUG; 4356 my($wstatus) = system($system); 4357 if ($f eq "lynx") { 4358 # lynx returns 0 when it fails somewhere 4359 if (-s $asl_ungz) { 4360 my $content = do { local *FH; 4361 open FH, $asl_ungz or die; 4362 local $/; 4363 <FH> }; 4364 if ($content =~ /^<.*(<title>[45]|Error [45])/si) { 4365 $CPAN::Frontend->mywarn(qq{ 4366 No success, the file that lynx has downloaded looks like an error message: 4367 $content 4368 }); 4369 $CPAN::Frontend->mysleep(1); 4370 next DLPRG; 4371 } 4372 } else { 4373 $CPAN::Frontend->myprint(qq{ 4374 No success, the file that lynx has downloaded is an empty file. 4375 }); 4376 next DLPRG; 4377 } 4378 } 4379 if ($wstatus == 0) { 4380 if (-s $aslocal) { 4381 # Looks good 4382 } elsif ($asl_ungz ne $aslocal) { 4383 # test gzip integrity 4384 if (eval{CPAN::Tarzip->new($asl_ungz)->gtest}) { 4385 # e.g. foo.tar is gzipped --> foo.tar.gz 4386 rename $asl_ungz, $aslocal; 4387 } else { 4388 eval{CPAN::Tarzip->new($asl_gz)->gzip($asl_ungz)}; 4389 } 4390 } 4391 $ThesiteURL = $ro_url; 4392 return $aslocal; 4393 } elsif ($url !~ /\.gz(?!\n)\Z/) { 4394 unlink $asl_ungz if 4395 -f $asl_ungz && -s _ == 0; 4396 my $gz = "$aslocal.gz"; 4397 my $gzurl = "$url.gz"; 4398 $CPAN::Frontend->myprint( 4399 qq[ 4400 Trying with "$funkyftp$src_switch" to get 4401 $url.gz 4402 ]); 4403 my($system) = "$funkyftp$src_switch \"$url.gz\" $devnull > $asl_gz"; 4404 $self->debug("system[$system]") if $CPAN::DEBUG; 4405 my($wstatus); 4406 if (($wstatus = system($system)) == 0 4407 && 4408 -s $asl_gz 4409 ) { 4410 # test gzip integrity 4411 my $ct = eval{CPAN::Tarzip->new($asl_gz)}; 4412 if ($ct && $ct->gtest) { 4413 $ct->gunzip($aslocal); 4414 } else { 4415 # somebody uncompressed file for us? 4416 rename $asl_ungz, $aslocal; 4417 } 4418 $ThesiteURL = $ro_url; 4419 return $aslocal; 4420 } else { 4421 unlink $asl_gz if -f $asl_gz; 4422 } 4423 } else { 4424 my $estatus = $wstatus >> 8; 4425 my $size = -f $aslocal ? 4426 ", left\n$aslocal with size ".-s _ : 4427 "\nWarning: expected file [$aslocal] doesn't exist"; 4428 $CPAN::Frontend->myprint(qq{ 4429 System call "$system" 4430 returned status $estatus (wstat $wstatus)$size 4431 }); 4432 } 4433 return if $CPAN::Signal; 4434 } # transfer programs 4435 } # host 4436 } 4437 4438 # package CPAN::FTP; 4439 sub hostdlhardest { 4440 my($self,$host_seq,$file,$aslocal,$stats) = @_; 4441 4442 return unless @$host_seq; 4443 my($ro_url); 4444 my($aslocal_dir) = File::Basename::dirname($aslocal); 4445 File::Path::mkpath($aslocal_dir); 4446 my $ftpbin = $CPAN::Config->{ftp}; 4447 unless ($ftpbin && length $ftpbin && MM->maybe_command($ftpbin)) { 4448 $CPAN::Frontend->myprint("No external ftp command available\n\n"); 4449 return; 4450 } 4451 $CPAN::Frontend->mywarn(qq{ 4452 As a last ressort we now switch to the external ftp command '$ftpbin' 4453 to get '$aslocal'. 4454 4455 Doing so often leads to problems that are hard to diagnose. 4456 4457 If you're victim of such problems, please consider unsetting the ftp 4458 config variable with 4459 4460 o conf ftp "" 4461 o conf commit 4462 4463 }); 4464 $CPAN::Frontend->mysleep(2); 4465 HOSTHARDEST: for $ro_url (@$host_seq) { 4466 $self->_set_attempt($stats,"dlhardest",$ro_url); 4467 my $url = "$ro_url$file"; 4468 $self->debug("localizing ftpwise[$url]") if $CPAN::DEBUG; 4469 unless ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { 4470 next; 4471 } 4472 my($host,$dir,$getfile) = ($1,$2,$3); 4473 my $timestamp = 0; 4474 my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, 4475 $ctime