| ... | ... |
@@ -27,3 +27,4 @@ user/templates/* |
| 27 | 27 |
!user/templates/.gitignore |
| 28 | 28 |
db/* |
| 29 | 29 |
!db/.gitignore |
| 30 |
+extlib |
| ... | ... |
@@ -1,19 +0,0 @@ |
| 1 |
-1.00 |
|
| 2 |
- - First major release! |
|
| 3 |
-0.05 |
|
| 4 |
- - added reverse proxy support |
|
| 5 |
-0.04 |
|
| 6 |
- - added encoding option |
|
| 7 |
- - added text_exts option |
|
| 8 |
- - added prevent_xss option |
|
| 9 |
- - fixed many bugs. |
|
| 10 |
-0.03 (beta) |
|
| 11 |
- - don't overwrite gitweblite.conf |
|
| 12 |
- - config file become JSON. |
|
| 13 |
- - URL become more restful. |
|
| 14 |
- - Git command automatically detect |
|
| 15 |
-0.02 (beta) |
|
| 16 |
- - fixed blob, blog_plain, commitdiff, commitdiff_plain encoding bug |
|
| 17 |
- |
|
| 18 |
-0.01 (beta) |
|
| 19 |
- - first beta release. |
| ... | ... |
@@ -2,6 +2,15 @@ gitprep |
| 2 | 2 |
|
| 3 | 3 |
Github clone. Serve git repository. |
| 4 | 4 |
|
| 5 |
+Features |
|
| 6 |
+ |
|
| 7 |
+ - Github clone |
|
| 8 |
+ - Perl 5.8.7+ only needed |
|
| 9 |
+ |
|
| 10 |
+Installation |
|
| 11 |
+ |
|
| 12 |
+ perl cpanm -L extlib --installdeps . |
|
| 13 |
+ |
|
| 5 | 14 |
Operation |
| 6 | 15 |
|
| 7 | 16 |
Start |
| ... | ... |
@@ -11,7 +20,7 @@ Operation |
| 11 | 20 |
Application start in back ground. Port is 10020. |
| 12 | 21 |
You can access the following URL. |
| 13 | 22 |
|
| 14 |
- http://localhost:10010 |
|
| 23 |
+ http://localhost:10020 |
|
| 15 | 24 |
|
| 16 | 25 |
If you change port, edit githublite.conf. |
| 17 | 26 |
|
| ... | ... |
@@ -0,0 +1,4 @@ |
| 1 |
+requires 'DBI', '== 1.621'; |
|
| 2 |
+requires 'DBD::SQLite', '== 1.37'; |
|
| 3 |
+requires 'Object::Simple', '== 3.08'; |
|
| 4 |
+requires 'DBIx::Custom', '== 0.28'; |
| ... | ... |
@@ -18,7 +18,7 @@ my %fatpacked; |
| 18 | 18 |
|
| 19 | 19 |
$fatpacked{"App/cpanminus.pm"} = <<'APP_CPANMINUS';
|
| 20 | 20 |
package App::cpanminus; |
| 21 |
- our $VERSION = "1.5007"; |
|
| 21 |
+ our $VERSION = "1.6005"; |
|
| 22 | 22 |
|
| 23 | 23 |
=head1 NAME |
| 24 | 24 |
|
| ... | ... |
@@ -28,7 +28,7 @@ $fatpacked{"App/cpanminus.pm"} = <<'APP_CPANMINUS';
|
| 28 | 28 |
|
| 29 | 29 |
cpanm Module |
| 30 | 30 |
|
| 31 |
- Run C<cpanm -h> for more options. |
|
| 31 |
+ Run C<cpanm -h> or C<perldoc cpanm> for more options. |
|
| 32 | 32 |
|
| 33 | 33 |
=head1 DESCRIPTION |
| 34 | 34 |
|
| ... | ... |
@@ -133,10 +133,10 @@ $fatpacked{"App/cpanminus.pm"} = <<'APP_CPANMINUS';
|
| 133 | 133 |
|
| 134 | 134 |
=head2 Zero-conf? How does this module get/parse/update the CPAN index? |
| 135 | 135 |
|
| 136 |
- It queries the CPAN Meta DB site running on Google AppEngine at |
|
| 137 |
- L<http://cpanmetadb.plackperl.org/>. The site is updated every hour to reflect |
|
| 138 |
- the latest changes from fast syncing mirrors. The script then also falls back |
|
| 139 |
- to scrape the site L<http://search.cpan.org/>. |
|
| 136 |
+ It queries the CPAN Meta DB site at L<http://cpanmetadb.plackperl.org/>. |
|
| 137 |
+ The site is updated at least every hour to reflect the latest changes |
|
| 138 |
+ from fast syncing mirrors. The script then also falls back to query the |
|
| 139 |
+ module at L<http://metacpan.org/> using its wonderful API. |
|
| 140 | 140 |
|
| 141 | 141 |
Fetched files are unpacked in C<~/.cpanm> and automatically cleaned up |
| 142 | 142 |
periodically. You can configure the location of this with the |
| ... | ... |
@@ -247,15 +247,11 @@ $fatpacked{"App/cpanminus.pm"} = <<'APP_CPANMINUS';
|
| 247 | 247 |
|
| 248 | 248 |
=item L<JSON::PP> Copyright 2007−2011 by Makamaka Hannyaharamitu |
| 249 | 249 |
|
| 250 |
- =item L<CPAN::Meta> Copyright (c) 2010 by David Golden and Ricardo Signes |
|
| 250 |
+ =item L<CPAN::Meta>, L<CPAN::Meta::Requirements> Copyright (c) 2010 by David Golden and Ricardo Signes |
|
| 251 | 251 |
|
| 252 |
- =item L<Try::Tiny> Copyright (c) 2009 Yuval Kogman |
|
| 252 |
+ =item L<CPAN::Meta::YAML> Copyright 2010 Adam Kennedy |
|
| 253 | 253 |
|
| 254 |
- =item L<parent> Copyright (c) 2007-10 Max Maischein |
|
| 255 |
- |
|
| 256 |
- =item L<Version::Requirements> copyright (c) 2010 by Ricardo Signes |
|
| 257 |
- |
|
| 258 |
- =item L<CPAN::Meta::YAML> copyright (c) 2010 by Adam Kennedy |
|
| 254 |
+ =item L<File::pushd> Copyright 2012 David Golden |
|
| 259 | 255 |
|
| 260 | 256 |
=back |
| 261 | 257 |
|
| ... | ... |
@@ -314,11 +310,13 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 314 | 310 |
use strict; |
| 315 | 311 |
use Config; |
| 316 | 312 |
use Cwd (); |
| 313 |
+ use App::cpanminus; |
|
| 317 | 314 |
use File::Basename (); |
| 318 | 315 |
use File::Find (); |
| 319 | 316 |
use File::Path (); |
| 320 | 317 |
use File::Spec (); |
| 321 | 318 |
use File::Copy (); |
| 319 |
+ use File::Temp (); |
|
| 322 | 320 |
use Getopt::Long (); |
| 323 | 321 |
use Parse::CPAN::Meta; |
| 324 | 322 |
use Symbol (); |
| ... | ... |
@@ -326,18 +324,47 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 326 | 324 |
use constant WIN32 => $^O eq 'MSWin32'; |
| 327 | 325 |
use constant SUNOS => $^O eq 'solaris'; |
| 328 | 326 |
|
| 329 |
- our $VERSION = "1.5007"; |
|
| 327 |
+ our $VERSION = $App::cpanminus::VERSION; |
|
| 328 |
+ |
|
| 329 |
+ if ($INC{"App/FatPacker/Trace.pm"}) {
|
|
| 330 |
+ require JSON::PP; |
|
| 331 |
+ require CPAN::Meta::YAML; |
|
| 332 |
+ require CPAN::Meta::Prereqs; |
|
| 333 |
+ require version::vpp; |
|
| 334 |
+ require File::pushd; |
|
| 335 |
+ } |
|
| 330 | 336 |
|
| 331 | 337 |
my $quote = WIN32 ? q/"/ : q/'/; |
| 332 | 338 |
|
| 339 |
+ sub agent {
|
|
| 340 |
+ my $self = shift; |
|
| 341 |
+ "cpanminus/$VERSION perl/$]"; |
|
| 342 |
+ } |
|
| 343 |
+ |
|
| 344 |
+ sub determine_home {
|
|
| 345 |
+ my $class = shift; |
|
| 346 |
+ |
|
| 347 |
+ my $homedir = $ENV{HOME}
|
|
| 348 |
+ || eval { require File::HomeDir; File::HomeDir->my_home }
|
|
| 349 |
+ || join('', @ENV{qw(HOMEDRIVE HOMEPATH)}); # Win32
|
|
| 350 |
+ |
|
| 351 |
+ if (WIN32) {
|
|
| 352 |
+ require Win32; # no fatpack |
|
| 353 |
+ $homedir = Win32::GetShortPathName($homedir); |
|
| 354 |
+ } |
|
| 355 |
+ |
|
| 356 |
+ return "$homedir/.cpanm"; |
|
| 357 |
+ } |
|
| 358 |
+ |
|
| 333 | 359 |
sub new {
|
| 334 | 360 |
my $class = shift; |
| 335 | 361 |
|
| 336 | 362 |
bless {
|
| 337 |
- home => "$ENV{HOME}/.cpanm",
|
|
| 363 |
+ home => $class->determine_home, |
|
| 338 | 364 |
cmd => 'install', |
| 339 | 365 |
seen => {},
|
| 340 | 366 |
notest => undef, |
| 367 |
+ test_only => undef, |
|
| 341 | 368 |
installdeps => undef, |
| 342 | 369 |
force => undef, |
| 343 | 370 |
sudo => undef, |
| ... | ... |
@@ -371,6 +398,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 371 | 398 |
format => 'tree', |
| 372 | 399 |
save_dists => undef, |
| 373 | 400 |
skip_configure => 0, |
| 401 |
+ verify => 0, |
|
| 374 | 402 |
@_, |
| 375 | 403 |
}, $class; |
| 376 | 404 |
} |
| ... | ... |
@@ -391,8 +419,10 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 391 | 419 |
Getopt::Long::GetOptions( |
| 392 | 420 |
'f|force' => sub { $self->{skip_installed} = 0; $self->{force} = 1 },
|
| 393 | 421 |
'n|notest!' => \$self->{notest},
|
| 422 |
+ 'test-only' => sub { $self->{notest} = 0; $self->{skip_installed} = 0; $self->{test_only} = 1 },
|
|
| 394 | 423 |
'S|sudo!' => \$self->{sudo},
|
| 395 | 424 |
'v|verbose' => sub { $self->{verbose} = $self->{interactive} = 1 },
|
| 425 |
+ 'verify!' => \$self->{verify},
|
|
| 396 | 426 |
'q|quiet!' => \$self->{quiet},
|
| 397 | 427 |
'h|help' => sub { $self->{action} = 'show_help' },
|
| 398 | 428 |
'V|version' => sub { $self->{action} = 'show_version' },
|
| ... | ... |
@@ -405,7 +435,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 405 | 435 |
}, |
| 406 | 436 |
'mirror=s@' => $self->{mirrors},
|
| 407 | 437 |
'mirror-only!' => \$self->{mirror_only},
|
| 408 |
- 'mirror-index=s' => sub { $self->{mirror_index} = $_[1]; $self->{mirror_only} = 1 },
|
|
| 438 |
+ 'mirror-index=s' => \$self->{mirror_index},
|
|
| 409 | 439 |
'cascade-search!' => \$self->{cascade_search},
|
| 410 | 440 |
'prompt!' => \$self->{prompt},
|
| 411 | 441 |
'installdeps' => \$self->{installdeps},
|
| ... | ... |
@@ -416,7 +446,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 416 | 446 |
'i|install' => sub { $self->{cmd} = 'install' },
|
| 417 | 447 |
'info' => sub { $self->{cmd} = 'info' },
|
| 418 | 448 |
'look' => sub { $self->{cmd} = 'look'; $self->{skip_installed} = 0 },
|
| 419 |
- 'self-upgrade' => sub { $self->{cmd} = 'install'; $self->{skip_installed} = 1; push @ARGV, 'App::cpanminus' },
|
|
| 449 |
+ 'self-upgrade' => sub { $self->check_upgrade; $self->{cmd} = 'install'; $self->{skip_installed} = 1; push @ARGV, 'App::cpanminus' },
|
|
| 420 | 450 |
'uninst-shadows!' => \$self->{uninstall_shadows},
|
| 421 | 451 |
'lwp!' => \$self->{try_lwp},
|
| 422 | 452 |
'wget!' => \$self->{try_wget},
|
| ... | ... |
@@ -430,7 +460,8 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 430 | 460 |
$self->{save_dists} = $self->maybe_abs($_[1]);
|
| 431 | 461 |
}, |
| 432 | 462 |
'skip-configure!' => \$self->{skip_configure},
|
| 433 |
- 'metacpan' => \$self->{metacpan},
|
|
| 463 |
+ 'dev!' => \$self->{dev_release},
|
|
| 464 |
+ 'metacpan!' => \$self->{metacpan},
|
|
| 434 | 465 |
); |
| 435 | 466 |
|
| 436 | 467 |
if (!@ARGV && $0 ne '-' && !-t STDIN){ # e.g. # cpanm < author/requires.cpanm
|
| ... | ... |
@@ -441,6 +472,33 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 441 | 472 |
$self->{argv} = \@ARGV;
|
| 442 | 473 |
} |
| 443 | 474 |
|
| 475 |
+ sub check_upgrade {
|
|
| 476 |
+ if ($0 !~ /^$Config{installsitebin}/) {
|
|
| 477 |
+ if ($0 =~ m!perlbrew/bin!) {
|
|
| 478 |
+ warn <<WARN; |
|
| 479 |
+ It appears your cpanm executable was installed via `perlbrew install-cpanm`. |
|
| 480 |
+ cpanm --self-upgrade won't upgrade the version of cpanm you're running. |
|
| 481 |
+ |
|
| 482 |
+ Run the following command to get it upgraded. |
|
| 483 |
+ |
|
| 484 |
+ perlbrew install-cpanm |
|
| 485 |
+ |
|
| 486 |
+ WARN |
|
| 487 |
+ } else {
|
|
| 488 |
+ warn <<WARN; |
|
| 489 |
+ You are running cpanm from the path where your current perl won't install executables to. |
|
| 490 |
+ Because of that, cpanm --self-upgrade won't upgrade the version of cpanm you're running. |
|
| 491 |
+ |
|
| 492 |
+ cpanm path : $0 |
|
| 493 |
+ Install path : $Config{installsitebin}
|
|
| 494 |
+ |
|
| 495 |
+ It means you either installed cpanm globally with system perl, or use distro packages such |
|
| 496 |
+ as rpm or apt-get, and you have to use them again to upgrade cpanm. |
|
| 497 |
+ WARN |
|
| 498 |
+ } |
|
| 499 |
+ } |
|
| 500 |
+ } |
|
| 501 |
+ |
|
| 444 | 502 |
sub check_libs {
|
| 445 | 503 |
my $self = shift; |
| 446 | 504 |
return if $self->{_checked}++;
|
| ... | ... |
@@ -453,11 +511,39 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 453 | 511 |
} |
| 454 | 512 |
} |
| 455 | 513 |
|
| 514 |
+ sub setup_verify {
|
|
| 515 |
+ my $self = shift; |
|
| 516 |
+ |
|
| 517 |
+ my $has_modules = eval { require Module::Signature; require Digest::SHA; 1 };
|
|
| 518 |
+ $self->{cpansign} = $self->which('cpansign');
|
|
| 519 |
+ |
|
| 520 |
+ unless ($has_modules && $self->{cpansign}) {
|
|
| 521 |
+ warn "WARNING: Module::Signature and Digest::SHA is required for distribution verifications.\n"; |
|
| 522 |
+ $self->{verify} = 0;
|
|
| 523 |
+ } |
|
| 524 |
+ } |
|
| 525 |
+ |
|
| 526 |
+ sub parse_module_args {
|
|
| 527 |
+ my($self, $module) = @_; |
|
| 528 |
+ |
|
| 529 |
+ # Plack@1.2 -> Plack~"==1.2" |
|
| 530 |
+ # BUT don't expand @ in git URLs |
|
| 531 |
+ $module =~ s/^([A-Za-z0-9_:]+)@([v\d\._]+)$/$1~== $2/; |
|
| 532 |
+ |
|
| 533 |
+ # Plack~1.20, DBI~"> 1.0, <= 2.0" |
|
| 534 |
+ if ($module =~ /\~[v\d\._,\!<>= ]+$/) {
|
|
| 535 |
+ return split /\~/, $module, 2; |
|
| 536 |
+ } else {
|
|
| 537 |
+ return $module, undef; |
|
| 538 |
+ } |
|
| 539 |
+ } |
|
| 540 |
+ |
|
| 456 | 541 |
sub doit {
|
| 457 | 542 |
my $self = shift; |
| 458 | 543 |
|
| 459 | 544 |
$self->setup_home; |
| 460 | 545 |
$self->init_tools; |
| 546 |
+ $self->setup_verify if $self->{verify};
|
|
| 461 | 547 |
|
| 462 | 548 |
if (my $action = $self->{action}) {
|
| 463 | 549 |
$self->$action() and return 1; |
| ... | ... |
@@ -477,12 +563,12 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 477 | 563 |
$module = join '::', grep { $_ } File::Spec->splitdir($dirs), $file;
|
| 478 | 564 |
} |
| 479 | 565 |
|
| 480 |
- ($module, my $version) = split /\~/, $module, 2; |
|
| 481 |
- if ($self->{skip_satisfied} or defined $version) {
|
|
| 566 |
+ ($module, my $version) = $self->parse_module_args($module); |
|
| 567 |
+ if ($self->{skip_satisfied}) {
|
|
| 482 | 568 |
$self->check_libs; |
| 483 | 569 |
my($ok, $local) = $self->check_module($module, $version || 0); |
| 484 | 570 |
if ($ok) {
|
| 485 |
- $self->diag("You have $module (" . ($local || 'undef') . ")\n", 1);
|
|
| 571 |
+ $self->diag("You have $module ($local)\n", 1);
|
|
| 486 | 572 |
next; |
| 487 | 573 |
} |
| 488 | 574 |
} |
| ... | ... |
@@ -504,6 +590,13 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 504 | 590 |
if ($self->{scandeps}) {
|
| 505 | 591 |
$self->dump_scandeps(); |
| 506 | 592 |
} |
| 593 |
+ # Workaround for older File::Temp's |
|
| 594 |
+ # where creating a tempdir with an implicit $PWD |
|
| 595 |
+ # causes tempdir non-cleanup if $PWD changes |
|
| 596 |
+ # as paths are stored internally without being resolved |
|
| 597 |
+ # absolutely. |
|
| 598 |
+ # https://rt.cpan.org/Public/Bug/Display.html?id=44924 |
|
| 599 |
+ $self->chdir($cwd); |
|
| 507 | 600 |
|
| 508 | 601 |
return !@fail; |
| 509 | 602 |
} |
| ... | ... |
@@ -595,7 +688,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 595 | 688 |
open my $fh, '<', $file or return; |
| 596 | 689 |
my $found; |
| 597 | 690 |
while (<$fh>) {
|
| 598 |
- if (m!^\Q$module\E\s+([\w\.]+)\s+(.*)!m) {
|
|
| 691 |
+ if (m!^\Q$module\E\s+([\w\.]+)\s+(\S*)!m) {
|
|
| 599 | 692 |
$found = $self->cpan_module($module, $2, $1); |
| 600 | 693 |
last; |
| 601 | 694 |
} |
| ... | ... |
@@ -604,66 +697,227 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 604 | 697 |
return $found unless $self->{cascade_search};
|
| 605 | 698 |
|
| 606 | 699 |
if ($found) {
|
| 607 |
- if (!$version or |
|
| 608 |
- version->new($found->{version} || 0) >= version->new($version)) {
|
|
| 700 |
+ if ($self->satisfy_version($module, $found->{module_version}, $version)) {
|
|
| 609 | 701 |
return $found; |
| 610 | 702 |
} else {
|
| 611 |
- $self->chat("Found $module version $found->{version} < $version.\n");
|
|
| 703 |
+ $self->chat("Found $module $found->{module_version} which doesn't satisfy $version.\n");
|
|
| 612 | 704 |
} |
| 613 | 705 |
} |
| 614 | 706 |
|
| 615 | 707 |
return; |
| 616 | 708 |
} |
| 617 | 709 |
|
| 618 |
- sub search_module {
|
|
| 710 |
+ sub with_version_range {
|
|
| 711 |
+ my($self, $version) = @_; |
|
| 712 |
+ defined($version) && $version =~ /[<>=]/; |
|
| 713 |
+ } |
|
| 714 |
+ |
|
| 715 |
+ sub encode_json {
|
|
| 716 |
+ my($self, $data) = @_; |
|
| 717 |
+ require JSON::PP; |
|
| 718 |
+ |
|
| 719 |
+ my $json = JSON::PP::encode_json($data); |
|
| 720 |
+ $json =~ s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg;
|
|
| 721 |
+ $json; |
|
| 722 |
+ } |
|
| 723 |
+ |
|
| 724 |
+ # TODO extract this as a module? |
|
| 725 |
+ sub version_to_query {
|
|
| 619 | 726 |
my($self, $module, $version) = @_; |
| 620 | 727 |
|
| 621 |
- unless ($self->{mirror_only}) {
|
|
| 622 |
- if ($self->{metacpan}) {
|
|
| 623 |
- require JSON::PP; |
|
| 624 |
- $self->chat("Searching $module on metacpan ...\n");
|
|
| 625 |
- my $module_uri = "http://api.metacpan.org/module/$module"; |
|
| 626 |
- my $module_json = $self->get($module_uri); |
|
| 627 |
- my $module_meta = eval { JSON::PP::decode_json($module_json) };
|
|
| 628 |
- if ($module_meta && $module_meta->{distribution}) {
|
|
| 629 |
- my $dist_uri = "http://api.metacpan.org/release/$module_meta->{distribution}";
|
|
| 630 |
- my $dist_json = $self->get($dist_uri); |
|
| 631 |
- my $dist_meta = eval { JSON::PP::decode_json($dist_json) };
|
|
| 632 |
- if ($dist_meta && $dist_meta->{download_url}) {
|
|
| 633 |
- (my $distfile = $dist_meta->{download_url}) =~ s!.+/authors/id/!!;
|
|
| 634 |
- local $self->{mirrors} = $self->{mirrors};
|
|
| 635 |
- if ($dist_meta->{stat}->{mtime} > time()-24*60*60) {
|
|
| 636 |
- $self->{mirrors} = ['http://cpan.metacpan.org'];
|
|
| 637 |
- } |
|
| 638 |
- return $self->cpan_module($module, $distfile, $dist_meta->{version});
|
|
| 639 |
- } |
|
| 728 |
+ require CPAN::Meta::Requirements; |
|
| 729 |
+ |
|
| 730 |
+ my $requirements = CPAN::Meta::Requirements->new; |
|
| 731 |
+ $requirements->add_string_requirement($module, $version || '0'); |
|
| 732 |
+ |
|
| 733 |
+ my $req = $requirements->requirements_for_module($module); |
|
| 734 |
+ |
|
| 735 |
+ if ($req =~ s/^==\s*//) {
|
|
| 736 |
+ return {
|
|
| 737 |
+ term => { 'module.version' => $req },
|
|
| 738 |
+ }; |
|
| 739 |
+ } elsif ($req !~ /\s/) {
|
|
| 740 |
+ return {
|
|
| 741 |
+ range => { 'module.version_numified' => { 'gte' => $self->numify_ver($req) } },
|
|
| 742 |
+ }; |
|
| 743 |
+ } else {
|
|
| 744 |
+ my %ops = qw(< lt <= lte > gt >= gte); |
|
| 745 |
+ my(%range, @exclusion); |
|
| 746 |
+ my @requirements = split /,\s*/, $req; |
|
| 747 |
+ for my $r (@requirements) {
|
|
| 748 |
+ if ($r =~ s/^([<>]=?)\s*//) {
|
|
| 749 |
+ $range{$ops{$1}} = $self->numify_ver($r);
|
|
| 750 |
+ } elsif ($r =~ s/\!=\s*//) {
|
|
| 751 |
+ push @exclusion, $self->numify_ver($r); |
|
| 640 | 752 |
} |
| 641 |
- $self->diag_fail("Finding $module on metacpan failed.");
|
|
| 642 | 753 |
} |
| 643 | 754 |
|
| 644 |
- $self->chat("Searching $module on cpanmetadb ...\n");
|
|
| 645 |
- my $uri = "http://cpanmetadb.plackperl.org/v1.0/package/$module"; |
|
| 646 |
- my $yaml = $self->get($uri); |
|
| 647 |
- my $meta = $self->parse_meta_string($yaml); |
|
| 648 |
- if ($meta && $meta->{distfile}) {
|
|
| 649 |
- return $self->cpan_module($module, $meta->{distfile}, $meta->{version});
|
|
| 755 |
+ my @filters= ( |
|
| 756 |
+ { range => { 'module.version_numified' => \%range } },
|
|
| 757 |
+ ); |
|
| 758 |
+ |
|
| 759 |
+ if (@exclusion) {
|
|
| 760 |
+ push @filters, {
|
|
| 761 |
+ not => { or => [ map { +{ term => { 'module.version_numified' => $self->numify_ver($_) } } } @exclusion ] },
|
|
| 762 |
+ }; |
|
| 763 |
+ } |
|
| 764 |
+ |
|
| 765 |
+ return @filters; |
|
| 766 |
+ } |
|
| 767 |
+ } |
|
| 768 |
+ |
|
| 769 |
+ sub numify_ver {
|
|
| 770 |
+ my($self, $ver) = @_; |
|
| 771 |
+ version->new($ver)->numify; |
|
| 772 |
+ } |
|
| 773 |
+ |
|
| 774 |
+ sub maturity_filter {
|
|
| 775 |
+ my($self, $module, $version) = @_; |
|
| 776 |
+ |
|
| 777 |
+ my @filters; |
|
| 778 |
+ |
|
| 779 |
+ # TODO: dev release should be enabled per dist |
|
| 780 |
+ if (!$self->with_version_range($version) or $self->{dev_release}) {
|
|
| 781 |
+ # backpan'ed dev release are considered "cancelled" |
|
| 782 |
+ push @filters, { not => { term => { status => 'backpan' } } };
|
|
| 783 |
+ } |
|
| 784 |
+ |
|
| 785 |
+ unless ($self->{dev_release} or $version =~ /==/) {
|
|
| 786 |
+ push @filters, { term => { maturity => 'released' } };
|
|
| 787 |
+ } |
|
| 788 |
+ |
|
| 789 |
+ return @filters; |
|
| 790 |
+ } |
|
| 791 |
+ |
|
| 792 |
+ sub search_metacpan {
|
|
| 793 |
+ my($self, $module, $version) = @_; |
|
| 794 |
+ |
|
| 795 |
+ require JSON::PP; |
|
| 796 |
+ |
|
| 797 |
+ $self->chat("Searching $module ($version) on metacpan ...\n");
|
|
| 798 |
+ |
|
| 799 |
+ my $metacpan_uri = 'http://api.metacpan.org/v0'; |
|
| 800 |
+ |
|
| 801 |
+ my @filter = $self->maturity_filter($module, $version); |
|
| 802 |
+ |
|
| 803 |
+ my $query = { filtered => {
|
|
| 804 |
+ (@filter ? (filter => { and => \@filter }) : ()),
|
|
| 805 |
+ query => { nested => {
|
|
| 806 |
+ score_mode => 'max', |
|
| 807 |
+ path => 'module', |
|
| 808 |
+ query => { custom_score => {
|
|
| 809 |
+ metacpan_script => "score_version_numified", |
|
| 810 |
+ query => { constant_score => {
|
|
| 811 |
+ filter => { and => [
|
|
| 812 |
+ { term => { 'module.authorized' => JSON::PP::true() } },
|
|
| 813 |
+ { term => { 'module.indexed' => JSON::PP::true() } },
|
|
| 814 |
+ { term => { 'module.name' => $module } },
|
|
| 815 |
+ $self->version_to_query($module, $version), |
|
| 816 |
+ ] } |
|
| 817 |
+ } }, |
|
| 818 |
+ } }, |
|
| 819 |
+ } }, |
|
| 820 |
+ } }; |
|
| 821 |
+ |
|
| 822 |
+ my $module_uri = "$metacpan_uri/file/_search?source="; |
|
| 823 |
+ $module_uri .= $self->encode_json({
|
|
| 824 |
+ query => $query, |
|
| 825 |
+ fields => [ 'release', 'module' ], |
|
| 826 |
+ }); |
|
| 827 |
+ |
|
| 828 |
+ my($release, $module_version); |
|
| 829 |
+ |
|
| 830 |
+ my $module_json = $self->get($module_uri); |
|
| 831 |
+ my $module_meta = eval { JSON::PP::decode_json($module_json) };
|
|
| 832 |
+ my $match = $module_meta ? $module_meta->{hits}{hits}[0]{fields} : undef;
|
|
| 833 |
+ if ($match) {
|
|
| 834 |
+ $release = $match->{release};
|
|
| 835 |
+ my $module_matched = (grep { $_->{name} eq $module } @{$match->{module}})[0];
|
|
| 836 |
+ $module_version = $module_matched->{version};
|
|
| 837 |
+ } |
|
| 838 |
+ |
|
| 839 |
+ unless ($release) {
|
|
| 840 |
+ $self->chat("! Could not find a release matching $module ($version) on MetaCPAN.\n");
|
|
| 841 |
+ return; |
|
| 842 |
+ } |
|
| 843 |
+ |
|
| 844 |
+ my $dist_uri = "$metacpan_uri/release/_search?source="; |
|
| 845 |
+ $dist_uri .= $self->encode_json({
|
|
| 846 |
+ filter => {
|
|
| 847 |
+ term => { 'release.name' => $release },
|
|
| 848 |
+ }, |
|
| 849 |
+ fields => [ 'download_url', 'stat', 'status' ], |
|
| 850 |
+ }); |
|
| 851 |
+ |
|
| 852 |
+ my $dist_json = $self->get($dist_uri); |
|
| 853 |
+ my $dist_meta = eval { JSON::PP::decode_json($dist_json) };
|
|
| 854 |
+ |
|
| 855 |
+ if ($dist_meta) {
|
|
| 856 |
+ $dist_meta = $dist_meta->{hits}{hits}[0]{fields};
|
|
| 857 |
+ } |
|
| 858 |
+ if ($dist_meta && $dist_meta->{download_url}) {
|
|
| 859 |
+ (my $distfile = $dist_meta->{download_url}) =~ s!.+/authors/id/!!;
|
|
| 860 |
+ local $self->{mirrors} = $self->{mirrors};
|
|
| 861 |
+ if ($dist_meta->{status} eq 'backpan') {
|
|
| 862 |
+ $self->{mirrors} = [ 'http://backpan.perl.org' ];
|
|
| 863 |
+ } elsif ($dist_meta->{stat}{mtime} > time()-24*60*60) {
|
|
| 864 |
+ $self->{mirrors} = [ 'http://cpan.metacpan.org' ];
|
|
| 650 | 865 |
} |
| 866 |
+ return $self->cpan_module($module, $distfile, $module_version); |
|
| 867 |
+ } |
|
| 868 |
+ |
|
| 869 |
+ $self->diag_fail("Finding $module on metacpan failed.");
|
|
| 870 |
+ return; |
|
| 871 |
+ } |
|
| 872 |
+ |
|
| 873 |
+ sub search_database {
|
|
| 874 |
+ my($self, $module, $version) = @_; |
|
| 875 |
+ |
|
| 876 |
+ my $found; |
|
| 877 |
+ my $range = ($self->with_version_range($version) || $self->{dev_release});
|
|
| 878 |
+ |
|
| 879 |
+ if ($range or $self->{metacpan}) {
|
|
| 880 |
+ $found = $self->search_metacpan($module, $version) and return $found; |
|
| 881 |
+ $found = $self->search_cpanmetadb($module, $version) and return $found; |
|
| 882 |
+ } else {
|
|
| 883 |
+ $found = $self->search_cpanmetadb($module, $version) and return $found; |
|
| 884 |
+ $found = $self->search_metacpan($module, $version) and return $found; |
|
| 885 |
+ } |
|
| 886 |
+ } |
|
| 651 | 887 |
|
| 652 |
- $self->diag_fail("Finding $module on cpanmetadb failed.");
|
|
| 888 |
+ sub search_cpanmetadb {
|
|
| 889 |
+ my($self, $module, $version) = @_; |
|
| 653 | 890 |
|
| 654 |
- $self->chat("Searching $module on search.cpan.org ...\n");
|
|
| 655 |
- my $uri = "http://search.cpan.org/perldoc?$module"; |
|
| 656 |
- my $html = $self->get($uri); |
|
| 657 |
- $html =~ m!<a href="/CPAN/authors/id/(.*?\.(?:tar\.gz|tgz|tar\.bz2|zip))">! |
|
| 658 |
- and return $self->cpan_module($module, $1); |
|
| 891 |
+ $self->chat("Searching $module on cpanmetadb ...\n");
|
|
| 659 | 892 |
|
| 660 |
- $self->diag_fail("Finding $module on search.cpan.org failed.");
|
|
| 893 |
+ my $uri = "http://cpanmetadb.plackperl.org/v1.0/package/$module"; |
|
| 894 |
+ my $yaml = $self->get($uri); |
|
| 895 |
+ my $meta = $self->parse_meta_string($yaml); |
|
| 896 |
+ if ($meta && $meta->{distfile}) {
|
|
| 897 |
+ return $self->cpan_module($module, $meta->{distfile}, $meta->{version});
|
|
| 661 | 898 |
} |
| 662 | 899 |
|
| 900 |
+ $self->diag_fail("Finding $module on cpanmetadb failed.");
|
|
| 901 |
+ return; |
|
| 902 |
+ } |
|
| 903 |
+ |
|
| 904 |
+ sub search_module {
|
|
| 905 |
+ my($self, $module, $version) = @_; |
|
| 906 |
+ |
|
| 663 | 907 |
if ($self->{mirror_index}) {
|
| 664 | 908 |
$self->chat("Searching $module on mirror index $self->{mirror_index} ...\n");
|
| 665 | 909 |
my $pkg = $self->search_mirror_index_file($self->{mirror_index}, $module, $version);
|
| 666 | 910 |
return $pkg if $pkg; |
| 911 |
+ |
|
| 912 |
+ unless ($self->{cascade_search}) {
|
|
| 913 |
+ $self->diag_fail("Finding $module ($version) on mirror index $self->{mirror_index} failed.");
|
|
| 914 |
+ return; |
|
| 915 |
+ } |
|
| 916 |
+ } |
|
| 917 |
+ |
|
| 918 |
+ unless ($self->{mirror_only}) {
|
|
| 919 |
+ my $found = $self->search_database($module, $version); |
|
| 920 |
+ return $found if $found; |
|
| 667 | 921 |
} |
| 668 | 922 |
|
| 669 | 923 |
MIRROR: for my $mirror (@{ $self->{mirrors} }) {
|
| ... | ... |
@@ -738,6 +992,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 738 | 992 |
--interactive Turns on interactive configure (required for Task:: modules) |
| 739 | 993 |
-f,--force force install |
| 740 | 994 |
-n,--notest Do not run unit tests |
| 995 |
+ --test-only Run tests only, do not install |
|
| 741 | 996 |
-S,--sudo sudo to run install commands |
| 742 | 997 |
--installdeps Only install dependencies |
| 743 | 998 |
--showdeps Only display direct dependencies |
| ... | ... |
@@ -794,8 +1049,11 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 794 | 1049 |
|
| 795 | 1050 |
sub maybe_abs {
|
| 796 | 1051 |
my($self, $lib) = @_; |
| 797 |
- return $lib if $lib eq '_'; # special case: gh-113 |
|
| 798 |
- $lib =~ /^[~\/]/ ? $lib : File::Spec->canonpath(Cwd::cwd . "/$lib"); |
|
| 1052 |
+ if ($lib eq '_' or $lib =~ /^~/ or File::Spec->file_name_is_absolute($lib)) {
|
|
| 1053 |
+ return $lib; |
|
| 1054 |
+ } else {
|
|
| 1055 |
+ return File::Spec->canonpath(File::Spec->catdir(Cwd::cwd(), $lib)); |
|
| 1056 |
+ } |
|
| 799 | 1057 |
} |
| 800 | 1058 |
|
| 801 | 1059 |
sub bootstrap_local_lib {
|
| ... | ... |
@@ -822,9 +1080,10 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 822 | 1080 |
! Can't write to $Config{installsitelib} and $Config{installsitebin}: Installing modules to $ENV{HOME}/perl5
|
| 823 | 1081 |
! To turn off this warning, you have to do one of the following: |
| 824 | 1082 |
! - run me as a root or with --sudo option (to install to $Config{installsitelib} and $Config{installsitebin})
|
| 825 |
- | - run me with --local-lib option e.g. cpanm --local-lib=~/perl5 |
|
| 826 |
- ! - Set PERL_CPANM_OPT="--local-lib=~/perl5" environment variable (in your shell rc file) |
|
| 827 |
- ! - Configure local::lib in your shell to set PERL_MM_OPT etc. |
|
| 1083 |
+ ! - Configure local::lib your existing local::lib in this shell to set PERL_MM_OPT etc. |
|
| 1084 |
+ ! - Install local::lib by running the following commands |
|
| 1085 |
+ ! |
|
| 1086 |
+ ! cpanm --local-lib=~/perl5 local::lib && eval \$(perl -I ~/perl5/lib/perl5/ -Mlocal::lib) |
|
| 828 | 1087 |
! |
| 829 | 1088 |
DIAG |
| 830 | 1089 |
sleep 2; |
| ... | ... |
@@ -834,8 +1093,8 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 834 | 1093 |
my($self, $base) = @_; |
| 835 | 1094 |
require local::lib; |
| 836 | 1095 |
( |
| 837 |
- local::lib->install_base_perl_path($base), |
|
| 838 |
- local::lib->install_base_arch_path($base), |
|
| 1096 |
+ local::lib->resolve_path(local::lib->install_base_perl_path($base)), |
|
| 1097 |
+ local::lib->resolve_path(local::lib->install_base_arch_path($base)), |
|
| 839 | 1098 |
@Config{qw(privlibexp archlibexp)},
|
| 840 | 1099 |
); |
| 841 | 1100 |
} |
| ... | ... |
@@ -871,8 +1130,8 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 871 | 1130 |
$self->{search_inc} = [ @inc ];
|
| 872 | 1131 |
} else {
|
| 873 | 1132 |
$self->{search_inc} = [
|
| 874 |
- local::lib->install_base_arch_path($base), |
|
| 875 |
- local::lib->install_base_perl_path($base), |
|
| 1133 |
+ local::lib->resolve_path(local::lib->install_base_arch_path($base)), |
|
| 1134 |
+ local::lib->resolve_path(local::lib->install_base_perl_path($base)), |
|
| 876 | 1135 |
@INC, |
| 877 | 1136 |
]; |
| 878 | 1137 |
} |
| ... | ... |
@@ -1112,7 +1371,11 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1112 | 1371 |
} |
| 1113 | 1372 |
|
| 1114 | 1373 |
sub install {
|
| 1115 |
- my($self, $cmd, $uninst_opts) = @_; |
|
| 1374 |
+ my($self, $cmd, $uninst_opts, $depth) = @_; |
|
| 1375 |
+ |
|
| 1376 |
+ if ($depth == 0 && $self->{test_only}) {
|
|
| 1377 |
+ return 1; |
|
| 1378 |
+ } |
|
| 1116 | 1379 |
|
| 1117 | 1380 |
if ($self->{sudo}) {
|
| 1118 | 1381 |
unshift @$cmd, "sudo"; |
| ... | ... |
@@ -1173,7 +1436,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1173 | 1436 |
sub configure_mirrors {
|
| 1174 | 1437 |
my $self = shift; |
| 1175 | 1438 |
unless (@{$self->{mirrors}}) {
|
| 1176 |
- $self->{mirrors} = [ 'http://search.cpan.org/CPAN' ];
|
|
| 1439 |
+ $self->{mirrors} = [ 'http://www.cpan.org' ];
|
|
| 1177 | 1440 |
} |
| 1178 | 1441 |
for (@{$self->{mirrors}}) {
|
| 1179 | 1442 |
s!^/!file:///!; |
| ... | ... |
@@ -1215,10 +1478,17 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1215 | 1478 |
$self->setup_module_build_patch unless $self->{pod2man};
|
| 1216 | 1479 |
|
| 1217 | 1480 |
if ($dist->{module}) {
|
| 1218 |
- my($ok, $local) = $self->check_module($dist->{module}, $dist->{module_version} || 0);
|
|
| 1219 |
- if ($self->{skip_installed} && $ok) {
|
|
| 1220 |
- $self->diag("$dist->{module} is up to date. ($local)\n", 1);
|
|
| 1221 |
- return 1; |
|
| 1481 |
+ unless ($self->with_version_range($version)) {
|
|
| 1482 |
+ my($ok, $local) = $self->check_module($dist->{module}, $dist->{module_version} || 0);
|
|
| 1483 |
+ if ($self->{skip_installed} && $ok) {
|
|
| 1484 |
+ $self->diag("$dist->{module} is up to date. ($local)\n", 1);
|
|
| 1485 |
+ return 1; |
|
| 1486 |
+ } |
|
| 1487 |
+ } |
|
| 1488 |
+ |
|
| 1489 |
+ unless ($self->satisfy_version($dist->{module}, $dist->{module_version}, $version)) {
|
|
| 1490 |
+ $self->diag("Found $dist->{module} $dist->{module_version} which doesn't satisfy $version.\n");
|
|
| 1491 |
+ return; |
|
| 1222 | 1492 |
} |
| 1223 | 1493 |
} |
| 1224 | 1494 |
|
| ... | ... |
@@ -1299,7 +1569,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1299 | 1569 |
$self->diag_ok; |
| 1300 | 1570 |
$dist->{local_path} = File::Spec->rel2abs($name);
|
| 1301 | 1571 |
|
| 1302 |
- my $dir = $self->unpack($file); |
|
| 1572 |
+ my $dir = $self->unpack($file, $uri, $dist); |
|
| 1303 | 1573 |
next unless $dir; # unpack failed |
| 1304 | 1574 |
|
| 1305 | 1575 |
if (my $save = $self->{save_dists}) {
|
| ... | ... |
@@ -1314,7 +1584,12 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1314 | 1584 |
} |
| 1315 | 1585 |
|
| 1316 | 1586 |
sub unpack {
|
| 1317 |
- my($self, $file) = @_; |
|
| 1587 |
+ my($self, $file, $uri, $dist) = @_; |
|
| 1588 |
+ |
|
| 1589 |
+ if ($self->{verify}) {
|
|
| 1590 |
+ $self->verify_archive($file, $uri, $dist) or return; |
|
| 1591 |
+ } |
|
| 1592 |
+ |
|
| 1318 | 1593 |
$self->chat("Unpacking $file\n");
|
| 1319 | 1594 |
my $dir = $file =~ /\.zip/i ? $self->unzip($file) : $self->untar($file); |
| 1320 | 1595 |
unless ($dir) {
|
| ... | ... |
@@ -1323,13 +1598,121 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1323 | 1598 |
return $dir; |
| 1324 | 1599 |
} |
| 1325 | 1600 |
|
| 1601 |
+ sub verify_checksums_signature {
|
|
| 1602 |
+ my($self, $chk_file) = @_; |
|
| 1603 |
+ |
|
| 1604 |
+ require Module::Signature; # no fatpack |
|
| 1605 |
+ |
|
| 1606 |
+ $self->chat("Verifying the signature of CHECKSUMS\n");
|
|
| 1607 |
+ |
|
| 1608 |
+ my $rv = eval {
|
|
| 1609 |
+ local $SIG{__WARN__} = sub {}; # suppress warnings
|
|
| 1610 |
+ my $v = Module::Signature::_verify($chk_file); |
|
| 1611 |
+ $v == Module::Signature::SIGNATURE_OK(); |
|
| 1612 |
+ }; |
|
| 1613 |
+ if ($rv) {
|
|
| 1614 |
+ $self->chat("Verified OK!\n");
|
|
| 1615 |
+ } else {
|
|
| 1616 |
+ $self->diag_fail("Verifying CHECKSUMS signature failed: $rv\n");
|
|
| 1617 |
+ return; |
|
| 1618 |
+ } |
|
| 1619 |
+ |
|
| 1620 |
+ return 1; |
|
| 1621 |
+ } |
|
| 1622 |
+ |
|
| 1623 |
+ sub verify_archive {
|
|
| 1624 |
+ my($self, $file, $uri, $dist) = @_; |
|
| 1625 |
+ |
|
| 1626 |
+ unless ($dist->{cpanid}) {
|
|
| 1627 |
+ $self->chat("Archive '$file' does not seem to be from PAUSE. Skip verification.\n");
|
|
| 1628 |
+ } |
|
| 1629 |
+ |
|
| 1630 |
+ (my $mirror = $uri) =~ s!/authors/id.*$!!; |
|
| 1631 |
+ |
|
| 1632 |
+ (my $chksum_uri = $uri) =~ s!/[^/]*$!/CHECKSUMS!; |
|
| 1633 |
+ my $chk_file = $self->source_for($mirror) . "/$dist->{cpanid}.CHECKSUMS";
|
|
| 1634 |
+ $self->diag_progress("Fetching $chksum_uri");
|
|
| 1635 |
+ $self->mirror($chksum_uri, $chk_file); |
|
| 1636 |
+ |
|
| 1637 |
+ unless (-e $chk_file) {
|
|
| 1638 |
+ $self->diag_fail("Fetching $chksum_uri failed.\n");
|
|
| 1639 |
+ return; |
|
| 1640 |
+ } |
|
| 1641 |
+ |
|
| 1642 |
+ $self->diag_ok; |
|
| 1643 |
+ $self->verify_checksums_signature($chk_file) or return; |
|
| 1644 |
+ $self->verify_checksum($file, $chk_file); |
|
| 1645 |
+ } |
|
| 1646 |
+ |
|
| 1647 |
+ sub verify_checksum {
|
|
| 1648 |
+ my($self, $file, $chk_file) = @_; |
|
| 1649 |
+ |
|
| 1650 |
+ $self->chat("Verifying the SHA1 for $file\n");
|
|
| 1651 |
+ |
|
| 1652 |
+ open my $fh, "<$chk_file" or die "$chk_file: $!"; |
|
| 1653 |
+ my $data = join '', <$fh>; |
|
| 1654 |
+ $data =~ s/\015?\012/\n/g; |
|
| 1655 |
+ |
|
| 1656 |
+ require Safe; # no fatpack |
|
| 1657 |
+ my $chksum = Safe->new->reval($data); |
|
| 1658 |
+ |
|
| 1659 |
+ if (!ref $chksum or ref $chksum ne 'HASH') {
|
|
| 1660 |
+ $self->diag_fail("! Checksum file downloaded from $chk_file is broken.\n");
|
|
| 1661 |
+ return; |
|
| 1662 |
+ } |
|
| 1663 |
+ |
|
| 1664 |
+ if (my $sha = $chksum->{$file}{sha256}) {
|
|
| 1665 |
+ my $hex = $self->sha1_for($file); |
|
| 1666 |
+ if ($hex eq $sha) {
|
|
| 1667 |
+ $self->chat("Checksum for $file: Verified!\n");
|
|
| 1668 |
+ } else {
|
|
| 1669 |
+ $self->diag_fail("Checksum mismatch for $file\n");
|
|
| 1670 |
+ return; |
|
| 1671 |
+ } |
|
| 1672 |
+ } else {
|
|
| 1673 |
+ $self->chat("Checksum for $file not found in CHECKSUMS.\n");
|
|
| 1674 |
+ return; |
|
| 1675 |
+ } |
|
| 1676 |
+ } |
|
| 1677 |
+ |
|
| 1678 |
+ sub sha1_for {
|
|
| 1679 |
+ my($self, $file) = @_; |
|
| 1680 |
+ |
|
| 1681 |
+ require Digest::SHA; # no fatpack |
|
| 1682 |
+ |
|
| 1683 |
+ open my $fh, "<", $file or die "$file: $!"; |
|
| 1684 |
+ my $dg = Digest::SHA->new(256); |
|
| 1685 |
+ my($data); |
|
| 1686 |
+ while (read($fh, $data, 4096)) {
|
|
| 1687 |
+ $dg->add($data); |
|
| 1688 |
+ } |
|
| 1689 |
+ |
|
| 1690 |
+ return $dg->hexdigest; |
|
| 1691 |
+ } |
|
| 1692 |
+ |
|
| 1693 |
+ sub verify_signature {
|
|
| 1694 |
+ my($self, $dist) = @_; |
|
| 1695 |
+ |
|
| 1696 |
+ $self->diag_progress("Verifying the SIGNATURE file");
|
|
| 1697 |
+ my $out = `$self->{cpansign} -v --skip 2>&1`;
|
|
| 1698 |
+ $self->log($out); |
|
| 1699 |
+ |
|
| 1700 |
+ if ($out =~ /Signature verified OK/) {
|
|
| 1701 |
+ $self->diag_ok("Verified OK");
|
|
| 1702 |
+ return 1; |
|
| 1703 |
+ } else {
|
|
| 1704 |
+ $self->diag_fail("SIGNATURE verificaion for $dist->{filename} failed\n");
|
|
| 1705 |
+ return; |
|
| 1706 |
+ } |
|
| 1707 |
+ } |
|
| 1708 |
+ |
|
| 1326 | 1709 |
sub resolve_name {
|
| 1327 | 1710 |
my($self, $module, $version) = @_; |
| 1328 | 1711 |
|
| 1329 | 1712 |
# URL |
| 1330 | 1713 |
if ($module =~ /^(ftp|https?|file):/) {
|
| 1331 |
- if ($module =~ m!authors/id/!) {
|
|
| 1332 |
- return $self->cpan_dist($module, $module); |
|
| 1714 |
+ if ($module =~ m!authors/id/(.*)!) {
|
|
| 1715 |
+ return $self->cpan_dist($1, $module); |
|
| 1333 | 1716 |
} else {
|
| 1334 | 1717 |
return { uris => [ $module ] };
|
| 1335 | 1718 |
} |
| ... | ... |
@@ -1351,6 +1734,11 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1351 | 1734 |
}; |
| 1352 | 1735 |
} |
| 1353 | 1736 |
|
| 1737 |
+ # Git |
|
| 1738 |
+ if ($module =~ /(^git:|\.git$)/) {
|
|
| 1739 |
+ return $self->git_uri($module); |
|
| 1740 |
+ } |
|
| 1741 |
+ |
|
| 1354 | 1742 |
# cpan URI |
| 1355 | 1743 |
if ($module =~ s!^cpan:///distfile/!!) {
|
| 1356 | 1744 |
return $self->cpan_dist($module); |
| ... | ... |
@@ -1402,6 +1790,44 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1402 | 1790 |
}; |
| 1403 | 1791 |
} |
| 1404 | 1792 |
|
| 1793 |
+ sub git_uri {
|
|
| 1794 |
+ my ($self, $uri) = @_; |
|
| 1795 |
+ |
|
| 1796 |
+ # similar to http://www.pip-installer.org/en/latest/logic.html#vcs-support |
|
| 1797 |
+ # git URL has to end with .git when you need to use pin @ commit/tag/branch |
|
| 1798 |
+ |
|
| 1799 |
+ ($uri, my $commitish) = split /(?<=\.git)@/i, $uri, 2; |
|
| 1800 |
+ |
|
| 1801 |
+ my $dh = File::Temp->newdir(CLEANUP => 1); |
|
| 1802 |
+ my $dir = Cwd::abs_path($dh->dirname); |
|
| 1803 |
+ |
|
| 1804 |
+ $self->diag_progress("Cloning $uri");
|
|
| 1805 |
+ $self->run([ 'git', 'clone', $uri, $dir ]); |
|
| 1806 |
+ |
|
| 1807 |
+ unless (-e "$dir/.git") {
|
|
| 1808 |
+ $self->diag_fail("Failed cloning git repository $uri");
|
|
| 1809 |
+ return; |
|
| 1810 |
+ } |
|
| 1811 |
+ |
|
| 1812 |
+ if ($commitish) {
|
|
| 1813 |
+ require File::pushd; |
|
| 1814 |
+ my $dir = File::pushd::pushd($dir); |
|
| 1815 |
+ |
|
| 1816 |
+ unless ($self->run([ 'git', 'checkout', $commitish ])) {
|
|
| 1817 |
+ $self->diag_fail("Failed to checkout '$commitish' in git repository $uri\n");
|
|
| 1818 |
+ return; |
|
| 1819 |
+ } |
|
| 1820 |
+ } |
|
| 1821 |
+ |
|
| 1822 |
+ $self->diag_ok; |
|
| 1823 |
+ |
|
| 1824 |
+ return {
|
|
| 1825 |
+ source => 'local', |
|
| 1826 |
+ dir => $dir, |
|
| 1827 |
+ handle => $dh, |
|
| 1828 |
+ }; |
|
| 1829 |
+ } |
|
| 1830 |
+ |
|
| 1405 | 1831 |
sub setup_module_build_patch {
|
| 1406 | 1832 |
my $self = shift; |
| 1407 | 1833 |
|
| ... | ... |
@@ -1432,7 +1858,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1432 | 1858 |
# might be newer than (or actually wasn't core at) the version |
| 1433 | 1859 |
# that is shipped with the current perl |
| 1434 | 1860 |
if ($self->{self_contained} && $self->loaded_from_perl_lib($meta)) {
|
| 1435 |
- require Module::CoreList; |
|
| 1861 |
+ require Module::CoreList; # no fatpack |
|
| 1436 | 1862 |
unless (exists $Module::CoreList::version{$]+0}{$mod}) {
|
| 1437 | 1863 |
return 0, undef; |
| 1438 | 1864 |
} |
| ... | ... |
@@ -1443,23 +1869,43 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1443 | 1869 |
|
| 1444 | 1870 |
if ($self->is_deprecated($meta)){
|
| 1445 | 1871 |
return 0, $version; |
| 1446 |
- } elsif (!$want_ver or $version >= version->new($want_ver)) {
|
|
| 1872 |
+ } elsif ($self->satisfy_version($mod, $version, $want_ver)) {
|
|
| 1447 | 1873 |
return 1, ($version || 'undef'); |
| 1448 | 1874 |
} else {
|
| 1449 | 1875 |
return 0, $version; |
| 1450 | 1876 |
} |
| 1451 | 1877 |
} |
| 1452 | 1878 |
|
| 1879 |
+ sub satisfy_version {
|
|
| 1880 |
+ my($self, $mod, $version, $want_ver) = @_; |
|
| 1881 |
+ |
|
| 1882 |
+ $want_ver = '0' unless defined($want_ver) && length($want_ver); |
|
| 1883 |
+ |
|
| 1884 |
+ require CPAN::Meta::Requirements; |
|
| 1885 |
+ my $requirements = CPAN::Meta::Requirements->new; |
|
| 1886 |
+ $requirements->add_string_requirement($mod, $want_ver); |
|
| 1887 |
+ $requirements->accepts_module($mod, $version); |
|
| 1888 |
+ } |
|
| 1889 |
+ |
|
| 1890 |
+ sub unsatisfy_how {
|
|
| 1891 |
+ my($self, $ver, $want_ver) = @_; |
|
| 1892 |
+ |
|
| 1893 |
+ if ($want_ver =~ /^[v0-9\.\_]+$/) {
|
|
| 1894 |
+ return "$ver < $want_ver"; |
|
| 1895 |
+ } else {
|
|
| 1896 |
+ return "$ver doesn't satisfy $want_ver"; |
|
| 1897 |
+ } |
|
| 1898 |
+ } |
|
| 1899 |
+ |
|
| 1453 | 1900 |
sub is_deprecated {
|
| 1454 | 1901 |
my($self, $meta) = @_; |
| 1455 | 1902 |
|
| 1456 | 1903 |
my $deprecated = eval {
|
| 1457 |
- require Module::CoreList; |
|
| 1904 |
+ require Module::CoreList; # no fatpack |
|
| 1458 | 1905 |
Module::CoreList::is_deprecated($meta->{module});
|
| 1459 | 1906 |
}; |
| 1460 | 1907 |
|
| 1461 |
- return unless $deprecated; |
|
| 1462 |
- return $self->loaded_from_perl_lib($meta); |
|
| 1908 |
+ return $deprecated && $self->loaded_from_perl_lib($meta); |
|
| 1463 | 1909 |
} |
| 1464 | 1910 |
|
| 1465 | 1911 |
sub loaded_from_perl_lib {
|
| ... | ... |
@@ -1483,7 +1929,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1483 | 1929 |
my($ok, $local) = $self->check_module($mod, $ver); |
| 1484 | 1930 |
|
| 1485 | 1931 |
if ($ok) { $self->chat("Yes ($local)\n") }
|
| 1486 |
- elsif ($local) { $self->chat("No ($local < $ver)\n") }
|
|
| 1932 |
+ elsif ($local) { $self->chat("No (" . $self->unsatisfy_how($local, $ver) . ")\n") }
|
|
| 1487 | 1933 |
else { $self->chat("No\n") }
|
| 1488 | 1934 |
|
| 1489 | 1935 |
return $mod unless $ok; |
| ... | ... |
@@ -1536,20 +1982,32 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1536 | 1982 |
sub build_stuff {
|
| 1537 | 1983 |
my($self, $stuff, $dist, $depth) = @_; |
| 1538 | 1984 |
|
| 1985 |
+ if ($self->{verify} && -e 'SIGNATURE') {
|
|
| 1986 |
+ $self->verify_signature($dist) or return; |
|
| 1987 |
+ } |
|
| 1988 |
+ |
|
| 1539 | 1989 |
my @config_deps; |
| 1540 |
- if (!%{$dist->{meta} || {}} && -e 'META.yml') {
|
|
| 1990 |
+ if (-e 'META.json') {
|
|
| 1991 |
+ $self->chat("Checking configure dependencies from META.json\n");
|
|
| 1992 |
+ $dist->{meta} = $self->parse_meta('META.json');
|
|
| 1993 |
+ } elsif (-e 'META.yml') {
|
|
| 1541 | 1994 |
$self->chat("Checking configure dependencies from META.yml\n");
|
| 1542 | 1995 |
$dist->{meta} = $self->parse_meta('META.yml');
|
| 1543 | 1996 |
} |
| 1544 | 1997 |
|
| 1545 | 1998 |
if (!$dist->{meta} && $dist->{source} eq 'cpan') {
|
| 1546 |
- $self->chat("META.yml not found or unparsable. Fetching META.yml from search.cpan.org\n");
|
|
| 1999 |
+ $self->chat("META.yml/json not found or unparsable. Fetching META.yml from search.cpan.org\n");
|
|
| 1547 | 2000 |
$dist->{meta} = $self->fetch_meta_sco($dist);
|
| 1548 | 2001 |
} |
| 1549 | 2002 |
|
| 1550 | 2003 |
$dist->{meta} ||= {};
|
| 1551 | 2004 |
|
| 1552 |
- push @config_deps, %{$dist->{meta}{configure_requires} || {}};
|
|
| 2005 |
+ if ( $dist->{meta}->{prereqs} ) {
|
|
| 2006 |
+ push @config_deps, %{$dist->{meta}{prereqs}{configure}{requires} || {}};
|
|
| 2007 |
+ } |
|
| 2008 |
+ else {
|
|
| 2009 |
+ push @config_deps, %{$dist->{meta}{configure_requires} || {}};
|
|
| 2010 |
+ } |
|
| 1553 | 2011 |
|
| 1554 | 2012 |
my $target = $dist->{meta}{name} ? "$dist->{meta}{name}-$dist->{meta}{version}" : $dist->{dir};
|
| 1555 | 2013 |
|
| ... | ... |
@@ -1558,7 +2016,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1558 | 2016 |
|
| 1559 | 2017 |
$self->diag_progress("Configuring $target");
|
| 1560 | 2018 |
|
| 1561 |
- my $configure_state = $self->configure_this($dist); |
|
| 2019 |
+ my $configure_state = $self->configure_this($dist, $depth); |
|
| 1562 | 2020 |
|
| 1563 | 2021 |
$self->diag_ok($configure_state->{configured_ok} ? "OK" : "N/A");
|
| 1564 | 2022 |
|
| ... | ... |
@@ -1617,13 +2075,13 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1617 | 2075 |
$self->diag_progress("Building " . ($self->{notest} ? "" : "and testing ") . $distname);
|
| 1618 | 2076 |
$self->build([ $self->{perl}, @switches, "./Build" ], $distname) &&
|
| 1619 | 2077 |
$self->test([ $self->{perl}, "./Build", "test" ], $distname) &&
|
| 1620 |
- $self->install([ $self->{perl}, @switches, "./Build", "install" ], [ "--uninst", 1 ]) &&
|
|
| 2078 |
+ $self->install([ $self->{perl}, @switches, "./Build", "install" ], [ "--uninst", 1 ], $depth) &&
|
|
| 1621 | 2079 |
$installed++; |
| 1622 | 2080 |
} elsif ($self->{make} && -e 'Makefile') {
|
| 1623 | 2081 |
$self->diag_progress("Building " . ($self->{notest} ? "" : "and testing ") . $distname);
|
| 1624 | 2082 |
$self->build([ $self->{make} ], $distname) &&
|
| 1625 | 2083 |
$self->test([ $self->{make}, "test" ], $distname) &&
|
| 1626 |
- $self->install([ $self->{make}, "install" ], [ "UNINST=1" ]) &&
|
|
| 2084 |
+ $self->install([ $self->{make}, "install" ], [ "UNINST=1" ], $depth) &&
|
|
| 1627 | 2085 |
$installed++; |
| 1628 | 2086 |
} else {
|
| 1629 | 2087 |
my $why; |
| ... | ... |
@@ -1636,7 +2094,10 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1636 | 2094 |
return; |
| 1637 | 2095 |
} |
| 1638 | 2096 |
|
| 1639 |
- if ($installed) {
|
|
| 2097 |
+ if ($installed && $self->{test_only}) {
|
|
| 2098 |
+ $self->diag_ok; |
|
| 2099 |
+ $self->diag("Successfully tested $distname\n", 1);
|
|
| 2100 |
+ } elsif ($installed) {
|
|
| 1640 | 2101 |
my $local = $self->{local_versions}{$dist->{module} || ''};
|
| 1641 | 2102 |
my $version = $dist->{module_version} || $dist->{meta}{version} || $dist->{version};
|
| 1642 | 2103 |
my $reinstall = $local && ($local eq $version); |
| ... | ... |
@@ -1651,14 +2112,25 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1651 | 2112 |
$self->save_meta($stuff, $dist, $module_name, \@config_deps, \@deps); |
| 1652 | 2113 |
return 1; |
| 1653 | 2114 |
} else {
|
| 1654 |
- my $msg = "Building $distname failed"; |
|
| 1655 |
- $self->diag_fail("Installing $stuff failed. See $self->{log} for details.", 1);
|
|
| 2115 |
+ my $what = $self->{test_only} ? "Testing" : "Installing";
|
|
| 2116 |
+ $self->diag_fail("$what $stuff failed. See $self->{log} for details.", 1);
|
|
| 1656 | 2117 |
return; |
| 1657 | 2118 |
} |
| 1658 | 2119 |
} |
| 1659 | 2120 |
|
| 1660 | 2121 |
sub configure_this {
|
| 1661 |
- my($self, $dist) = @_; |
|
| 2122 |
+ my($self, $dist, $depth) = @_; |
|
| 2123 |
+ |
|
| 2124 |
+ if (-e 'cpanfile' && $self->{installdeps} && $depth == 0) {
|
|
| 2125 |
+ require Module::CPANfile; |
|
| 2126 |
+ $dist->{cpanfile} = eval { Module::CPANfile->load('cpanfile') };
|
|
| 2127 |
+ $self->diag_fail($@, 1) if $@; |
|
| 2128 |
+ return {
|
|
| 2129 |
+ configured => 1, |
|
| 2130 |
+ configured_ok => !!$dist->{cpanfile},
|
|
| 2131 |
+ use_module_build => 0, |
|
| 2132 |
+ }; |
|
| 2133 |
+ } |
|
| 1662 | 2134 |
|
| 1663 | 2135 |
if ($self->{skip_configure}) {
|
| 1664 | 2136 |
my $eumm = -e 'Makefile'; |
| ... | ... |
@@ -1723,10 +2195,10 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1723 | 2195 |
unless ($state->{configured_ok}) {
|
| 1724 | 2196 |
while (1) {
|
| 1725 | 2197 |
my $ans = lc $self->prompt("Configuring $dist->{dist} failed.\nYou can s)kip, r)etry, e)xamine build log, or l)ook ?", "s");
|
| 1726 |
- last if $ans eq 's'; |
|
| 1727 |
- return $self->configure_this($dist) if $ans eq 'r'; |
|
| 1728 |
- $self->show_build_log if $ans eq 'e'; |
|
| 1729 |
- $self->look if $ans eq 'l'; |
|
| 2198 |
+ last if $ans eq 's'; |
|
| 2199 |
+ return $self->configure_this($dist, $depth) if $ans eq 'r'; |
|
| 2200 |
+ $self->show_build_log if $ans eq 'e'; |
|
| 2201 |
+ $self->look if $ans eq 'l'; |
|
| 1730 | 2202 |
} |
| 1731 | 2203 |
} |
| 1732 | 2204 |
|
| ... | ... |
@@ -1767,12 +2239,12 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1767 | 2239 |
qw( blib/lib blib/arch ) # FCGI.pm :( |
| 1768 | 2240 |
); |
| 1769 | 2241 |
|
| 1770 |
- mkdir "blib/meta", 0777 or die $!; |
|
| 2242 |
+ File::Path::mkpath("blib/meta", 0, 0777);
|
|
| 1771 | 2243 |
|
| 1772 | 2244 |
my $local = {
|
| 1773 | 2245 |
name => $module_name, |
| 1774 |
- module => $module, |
|
| 1775 |
- version => $provides->{$module}{version} || $dist->{version},
|
|
| 2246 |
+ target => $module, |
|
| 2247 |
+ version => $provides->{$module_name}{version} || $dist->{version},
|
|
| 1776 | 2248 |
dist => $dist->{distvname},
|
| 1777 | 2249 |
pathname => $dist->{pathname},
|
| 1778 | 2250 |
provides => $provides, |
| ... | ... |
@@ -1834,6 +2306,15 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1834 | 2306 |
sub extract_meta_prereqs {
|
| 1835 | 2307 |
my($self, $dist) = @_; |
| 1836 | 2308 |
|
| 2309 |
+ if ($dist->{cpanfile}) {
|
|
| 2310 |
+ my $prereq = $dist->{cpanfile}->prereq;
|
|
| 2311 |
+ my @phase = $self->{notest} ? qw( build runtime ) : qw( build test runtime );
|
|
| 2312 |
+ require CPAN::Meta::Requirements; |
|
| 2313 |
+ my $req = CPAN::Meta::Requirements->new; |
|
| 2314 |
+ $req->add_requirements($prereq->requirements_for($_, 'requires')) for @phase; |
|
| 2315 |
+ return %{$req->as_string_hash};
|
|
| 2316 |
+ } |
|
| 2317 |
+ |
|
| 1837 | 2318 |
my $meta = $dist->{meta};
|
| 1838 | 2319 |
|
| 1839 | 2320 |
my @deps; |
| ... | ... |
@@ -1865,7 +2346,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1865 | 2346 |
$self->chat("Finding PREREQ from Makefile ...\n");
|
| 1866 | 2347 |
open my $mf, "Makefile"; |
| 1867 | 2348 |
while (<$mf>) {
|
| 1868 |
- if (/^\#\s+PREREQ_PM => {\s*(.*?)\s*}/) {
|
|
| 2349 |
+ if (/^\#\s+PREREQ_PM => \{\s*(.*?)\s*\}/) {
|
|
| 1869 | 2350 |
my @all; |
| 1870 | 2351 |
my @pairs = split ', ', $1; |
| 1871 | 2352 |
for (@pairs) {
|
| ... | ... |
@@ -1992,7 +2473,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 1992 | 2473 |
require JSON::PP; |
| 1993 | 2474 |
print JSON::PP::encode_json($self->{scandeps_tree});
|
| 1994 | 2475 |
} elsif ($self->{format} eq 'yaml') {
|
| 1995 |
- require YAML; |
|
| 2476 |
+ require YAML; # no fatpack |
|
| 1996 | 2477 |
print YAML::Dump($self->{scandeps_tree});
|
| 1997 | 2478 |
} else {
|
| 1998 | 2479 |
$self->diag("Unknown format: $self->{format}\n");
|
| ... | ... |
@@ -2042,20 +2523,50 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2042 | 2523 |
return; |
| 2043 | 2524 |
} |
| 2044 | 2525 |
|
| 2045 |
- sub get { $_[0]->{_backends}{get}->(@_) };
|
|
| 2046 |
- sub mirror { $_[0]->{_backends}{mirror}->(@_) };
|
|
| 2526 |
+ sub get {
|
|
| 2527 |
+ my($self, $uri) = @_; |
|
| 2528 |
+ if ($uri =~ /^file:/) {
|
|
| 2529 |
+ $self->file_get($uri); |
|
| 2530 |
+ } else {
|
|
| 2531 |
+ $self->{_backends}{get}->(@_);
|
|
| 2532 |
+ } |
|
| 2533 |
+ } |
|
| 2534 |
+ |
|
| 2535 |
+ sub mirror {
|
|
| 2536 |
+ my($self, $uri, $local) = @_; |
|
| 2537 |
+ if ($uri =~ /^file:/) {
|
|
| 2538 |
+ $self->file_mirror($uri, $local); |
|
| 2539 |
+ } else {
|
|
| 2540 |
+ $self->{_backends}{mirror}->(@_);
|
|
| 2541 |
+ } |
|
| 2542 |
+ } |
|
| 2543 |
+ |
|
| 2047 | 2544 |
sub untar { $_[0]->{_backends}{untar}->(@_) };
|
| 2048 | 2545 |
sub unzip { $_[0]->{_backends}{unzip}->(@_) };
|
| 2049 | 2546 |
|
| 2547 |
+ sub uri_to_file {
|
|
| 2548 |
+ my($self, $uri) = @_; |
|
| 2549 |
+ |
|
| 2550 |
+ # file:///path/to/file -> /path/to/file |
|
| 2551 |
+ # file://C:/path -> C:/path |
|
| 2552 |
+ if ($uri =~ s!file:/+!!) {
|
|
| 2553 |
+ $uri = "/$uri" unless $uri =~ m![a-zA-Z]:!; |
|
| 2554 |
+ } |
|
| 2555 |
+ |
|
| 2556 |
+ return $uri; |
|
| 2557 |
+ } |
|
| 2558 |
+ |
|
| 2050 | 2559 |
sub file_get {
|
| 2051 | 2560 |
my($self, $uri) = @_; |
| 2052 |
- open my $fh, "<$uri" or return; |
|
| 2561 |
+ my $file = $self->uri_to_file($uri); |
|
| 2562 |
+ open my $fh, "<$file" or return; |
|
| 2053 | 2563 |
join '', <$fh>; |
| 2054 | 2564 |
} |
| 2055 | 2565 |
|
| 2056 | 2566 |
sub file_mirror {
|
| 2057 | 2567 |
my($self, $uri, $path) = @_; |
| 2058 |
- File::Copy::copy($uri, $path); |
|
| 2568 |
+ my $file = $self->uri_to_file($uri); |
|
| 2569 |
+ File::Copy::copy($file, $path); |
|
| 2059 | 2570 |
} |
| 2060 | 2571 |
|
| 2061 | 2572 |
sub init_tools {
|
| ... | ... |
@@ -2074,7 +2585,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2074 | 2585 |
LWP::UserAgent->new( |
| 2075 | 2586 |
parse_head => 0, |
| 2076 | 2587 |
env_proxy => 1, |
| 2077 |
- agent => "cpanminus/$VERSION", |
|
| 2588 |
+ agent => $self->agent, |
|
| 2078 | 2589 |
timeout => 30, |
| 2079 | 2590 |
@_, |
| 2080 | 2591 |
); |
| ... | ... |
@@ -2092,49 +2603,57 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2092 | 2603 |
}; |
| 2093 | 2604 |
} elsif ($self->{try_wget} and my $wget = $self->which('wget')) {
|
| 2094 | 2605 |
$self->chat("You have $wget\n");
|
| 2606 |
+ my @common = ( |
|
| 2607 |
+ '--user-agent', $self->agent, |
|
| 2608 |
+ '--retry-connrefused', |
|
| 2609 |
+ ($self->{verbose} ? () : ('-q')),
|
|
| 2610 |
+ ); |
|
| 2095 | 2611 |
$self->{_backends}{get} = sub {
|
| 2096 | 2612 |
my($self, $uri) = @_; |
| 2097 |
- return $self->file_get($uri) if $uri =~ s!^file:/+!/!; |
|
| 2098 |
- $self->safeexec( my $fh, $wget, $uri, ( $self->{verbose} ? () : '-q' ), '-O', '-' ) or die "wget $uri: $!";
|
|
| 2613 |
+ $self->safeexec( my $fh, $wget, $uri, @common, '-O', '-' ) or die "wget $uri: $!"; |
|
| 2099 | 2614 |
local $/; |
| 2100 | 2615 |
<$fh>; |
| 2101 | 2616 |
}; |
| 2102 | 2617 |
$self->{_backends}{mirror} = sub {
|
| 2103 | 2618 |
my($self, $uri, $path) = @_; |
| 2104 |
- return $self->file_mirror($uri, $path) if $uri =~ s!^file:/+!/!; |
|
| 2105 |
- $self->safeexec( my $fh, $wget, '--retry-connrefused', $uri, ( $self->{verbose} ? () : '-q' ), '-O', $path ) or die "wget $uri: $!";
|
|
| 2619 |
+ $self->safeexec( my $fh, $wget, $uri, @common, '-O', $path ) or die "wget $uri: $!"; |
|
| 2106 | 2620 |
local $/; |
| 2107 | 2621 |
<$fh>; |
| 2108 | 2622 |
}; |
| 2109 | 2623 |
} elsif ($self->{try_curl} and my $curl = $self->which('curl')) {
|
| 2110 | 2624 |
$self->chat("You have $curl\n");
|
| 2625 |
+ my @common = ( |
|
| 2626 |
+ '--location', |
|
| 2627 |
+ '--user-agent', $self->agent, |
|
| 2628 |
+ ($self->{verbose} ? () : '-s'),
|
|
| 2629 |
+ ); |
|
| 2111 | 2630 |
$self->{_backends}{get} = sub {
|
| 2112 | 2631 |
my($self, $uri) = @_; |
| 2113 |
- return $self->file_get($uri) if $uri =~ s!^file:/+!/!; |
|
| 2114 |
- $self->safeexec( my $fh, $curl, '-L', ( $self->{verbose} ? () : '-s' ), $uri ) or die "curl $uri: $!";
|
|
| 2632 |
+ $self->safeexec( my $fh, $curl, @common, $uri ) or die "curl $uri: $!"; |
|
| 2115 | 2633 |
local $/; |
| 2116 | 2634 |
<$fh>; |
| 2117 | 2635 |
}; |
| 2118 | 2636 |
$self->{_backends}{mirror} = sub {
|
| 2119 | 2637 |
my($self, $uri, $path) = @_; |
| 2120 |
- return $self->file_mirror($uri, $path) if $uri =~ s!^file:/+!/!; |
|
| 2121 |
- $self->safeexec( my $fh, $curl, '-L', $uri, ( $self->{verbose} ? () : '-s' ), '-#', '-o', $path ) or die "curl $uri: $!";
|
|
| 2638 |
+ $self->safeexec( my $fh, $curl, @common, $uri, '-#', '-o', $path ) or die "curl $uri: $!"; |
|
| 2122 | 2639 |
local $/; |
| 2123 | 2640 |
<$fh>; |
| 2124 | 2641 |
}; |
| 2125 | 2642 |
} else {
|
| 2126 | 2643 |
require HTTP::Tiny; |
| 2127 | 2644 |
$self->chat("Falling back to HTTP::Tiny $HTTP::Tiny::VERSION\n");
|
| 2128 |
- |
|
| 2645 |
+ my %common = ( |
|
| 2646 |
+ agent => $self->agent, |
|
| 2647 |
+ ); |
|
| 2129 | 2648 |
$self->{_backends}{get} = sub {
|
| 2130 | 2649 |
my $self = shift; |
| 2131 |
- my $res = HTTP::Tiny->new->get($_[0]); |
|
| 2650 |
+ my $res = HTTP::Tiny->new(%common)->get($_[0]); |
|
| 2132 | 2651 |
return unless $res->{success};
|
| 2133 | 2652 |
return $res->{content};
|
| 2134 | 2653 |
}; |
| 2135 | 2654 |
$self->{_backends}{mirror} = sub {
|
| 2136 | 2655 |
my $self = shift; |
| 2137 |
- my $res = HTTP::Tiny->new->mirror(@_); |
|
| 2656 |
+ my $res = HTTP::Tiny->new(%common)->mirror(@_); |
|
| 2138 | 2657 |
return $res->{status};
|
| 2139 | 2658 |
}; |
| 2140 | 2659 |
} |
| ... | ... |
@@ -2149,17 +2668,25 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2149 | 2668 |
$self->{_backends}{untar} = sub {
|
| 2150 | 2669 |
my($self, $tarfile) = @_; |
| 2151 | 2670 |
|
| 2152 |
- my $xf = "xf" . ($self->{verbose} ? 'v' : '');
|
|
| 2671 |
+ my $xf = ($self->{verbose} ? 'v' : '')."xf";
|
|
| 2153 | 2672 |
my $ar = $tarfile =~ /bz2$/ ? 'j' : 'z'; |
| 2154 | 2673 |
|
| 2155 |
- my($root, @others) = `$tar tf$ar $tarfile` |
|
| 2674 |
+ my($root, @others) = `$tar ${ar}tf $tarfile`
|
|
| 2156 | 2675 |
or return undef; |
| 2157 | 2676 |
|
| 2158 |
- chomp $root; |
|
| 2159 |
- $root =~ s!^\./!!; |
|
| 2160 |
- $root =~ s{^(.+?)/.*$}{$1};
|
|
| 2677 |
+ FILE: {
|
|
| 2678 |
+ chomp $root; |
|
| 2679 |
+ $root =~ s!^\./!!; |
|
| 2680 |
+ $root =~ s{^(.+?)/.*$}{$1};
|
|
| 2161 | 2681 |
|
| 2162 |
- system "$tar $xf$ar $tarfile"; |
|
| 2682 |
+ if (!length($root)) {
|
|
| 2683 |
+ # archive had ./ as the first entry, so try again |
|
| 2684 |
+ $root = shift(@others); |
|
| 2685 |
+ redo FILE if $root; |
|
| 2686 |
+ } |
|
| 2687 |
+ } |
|
| 2688 |
+ |
|
| 2689 |
+ system "$tar $ar$xf $tarfile"; |
|
| 2163 | 2690 |
return $root if -d $root; |
| 2164 | 2691 |
|
| 2165 | 2692 |
$self->diag_fail("Bad archive: $tarfile");
|
| ... | ... |
@@ -2178,8 +2705,17 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2178 | 2705 |
my($root, @others) = `$ar -dc $tarfile | $tar tf -` |
| 2179 | 2706 |
or return undef; |
| 2180 | 2707 |
|
| 2181 |
- chomp $root; |
|
| 2182 |
- $root =~ s{^(.+?)/.*$}{$1};
|
|
| 2708 |
+ FILE: {
|
|
| 2709 |
+ chomp $root; |
|
| 2710 |
+ $root =~ s!^\./!!; |
|
| 2711 |
+ $root =~ s{^(.+?)/.*$}{$1};
|
|
| 2712 |
+ |
|
| 2713 |
+ if (!length($root)) {
|
|
| 2714 |
+ # archive had ./ as the first entry, so try again |
|
| 2715 |
+ $root = shift(@others); |
|
| 2716 |
+ redo FILE if $root; |
|
| 2717 |
+ } |
|
| 2718 |
+ } |
|
| 2183 | 2719 |
|
| 2184 | 2720 |
system "$ar -dc $tarfile | $tar $x"; |
| 2185 | 2721 |
return $root if -d $root; |
| ... | ... |
@@ -2192,8 +2728,17 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2192 | 2728 |
$self->{_backends}{untar} = sub {
|
| 2193 | 2729 |
my $self = shift; |
| 2194 | 2730 |
my $t = Archive::Tar->new($_[0]); |
| 2195 |
- my $root = ($t->list_files)[0]; |
|
| 2196 |
- $root =~ s{^(.+?)/.*$}{$1};
|
|
| 2731 |
+ my($root, @others) = $t->list_files; |
|
| 2732 |
+ FILE: {
|
|
| 2733 |
+ $root =~ s!^\./!!; |
|
| 2734 |
+ $root =~ s{^(.+?)/.*$}{$1};
|
|
| 2735 |
+ |
|
| 2736 |
+ if (!length($root)) {
|
|
| 2737 |
+ # archive had ./ as the first entry, so try again |
|
| 2738 |
+ $root = shift(@others); |
|
| 2739 |
+ redo FILE if $root; |
|
| 2740 |
+ } |
|
| 2741 |
+ } |
|
| 2197 | 2742 |
$t->extract; |
| 2198 | 2743 |
return -d $root ? $root : undef; |
| 2199 | 2744 |
}; |
| ... | ... |
@@ -2213,7 +2758,7 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2213 | 2758 |
or return undef; |
| 2214 | 2759 |
|
| 2215 | 2760 |
chomp $root; |
| 2216 |
- $root =~ s{^\s+testing:\s+(.+?)/\s+OK$}{$1};
|
|
| 2761 |
+ $root =~ s{^\s+testing:\s+([^/]+)/.*?\s+OK$}{$1};
|
|
| 2217 | 2762 |
|
| 2218 | 2763 |
system "$unzip $opt $zipfile"; |
| 2219 | 2764 |
return $root if -d $root; |
| ... | ... |
@@ -2232,15 +2777,16 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2232 | 2777 |
$self->diag_fail("Read of file[$file] failed")
|
| 2233 | 2778 |
if $status != Archive::Zip::AZ_OK(); |
| 2234 | 2779 |
my @members = $zip->members(); |
| 2235 |
- my $root; |
|
| 2236 | 2780 |
for my $member ( @members ) {
|
| 2237 | 2781 |
my $af = $member->fileName(); |
| 2238 | 2782 |
next if ($af =~ m!^(/|\.\./)!); |
| 2239 |
- $root = $af unless $root; |
|
| 2240 | 2783 |
$status = $member->extractToFileNamed( $af ); |
| 2241 | 2784 |
$self->diag_fail("Extracting of file[$af] from zipfile[$file failed")
|
| 2242 | 2785 |
if $status != Archive::Zip::AZ_OK(); |
| 2243 | 2786 |
} |
| 2787 |
+ |
|
| 2788 |
+ my ($root) = $zip->membersMatching( qr<^[^/]+/$> ); |
|
| 2789 |
+ $root &&= $root->fileName; |
|
| 2244 | 2790 |
return -d $root ? $root : undef; |
| 2245 | 2791 |
}; |
| 2246 | 2792 |
} |
| ... | ... |
@@ -2269,12 +2815,12 @@ $fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
|
| 2269 | 2815 |
|
| 2270 | 2816 |
sub parse_meta {
|
| 2271 | 2817 |
my($self, $file) = @_; |
| 2272 |
- return eval { (Parse::CPAN::Meta::LoadFile($file))[0] } || undef;
|
|
| 2818 |
+ return eval { Parse::CPAN::Meta->load_file($file) };
|
|
| 2273 | 2819 |
} |
| 2274 | 2820 |
|
| 2275 | 2821 |
sub parse_meta_string {
|
| 2276 | 2822 |
my($self, $yaml) = @_; |
| 2277 |
- return eval { (Parse::CPAN::Meta::Load($yaml))[0] } || undef;
|
|
| 2823 |
+ return eval { Parse::CPAN::Meta->load_yaml_string($yaml) };
|
|
| 2278 | 2824 |
} |
| 2279 | 2825 |
|
| 2280 | 2826 |
1; |
| ... | ... |
@@ -2284,7 +2830,7 @@ $fatpacked{"CPAN/DistnameInfo.pm"} = <<'CPAN_DISTNAMEINFO';
|
| 2284 | 2830 |
|
| 2285 | 2831 |
package CPAN::DistnameInfo; |
| 2286 | 2832 |
|
| 2287 |
- $VERSION = "0.11"; |
|
| 2833 |
+ $VERSION = "0.12"; |
|
| 2288 | 2834 |
use strict; |
| 2289 | 2835 |
|
| 2290 | 2836 |
sub distname_info {
|
| ... | ... |
@@ -2315,6 +2861,14 @@ $fatpacked{"CPAN/DistnameInfo.pm"} = <<'CPAN_DISTNAMEINFO';
|
| 2315 | 2861 |
$version = $2; |
| 2316 | 2862 |
} |
| 2317 | 2863 |
|
| 2864 |
+ if ($version =~ /(.+_.*)-(\d.*)/) {
|
|
| 2865 |
+ # Catch names like Task-Deprecations5_14-1.00.tar.gz where the 5_14 is |
|
| 2866 |
+ # part of the distname. However, names like libao-perl_0.03-1.tar.gz |
|
| 2867 |
+ # should still have 0.03-1 as their version. |
|
| 2868 |
+ $dist .= $1; |
|
| 2869 |
+ $version = $2; |
|
| 2870 |
+ } |
|
| 2871 |
+ |
|
| 2318 | 2872 |
# Normalize the Dist.pm-1.23 convention which CGI.pm and |
| 2319 | 2873 |
# a few others use. |
| 2320 | 2874 |
$dist =~ s{\.pm$}{};
|
| ... | ... |
@@ -2392,10 +2946,7 @@ $fatpacked{"CPAN/Meta.pm"} = <<'CPAN_META';
|
| 2392 | 2946 |
use strict; |
| 2393 | 2947 |
use warnings; |
| 2394 | 2948 |
package CPAN::Meta; |
| 2395 |
- BEGIN {
|
|
| 2396 |
- $CPAN::Meta::VERSION = '2.110930'; |
|
| 2397 |
- } |
|
| 2398 |
- # ABSTRACT: the distribution metadata for a CPAN dist |
|
| 2949 |
+ our $VERSION = '2.120921'; # VERSION |
|
| 2399 | 2950 |
|
| 2400 | 2951 |
|
| 2401 | 2952 |
use Carp qw(carp croak); |
| ... | ... |
@@ -2403,15 +2954,9 @@ $fatpacked{"CPAN/Meta.pm"} = <<'CPAN_META';
|
| 2403 | 2954 |
use CPAN::Meta::Prereqs; |
| 2404 | 2955 |
use CPAN::Meta::Converter; |
| 2405 | 2956 |
use CPAN::Meta::Validator; |
| 2406 |
- use Parse::CPAN::Meta 1.4400 (); |
|
| 2957 |
+ use Parse::CPAN::Meta 1.4403 (); |
|
| 2407 | 2958 |
|
| 2408 |
- sub _dclone {
|
|
| 2409 |
- my $ref = shift; |
|
| 2410 |
- my $backend = Parse::CPAN::Meta->json_backend(); |
|
| 2411 |
- return $backend->new->decode( |
|
| 2412 |
- $backend->new->convert_blessed->encode($ref) |
|
| 2413 |
- ); |
|
| 2414 |
- } |
|
| 2959 |
+ BEGIN { *_dclone = \&CPAN::Meta::Converter::_dclone }
|
|
| 2415 | 2960 |
|
| 2416 | 2961 |
|
| 2417 | 2962 |
BEGIN {
|
| ... | ... |
@@ -2733,6 +3278,8 @@ $fatpacked{"CPAN/Meta.pm"} = <<'CPAN_META';
|
| 2733 | 3278 |
|
| 2734 | 3279 |
1; |
| 2735 | 3280 |
|
| 3281 |
+ # ABSTRACT: the distribution metadata for a CPAN dist |
|
| 3282 |
+ |
|
| 2736 | 3283 |
|
| 2737 | 3284 |
|
| 2738 | 3285 |
|
| ... | ... |
@@ -2746,21 +3293,27 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 2746 | 3293 |
use strict; |
| 2747 | 3294 |
use warnings; |
| 2748 | 3295 |
package CPAN::Meta::Converter; |
| 2749 |
- BEGIN {
|
|
| 2750 |
- $CPAN::Meta::Converter::VERSION = '2.110930'; |
|
| 2751 |
- } |
|
| 2752 |
- # ABSTRACT: Convert CPAN distribution metadata structures |
|
| 3296 |
+ our $VERSION = '2.120921'; # VERSION |
|
| 2753 | 3297 |
|
| 2754 | 3298 |
|
| 2755 | 3299 |
use CPAN::Meta::Validator; |
| 2756 |
- use version 0.82 (); |
|
| 3300 |
+ use CPAN::Meta::Requirements; |
|
| 3301 |
+ use version 0.88 (); |
|
| 2757 | 3302 |
use Parse::CPAN::Meta 1.4400 (); |
| 2758 | 3303 |
|
| 2759 | 3304 |
sub _dclone {
|
| 2760 | 3305 |
my $ref = shift; |
| 3306 |
+ |
|
| 3307 |
+ # if an object is in the data structure and doesn't specify how to |
|
| 3308 |
+ # turn itself into JSON, we just stringify the object. That does the |
|
| 3309 |
+ # right thing for typical things that might be there, like version objects, |
|
| 3310 |
+ # Path::Class objects, etc. |
|
| 3311 |
+ no warnings 'once'; |
|
| 3312 |
+ local *UNIVERSAL::TO_JSON = sub { return "$_[0]" };
|
|
| 3313 |
+ |
|
| 2761 | 3314 |
my $backend = Parse::CPAN::Meta->json_backend(); |
| 2762 |
- return $backend->new->decode( |
|
| 2763 |
- $backend->new->convert_blessed->encode($ref) |
|
| 3315 |
+ return $backend->new->utf8->decode( |
|
| 3316 |
+ $backend->new->utf8->allow_blessed->convert_blessed->encode($ref) |
|
| 2764 | 3317 |
); |
| 2765 | 3318 |
} |
| 2766 | 3319 |
|
| ... | ... |
@@ -3055,7 +3608,10 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3055 | 3608 |
return 0 if ! length $element; |
| 3056 | 3609 |
return 0 if ( $element eq 'undef' || $element eq '<undef>' ); |
| 3057 | 3610 |
|
| 3058 |
- if ( my $v = eval { version->new($element) } ) {
|
|
| 3611 |
+ my $v = eval { version->new($element) };
|
|
| 3612 |
+ # XXX check defined $v and not just $v because version objects leak memory |
|
| 3613 |
+ # in boolean context -- dagolden, 2012-02-03 |
|
| 3614 |
+ if ( defined $v ) {
|
|
| 3059 | 3615 |
return $v->is_qv ? $v->normal : $element; |
| 3060 | 3616 |
} |
| 3061 | 3617 |
else {
|
| ... | ... |
@@ -3063,29 +3619,36 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3063 | 3619 |
} |
| 3064 | 3620 |
} |
| 3065 | 3621 |
|
| 3622 |
+ sub _bad_version_hook {
|
|
| 3623 |
+ my ($v) = @_; |
|
| 3624 |
+ $v =~ s{[a-z]+$}{}; # strip trailing alphabetics
|
|
| 3625 |
+ my $vobj = eval { version->parse($v) };
|
|
| 3626 |
+ return defined($vobj) ? $vobj : version->parse(0); # or give up |
|
| 3627 |
+ } |
|
| 3628 |
+ |
|
| 3066 | 3629 |
sub _version_map {
|
| 3067 | 3630 |
my ($element) = @_; |
| 3068 |
- return undef unless defined $element; |
|
| 3631 |
+ return unless defined $element; |
|
| 3069 | 3632 |
if ( ref $element eq 'HASH' ) {
|
| 3070 |
- my $new_map = {};
|
|
| 3071 |
- for my $k ( keys %$element ) {
|
|
| 3633 |
+ # XXX turn this into CPAN::Meta::Requirements with bad version hook |
|
| 3634 |
+ # and then turn it back into a hash |
|
| 3635 |
+ my $new_map = CPAN::Meta::Requirements->new( |
|
| 3636 |
+ { bad_version_hook => sub { version->new(0) } } # punt
|
|
| 3637 |
+ ); |
|
| 3638 |
+ while ( my ($k,$v) = each %$element ) {
|
|
| 3072 | 3639 |
next unless _is_module_name($k); |
| 3073 |
- my $value = $element->{$k};
|
|
| 3074 |
- if ( ! ( defined $value && length $value ) ) {
|
|
| 3075 |
- $new_map->{$k} = 0;
|
|
| 3076 |
- } |
|
| 3077 |
- elsif ( $value eq 'undef' || $value eq '<undef>' ) {
|
|
| 3078 |
- $new_map->{$k} = 0;
|
|
| 3640 |
+ if ( !defined($v) || !length($v) || $v eq 'undef' || $v eq '<undef>' ) {
|
|
| 3641 |
+ $v = 0; |
|
| 3079 | 3642 |
} |
| 3080 |
- elsif ( _is_module_name( $value ) ) { # some weird, old META have this
|
|
| 3081 |
- $new_map->{$k} = 0;
|
|
| 3082 |
- $new_map->{$value} = 0;
|
|
| 3083 |
- } |
|
| 3084 |
- else {
|
|
| 3085 |
- $new_map->{$k} = _clean_version($value);
|
|
| 3643 |
+ # some weird, old META have bad yml with module => module |
|
| 3644 |
+ # so check if value is like a module name and not like a version |
|
| 3645 |
+ if ( _is_module_name($v) && ! version::is_lax($v) ) {
|
|
| 3646 |
+ $new_map->add_minimum($k => 0); |
|
| 3647 |
+ $new_map->add_minimum($v => 0); |
|
| 3086 | 3648 |
} |
| 3649 |
+ $new_map->add_string_requirement($k => $v); |
|
| 3087 | 3650 |
} |
| 3088 |
- return $new_map; |
|
| 3651 |
+ return $new_map->as_string_hash; |
|
| 3089 | 3652 |
} |
| 3090 | 3653 |
elsif ( ref $element eq 'ARRAY' ) {
|
| 3091 | 3654 |
my $hashref = { map { $_ => 0 } @$element };
|
| ... | ... |
@@ -3168,9 +3731,8 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3168 | 3731 |
my $test_h = _extract_prereqs($_[2]->{prereqs}, qw(test requires)) || {};
|
| 3169 | 3732 |
my $build_h = _extract_prereqs($_[2]->{prereqs}, qw(build requires)) || {};
|
| 3170 | 3733 |
|
| 3171 |
- require Version::Requirements; |
|
| 3172 |
- my $test_req = Version::Requirements->from_string_hash($test_h); |
|
| 3173 |
- my $build_req = Version::Requirements->from_string_hash($build_h); |
|
| 3734 |
+ my $test_req = CPAN::Meta::Requirements->from_string_hash($test_h); |
|
| 3735 |
+ my $build_req = CPAN::Meta::Requirements->from_string_hash($build_h); |
|
| 3174 | 3736 |
|
| 3175 | 3737 |
$test_req->add_requirements($build_req)->as_string_hash; |
| 3176 | 3738 |
} |
| ... | ... |
@@ -3178,12 +3740,12 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3178 | 3740 |
sub _extract_prereqs {
|
| 3179 | 3741 |
my ($prereqs, $phase, $type) = @_; |
| 3180 | 3742 |
return unless ref $prereqs eq 'HASH'; |
| 3181 |
- return $prereqs->{$phase}{$type};
|
|
| 3743 |
+ return scalar _version_map($prereqs->{$phase}{$type});
|
|
| 3182 | 3744 |
} |
| 3183 | 3745 |
|
| 3184 | 3746 |
sub _downgrade_optional_features {
|
| 3185 | 3747 |
my (undef, undef, $meta) = @_; |
| 3186 |
- return undef unless exists $meta->{optional_features};
|
|
| 3748 |
+ return unless exists $meta->{optional_features};
|
|
| 3187 | 3749 |
my $origin = $meta->{optional_features};
|
| 3188 | 3750 |
my $features = {};
|
| 3189 | 3751 |
for my $name ( keys %$origin ) {
|
| ... | ... |
@@ -3204,7 +3766,7 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3204 | 3766 |
|
| 3205 | 3767 |
sub _upgrade_optional_features {
|
| 3206 | 3768 |
my (undef, undef, $meta) = @_; |
| 3207 |
- return undef unless exists $meta->{optional_features};
|
|
| 3769 |
+ return unless exists $meta->{optional_features};
|
|
| 3208 | 3770 |
my $origin = $meta->{optional_features};
|
| 3209 | 3771 |
my $features = {};
|
| 3210 | 3772 |
for my $name ( keys %$origin ) {
|
| ... | ... |
@@ -3300,7 +3862,7 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3300 | 3862 |
return unless $item; |
| 3301 | 3863 |
if ( $item =~ m{^mailto:(.*)$} ) { return { mailto => $1 } }
|
| 3302 | 3864 |
elsif( _is_urlish($item) ) { return { web => $item } }
|
| 3303 |
- else { return undef }
|
|
| 3865 |
+ else { return }
|
|
| 3304 | 3866 |
}, |
| 3305 | 3867 |
repository => sub { return _is_urlish($_[0]) ? { url => $_[0] } : undef },
|
| 3306 | 3868 |
':custom' => \&_prefix_custom, |
| ... | ... |
@@ -3308,7 +3870,7 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3308 | 3870 |
|
| 3309 | 3871 |
sub _upgrade_resources_2 {
|
| 3310 | 3872 |
my (undef, undef, $meta, $version) = @_; |
| 3311 |
- return undef unless exists $meta->{resources};
|
|
| 3873 |
+ return unless exists $meta->{resources};
|
|
| 3312 | 3874 |
return _convert($meta->{resources}, $resource2_upgrade);
|
| 3313 | 3875 |
} |
| 3314 | 3876 |
|
| ... | ... |
@@ -3346,7 +3908,7 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3346 | 3908 |
|
| 3347 | 3909 |
sub _cleanup_resources_2 {
|
| 3348 | 3910 |
my ($resources, $key, $meta, $to_version) = @_; |
| 3349 |
- return undef unless $resources && ref $resources eq 'HASH'; |
|
| 3911 |
+ return unless $resources && ref $resources eq 'HASH'; |
|
| 3350 | 3912 |
return _convert($resources, $resources2_cleanup, $to_version); |
| 3351 | 3913 |
} |
| 3352 | 3914 |
|
| ... | ... |
@@ -3360,7 +3922,7 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3360 | 3922 |
|
| 3361 | 3923 |
sub _resources_1_3 {
|
| 3362 | 3924 |
my (undef, undef, $meta, $version) = @_; |
| 3363 |
- return undef unless exists $meta->{resources};
|
|
| 3925 |
+ return unless exists $meta->{resources};
|
|
| 3364 | 3926 |
return _convert($meta->{resources}, $resource1_spec);
|
| 3365 | 3927 |
} |
| 3366 | 3928 |
|
| ... | ... |
@@ -3373,7 +3935,7 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3373 | 3935 |
$resources->{license} = $meta->license_url
|
| 3374 | 3936 |
if _is_urlish($meta->{license_url});
|
| 3375 | 3937 |
} |
| 3376 |
- return undef unless keys %$resources; |
|
| 3938 |
+ return unless keys %$resources; |
|
| 3377 | 3939 |
return _convert($resources, $resource1_spec); |
| 3378 | 3940 |
} |
| 3379 | 3941 |
|
| ... | ... |
@@ -3387,7 +3949,7 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3387 | 3949 |
|
| 3388 | 3950 |
sub _downgrade_resources {
|
| 3389 | 3951 |
my (undef, undef, $meta, $version) = @_; |
| 3390 |
- return undef unless exists $meta->{resources};
|
|
| 3952 |
+ return unless exists $meta->{resources};
|
|
| 3391 | 3953 |
return _convert($meta->{resources}, $resource_downgrade_spec);
|
| 3392 | 3954 |
} |
| 3393 | 3955 |
|
| ... | ... |
@@ -3987,6 +4549,8 @@ $fatpacked{"CPAN/Meta/Converter.pm"} = <<'CPAN_META_CONVERTER';
|
| 3987 | 4549 |
|
| 3988 | 4550 |
1; |
| 3989 | 4551 |
|
| 4552 |
+ # ABSTRACT: Convert CPAN distribution metadata structures |
|
| 4553 |
+ |
|
| 3990 | 4554 |
|
| 3991 | 4555 |
|
| 3992 | 4556 |
|
| ... | ... |
@@ -4000,10 +4564,7 @@ $fatpacked{"CPAN/Meta/Feature.pm"} = <<'CPAN_META_FEATURE';
|
| 4000 | 4564 |
use strict; |
| 4001 | 4565 |
use warnings; |
| 4002 | 4566 |
package CPAN::Meta::Feature; |
| 4003 |
- BEGIN {
|
|
| 4004 |
- $CPAN::Meta::Feature::VERSION = '2.110930'; |
|
| 4005 |
- } |
|
| 4006 |
- # ABSTRACT: an optional feature provided by a CPAN distribution |
|
| 4567 |
+ our $VERSION = '2.120921'; # VERSION |
|
| 4007 | 4568 |
|
| 4008 | 4569 |
use CPAN::Meta::Prereqs; |
| 4009 | 4570 |
|
| ... | ... |
@@ -4031,6 +4592,8 @@ $fatpacked{"CPAN/Meta/Feature.pm"} = <<'CPAN_META_FEATURE';
|
| 4031 | 4592 |
|
| 4032 | 4593 |
1; |
| 4033 | 4594 |
|
| 4595 |
+ # ABSTRACT: an optional feature provided by a CPAN distribution |
|
| 4596 |
+ |
|
| 4034 | 4597 |
|
| 4035 | 4598 |
|
| 4036 | 4599 |
|
| ... | ... |
@@ -4046,12 +4609,12 @@ $fatpacked{"CPAN/Meta/History.pm"} = <<'CPAN_META_HISTORY';
|
| 4046 | 4609 |
use strict; |
| 4047 | 4610 |
use warnings; |
| 4048 | 4611 |
package CPAN::Meta::History; |
| 4049 |
- BEGIN {
|
|
| 4050 |
- $CPAN::Meta::History::VERSION = '2.110930'; |
|
| 4051 |
- } |
|
| 4052 |
- # ABSTRACT: history of CPAN Meta Spec changes |
|
| 4612 |
+ our $VERSION = '2.120921'; # VERSION |
|
| 4613 |
+ |
|
| 4053 | 4614 |
1; |
| 4054 | 4615 |
|
| 4616 |
+ # ABSTRACT: history of CPAN Meta Spec changes |
|
| 4617 |
+ |
|
| 4055 | 4618 |
|
| 4056 | 4619 |
|
| 4057 | 4620 |
__END__ |
| ... | ... |
@@ -4064,15 +4627,12 @@ $fatpacked{"CPAN/Meta/Prereqs.pm"} = <<'CPAN_META_PREREQS';
|
| 4064 | 4627 |
use strict; |
| 4065 | 4628 |
use warnings; |
| 4066 | 4629 |
package CPAN::Meta::Prereqs; |
| 4067 |
- BEGIN {
|
|
| 4068 |
- $CPAN::Meta::Prereqs::VERSION = '2.110930'; |
|
| 4069 |
- } |
|
| 4070 |
- # ABSTRACT: a set of distribution prerequisites by phase and type |
|
| 4630 |
+ our $VERSION = '2.120921'; # VERSION |
|
| 4071 | 4631 |
|
| 4072 | 4632 |
|
| 4073 | 4633 |
use Carp qw(confess); |
| 4074 | 4634 |
use Scalar::Util qw(blessed); |
| 4075 |
- use Version::Requirements 0.101020; # finalize |
|
| 4635 |
+ use CPAN::Meta::Requirements 2.121; |
|
| 4076 | 4636 |
|
| 4077 | 4637 |
|
| 4078 | 4638 |
sub __legal_phases { qw(configure build test runtime develop) }
|
| ... | ... |
@@ -4100,7 +4660,7 @@ $fatpacked{"CPAN/Meta/Prereqs.pm"} = <<'CPAN_META_PREREQS';
|
| 4100 | 4660 |
|
| 4101 | 4661 |
next TYPE unless keys %$spec; |
| 4102 | 4662 |
|
| 4103 |
- $guts{prereqs}{$phase}{$type} = Version::Requirements->from_string_hash(
|
|
| 4663 |
+ $guts{prereqs}{$phase}{$type} = CPAN::Meta::Requirements->from_string_hash(
|
|
| 4104 | 4664 |
$spec |
| 4105 | 4665 |
); |
| 4106 | 4666 |
} |
| ... | ... |
@@ -4124,7 +4684,7 @@ $fatpacked{"CPAN/Meta/Prereqs.pm"} = <<'CPAN_META_PREREQS';
|
| 4124 | 4684 |
confess "requested requirements for unknown type: $type"; |
| 4125 | 4685 |
} |
| 4126 | 4686 |
|
| 4127 |
- my $req = ($self->{prereqs}{$phase}{$type} ||= Version::Requirements->new);
|
|
| 4687 |
+ my $req = ($self->{prereqs}{$phase}{$type} ||= CPAN::Meta::Requirements->new);
|
|
| 4128 | 4688 |
|
| 4129 | 4689 |
$req->finalize if $self->is_finalized; |
| 4130 | 4690 |
|
| ... | ... |
@@ -4143,7 +4703,7 @@ $fatpacked{"CPAN/Meta/Prereqs.pm"} = <<'CPAN_META_PREREQS';
|
| 4143 | 4703 |
|
| 4144 | 4704 |
for my $phase ($self->__legal_phases) {
|
| 4145 | 4705 |
for my $type ($self->__legal_types) {
|
| 4146 |
- my $req = Version::Requirements->new; |
|
| 4706 |
+ my $req = CPAN::Meta::Requirements->new; |
|
| 4147 | 4707 |
|
| 4148 | 4708 |
for my $prereq (@prereq_objs) {
|
| 4149 | 4709 |
my $this_req = $prereq->requirements_for($phase, $type); |
| ... | ... |
@@ -4202,6 +4762,8 @@ $fatpacked{"CPAN/Meta/Prereqs.pm"} = <<'CPAN_META_PREREQS';
|
| 4202 | 4762 |
|
| 4203 | 4763 |
1; |
| 4204 | 4764 |
|
| 4765 |
+ # ABSTRACT: a set of distribution prerequisites by phase and type |
|
| 4766 |
+ |
|
| 4205 | 4767 |
|
| 4206 | 4768 |
|
| 4207 | 4769 |
|
| ... | ... |
@@ -4211,5793 +4773,6440 @@ $fatpacked{"CPAN/Meta/Prereqs.pm"} = <<'CPAN_META_PREREQS';
|
| 4211 | 4773 |
|
| 4212 | 4774 |
CPAN_META_PREREQS |
| 4213 | 4775 |
|
| 4214 |
-$fatpacked{"CPAN/Meta/Spec.pm"} = <<'CPAN_META_SPEC';
|
|
| 4215 |
- # vi:tw=72 |
|
| 4216 |
- use 5.006; |
|
| 4776 |
+$fatpacked{"CPAN/Meta/Requirements.pm"} = <<'CPAN_META_REQUIREMENTS';
|
|
| 4217 | 4777 |
use strict; |
| 4218 | 4778 |
use warnings; |
| 4219 |
- package CPAN::Meta::Spec; |
|
| 4220 |
- BEGIN {
|
|
| 4221 |
- $CPAN::Meta::Spec::VERSION = '2.110930'; |
|
| 4222 |
- } |
|
| 4223 |
- # ABSTRACT: specification for CPAN distribution metadata |
|
| 4224 |
- 1; |
|
| 4225 |
- |
|
| 4779 |
+ package CPAN::Meta::Requirements; |
|
| 4780 |
+ our $VERSION = '2.122'; # VERSION |
|
| 4781 |
+ # ABSTRACT: a set of version requirements for a CPAN dist |
|
| 4226 | 4782 |
|
| 4227 | 4783 |
|
| 4228 |
- __END__ |
|
| 4229 |
- =pod |
|
| 4784 |
+ use Carp (); |
|
| 4785 |
+ use Scalar::Util (); |
|
| 4786 |
+ use version 0.77 (); # the ->parse method |
|
| 4230 | 4787 |
|
| 4231 |
-CPAN_META_SPEC |
|
| 4232 |
- |
|
| 4233 |
-$fatpacked{"CPAN/Meta/Validator.pm"} = <<'CPAN_META_VALIDATOR';
|
|
| 4234 |
- use 5.006; |
|
| 4235 |
- use strict; |
|
| 4236 |
- use warnings; |
|
| 4237 |
- package CPAN::Meta::Validator; |
|
| 4238 |
- BEGIN {
|
|
| 4239 |
- $CPAN::Meta::Validator::VERSION = '2.110930'; |
|
| 4240 |
- } |
|
| 4241 |
- # ABSTRACT: validate CPAN distribution metadata structures |
|
| 4242 | 4788 |
|
| 4789 |
+ my @valid_options = qw( bad_version_hook ); |
|
| 4243 | 4790 |
|
| 4244 |
- #--------------------------------------------------------------------------# |
|
| 4245 |
- # This code copied and adapted from Test::CPAN::Meta |
|
| 4246 |
- # by Barbie, <barbie@cpan.org> for Miss Barbell Productions, |
|
| 4247 |
- # L<http://www.missbarbell.co.uk> |
|
| 4248 |
- #--------------------------------------------------------------------------# |
|
| 4791 |
+ sub new {
|
|
| 4792 |
+ my ($class, $options) = @_; |
|
| 4793 |
+ $options ||= {};
|
|
| 4794 |
+ Carp::croak "Argument to $class\->new() must be a hash reference" |
|
| 4795 |
+ unless ref $options eq 'HASH'; |
|
| 4796 |
+ my %self = map {; $_ => $options->{$_}} @valid_options;
|
|
| 4249 | 4797 |
|
| 4250 |
- #--------------------------------------------------------------------------# |
|
| 4251 |
- # Specification Definitions |
|
| 4252 |
- #--------------------------------------------------------------------------# |
|
| 4798 |
+ return bless \%self => $class; |
|
| 4799 |
+ } |
|
| 4253 | 4800 |
|
| 4254 |
- my %known_specs = ( |
|
| 4255 |
- '1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html', |
|
| 4256 |
- '1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html', |
|
| 4257 |
- '1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html', |
|
| 4258 |
- '1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html', |
|
| 4259 |
- '1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html' |
|
| 4260 |
- ); |
|
| 4261 |
- my %known_urls = map {$known_specs{$_} => $_} keys %known_specs;
|
|
| 4801 |
+ sub _version_object {
|
|
| 4802 |
+ my ($self, $version) = @_; |
|
| 4262 | 4803 |
|
| 4263 |
- my $module_map1 = { 'map' => { ':key' => { name => \&module, value => \&exversion } } };
|
|
| 4804 |
+ my $vobj; |
|
| 4264 | 4805 |
|
| 4265 |
- my $module_map2 = { 'map' => { ':key' => { name => \&module, value => \&version } } };
|
|
| 4806 |
+ eval {
|
|
| 4807 |
+ $vobj = (! defined $version) ? version->parse(0) |
|
| 4808 |
+ : (! Scalar::Util::blessed($version)) ? version->parse($version) |
|
| 4809 |
+ : $version; |
|
| 4810 |
+ }; |
|
| 4266 | 4811 |
|
| 4267 |
- my $no_index_2 = {
|
|
| 4268 |
- 'map' => { file => { list => { value => \&string } },
|
|
| 4269 |
- directory => { list => { value => \&string } },
|
|
| 4270 |
- 'package' => { list => { value => \&string } },
|
|
| 4271 |
- namespace => { list => { value => \&string } },
|
|
| 4272 |
- ':key' => { name => \&custom_2, value => \&anything },
|
|
| 4812 |
+ if ( my $err = $@ ) {
|
|
| 4813 |
+ my $hook = $self->{bad_version_hook};
|
|
| 4814 |
+ $vobj = eval { $hook->($version) }
|
|
| 4815 |
+ if ref $hook eq 'CODE'; |
|
| 4816 |
+ unless (Scalar::Util::blessed($vobj) && $vobj->isa("version")) {
|
|
| 4817 |
+ $err =~ s{ at .* line \d+.*$}{};
|
|
| 4818 |
+ die "Can't convert '$version': $err"; |
|
| 4273 | 4819 |
} |
| 4274 |
- }; |
|
| 4820 |
+ } |
|
| 4275 | 4821 |
|
| 4276 |
- my $no_index_1_3 = {
|
|
| 4277 |
- 'map' => { file => { list => { value => \&string } },
|
|
| 4278 |
- directory => { list => { value => \&string } },
|
|
| 4279 |
- 'package' => { list => { value => \&string } },
|
|
| 4280 |
- namespace => { list => { value => \&string } },
|
|
| 4281 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4282 |
- } |
|
| 4283 |
- }; |
|
| 4822 |
+ # ensure no leading '.' |
|
| 4823 |
+ if ( $vobj =~ m{\A\.} ) {
|
|
| 4824 |
+ $vobj = version->parse("0$vobj");
|
|
| 4825 |
+ } |
|
| 4284 | 4826 |
|
| 4285 |
- my $no_index_1_2 = {
|
|
| 4286 |
- 'map' => { file => { list => { value => \&string } },
|
|
| 4287 |
- dir => { list => { value => \&string } },
|
|
| 4288 |
- 'package' => { list => { value => \&string } },
|
|
| 4289 |
- namespace => { list => { value => \&string } },
|
|
| 4290 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4291 |
- } |
|
| 4292 |
- }; |
|
| 4827 |
+ # ensure normal v-string form |
|
| 4828 |
+ if ( $vobj->is_qv ) {
|
|
| 4829 |
+ $vobj = version->parse($vobj->normal); |
|
| 4830 |
+ } |
|
| 4293 | 4831 |
|
| 4294 |
- my $no_index_1_1 = {
|
|
| 4295 |
- 'map' => { ':key' => { name => \&string, list => { value => \&string } },
|
|
| 4296 |
- } |
|
| 4297 |
- }; |
|
| 4832 |
+ return $vobj; |
|
| 4833 |
+ } |
|
| 4298 | 4834 |
|
| 4299 |
- my $prereq_map = {
|
|
| 4300 |
- map => {
|
|
| 4301 |
- ':key' => {
|
|
| 4302 |
- name => \&phase, |
|
| 4303 |
- 'map' => {
|
|
| 4304 |
- ':key' => {
|
|
| 4305 |
- name => \&relation, |
|
| 4306 |
- %$module_map1, |
|
| 4307 |
- }, |
|
| 4308 |
- }, |
|
| 4309 |
- } |
|
| 4310 |
- }, |
|
| 4311 |
- }; |
|
| 4312 | 4835 |
|
| 4313 |
- my %definitions = ( |
|
| 4314 |
- '2' => {
|
|
| 4315 |
- # REQUIRED |
|
| 4316 |
- 'abstract' => { mandatory => 1, value => \&string },
|
|
| 4317 |
- 'author' => { mandatory => 1, lazylist => { value => \&string } },
|
|
| 4318 |
- 'dynamic_config' => { mandatory => 1, value => \&boolean },
|
|
| 4319 |
- 'generated_by' => { mandatory => 1, value => \&string },
|
|
| 4320 |
- 'license' => { mandatory => 1, lazylist => { value => \&license } },
|
|
| 4321 |
- 'meta-spec' => {
|
|
| 4322 |
- mandatory => 1, |
|
| 4323 |
- 'map' => {
|
|
| 4324 |
- version => { mandatory => 1, value => \&version},
|
|
| 4325 |
- url => { value => \&url },
|
|
| 4326 |
- ':key' => { name => \&custom_2, value => \&anything },
|
|
| 4327 |
- } |
|
| 4328 |
- }, |
|
| 4329 |
- 'name' => { mandatory => 1, value => \&string },
|
|
| 4330 |
- 'release_status' => { mandatory => 1, value => \&release_status },
|
|
| 4331 |
- 'version' => { mandatory => 1, value => \&version },
|
|
| 4836 |
+ BEGIN {
|
|
| 4837 |
+ for my $type (qw(minimum maximum exclusion exact_version)) {
|
|
| 4838 |
+ my $method = "with_$type"; |
|
| 4839 |
+ my $to_add = $type eq 'exact_version' ? $type : "add_$type"; |
|
| 4332 | 4840 |
|
| 4333 |
- # OPTIONAL |
|
| 4334 |
- 'description' => { value => \&string },
|
|
| 4335 |
- 'keywords' => { lazylist => { value => \&string } },
|
|
| 4336 |
- 'no_index' => $no_index_2, |
|
| 4337 |
- 'optional_features' => {
|
|
| 4338 |
- 'map' => {
|
|
| 4339 |
- ':key' => {
|
|
| 4340 |
- name => \&string, |
|
| 4341 |
- 'map' => {
|
|
| 4342 |
- description => { value => \&string },
|
|
| 4343 |
- prereqs => $prereq_map, |
|
| 4344 |
- ':key' => { name => \&custom_2, value => \&anything },
|
|
| 4345 |
- } |
|
| 4346 |
- } |
|
| 4347 |
- } |
|
| 4348 |
- }, |
|
| 4349 |
- 'prereqs' => $prereq_map, |
|
| 4350 |
- 'provides' => {
|
|
| 4351 |
- 'map' => {
|
|
| 4352 |
- ':key' => {
|
|
| 4353 |
- name => \&module, |
|
| 4354 |
- 'map' => {
|
|
| 4355 |
- file => { mandatory => 1, value => \&file },
|
|
| 4356 |
- version => { value => \&version },
|
|
| 4357 |
- ':key' => { name => \&custom_2, value => \&anything },
|
|
| 4358 |
- } |
|
| 4359 |
- } |
|
| 4360 |
- } |
|
| 4361 |
- }, |
|
| 4362 |
- 'resources' => {
|
|
| 4363 |
- 'map' => {
|
|
| 4364 |
- license => { lazylist => { value => \&url } },
|
|
| 4365 |
- homepage => { value => \&url },
|
|
| 4366 |
- bugtracker => {
|
|
| 4367 |
- 'map' => {
|
|
| 4368 |
- web => { value => \&url },
|
|
| 4369 |
- mailto => { value => \&string},
|
|
| 4370 |
- ':key' => { name => \&custom_2, value => \&anything },
|
|
| 4371 |
- } |
|
| 4372 |
- }, |
|
| 4373 |
- repository => {
|
|
| 4374 |
- 'map' => {
|
|
| 4375 |
- web => { value => \&url },
|
|
| 4376 |
- url => { value => \&url },
|
|
| 4377 |
- type => { value => \&string },
|
|
| 4378 |
- ':key' => { name => \&custom_2, value => \&anything },
|
|
| 4379 |
- } |
|
| 4380 |
- }, |
|
| 4381 |
- ':key' => { value => \&string, name => \&custom_2 },
|
|
| 4382 |
- } |
|
| 4383 |
- }, |
|
| 4841 |
+ my $code = sub {
|
|
| 4842 |
+ my ($self, $name, $version) = @_; |
|
| 4384 | 4843 |
|
| 4385 |
- # CUSTOM -- additional user defined key/value pairs |
|
| 4386 |
- # note we can only validate the key name, as the structure is user defined |
|
| 4387 |
- ':key' => { name => \&custom_2, value => \&anything },
|
|
| 4388 |
- }, |
|
| 4844 |
+ $version = $self->_version_object( $version ); |
|
| 4389 | 4845 |
|
| 4390 |
- '1.4' => {
|
|
| 4391 |
- 'meta-spec' => {
|
|
| 4392 |
- mandatory => 1, |
|
| 4393 |
- 'map' => {
|
|
| 4394 |
- version => { mandatory => 1, value => \&version},
|
|
| 4395 |
- url => { mandatory => 1, value => \&urlspec },
|
|
| 4396 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4397 |
- }, |
|
| 4398 |
- }, |
|
| 4846 |
+ $self->__modify_entry_for($name, $method, $version); |
|
| 4399 | 4847 |
|
| 4400 |
- 'name' => { mandatory => 1, value => \&string },
|
|
| 4401 |
- 'version' => { mandatory => 1, value => \&version },
|
|
| 4402 |
- 'abstract' => { mandatory => 1, value => \&string },
|
|
| 4403 |
- 'author' => { mandatory => 1, list => { value => \&string } },
|
|
| 4404 |
- 'license' => { mandatory => 1, value => \&license },
|
|
| 4405 |
- 'generated_by' => { mandatory => 1, value => \&string },
|
|
| 4848 |
+ return $self; |
|
| 4849 |
+ }; |
|
| 4850 |
+ |
|
| 4851 |
+ no strict 'refs'; |
|
| 4852 |
+ *$to_add = $code; |
|
| 4853 |
+ } |
|
| 4854 |
+ } |
|
| 4406 | 4855 |
|
| 4407 |
- 'distribution_type' => { value => \&string },
|
|
| 4408 |
- 'dynamic_config' => { value => \&boolean },
|
|
| 4409 | 4856 |
|
| 4410 |
- 'requires' => $module_map1, |
|
| 4411 |
- 'recommends' => $module_map1, |
|
| 4412 |
- 'build_requires' => $module_map1, |
|
| 4413 |
- 'configure_requires' => $module_map1, |
|
| 4414 |
- 'conflicts' => $module_map2, |
|
| 4857 |
+ sub add_requirements {
|
|
| 4858 |
+ my ($self, $req) = @_; |
|
| 4415 | 4859 |
|
| 4416 |
- 'optional_features' => {
|
|
| 4417 |
- 'map' => {
|
|
| 4418 |
- ':key' => { name => \&string,
|
|
| 4419 |
- 'map' => { description => { value => \&string },
|
|
| 4420 |
- requires => $module_map1, |
|
| 4421 |
- recommends => $module_map1, |
|
| 4422 |
- build_requires => $module_map1, |
|
| 4423 |
- conflicts => $module_map2, |
|
| 4424 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4425 |
- } |
|
| 4426 |
- } |
|
| 4427 |
- } |
|
| 4428 |
- }, |
|
| 4429 |
- |
|
| 4430 |
- 'provides' => {
|
|
| 4431 |
- 'map' => {
|
|
| 4432 |
- ':key' => { name => \&module,
|
|
| 4433 |
- 'map' => {
|
|
| 4434 |
- file => { mandatory => 1, value => \&file },
|
|
| 4435 |
- version => { value => \&version },
|
|
| 4436 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4437 |
- } |
|
| 4438 |
- } |
|
| 4439 |
- } |
|
| 4440 |
- }, |
|
| 4860 |
+ for my $module ($req->required_modules) {
|
|
| 4861 |
+ my $modifiers = $req->__entry_for($module)->as_modifiers; |
|
| 4862 |
+ for my $modifier (@$modifiers) {
|
|
| 4863 |
+ my ($method, @args) = @$modifier; |
|
| 4864 |
+ $self->$method($module => @args); |
|
| 4865 |
+ }; |
|
| 4866 |
+ } |
|
| 4441 | 4867 |
|
| 4442 |
- 'no_index' => $no_index_1_3, |
|
| 4443 |
- 'private' => $no_index_1_3, |
|
| 4868 |
+ return $self; |
|
| 4869 |
+ } |
|
| 4444 | 4870 |
|
| 4445 |
- 'keywords' => { list => { value => \&string } },
|
|
| 4446 | 4871 |
|
| 4447 |
- 'resources' => {
|
|
| 4448 |
- 'map' => { license => { value => \&url },
|
|
| 4449 |
- homepage => { value => \&url },
|
|
| 4450 |
- bugtracker => { value => \&url },
|
|
| 4451 |
- repository => { value => \&url },
|
|
| 4452 |
- ':key' => { value => \&string, name => \&custom_1 },
|
|
| 4453 |
- } |
|
| 4454 |
- }, |
|
| 4872 |
+ sub accepts_module {
|
|
| 4873 |
+ my ($self, $module, $version) = @_; |
|
| 4455 | 4874 |
|
| 4456 |
- # additional user defined key/value pairs |
|
| 4457 |
- # note we can only validate the key name, as the structure is user defined |
|
| 4458 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4459 |
- }, |
|
| 4875 |
+ $version = $self->_version_object( $version ); |
|
| 4460 | 4876 |
|
| 4461 |
- '1.3' => {
|
|
| 4462 |
- 'meta-spec' => {
|
|
| 4463 |
- mandatory => 1, |
|
| 4464 |
- 'map' => {
|
|
| 4465 |
- version => { mandatory => 1, value => \&version},
|
|
| 4466 |
- url => { mandatory => 1, value => \&urlspec },
|
|
| 4467 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4468 |
- }, |
|
| 4469 |
- }, |
|
| 4877 |
+ return 1 unless my $range = $self->__entry_for($module); |
|
| 4878 |
+ return $range->_accepts($version); |
|
| 4879 |
+ } |
|
| 4470 | 4880 |
|
| 4471 |
- 'name' => { mandatory => 1, value => \&string },
|
|
| 4472 |
- 'version' => { mandatory => 1, value => \&version },
|
|
| 4473 |
- 'abstract' => { mandatory => 1, value => \&string },
|
|
| 4474 |
- 'author' => { mandatory => 1, list => { value => \&string } },
|
|
| 4475 |
- 'license' => { mandatory => 1, value => \&license },
|
|
| 4476 |
- 'generated_by' => { mandatory => 1, value => \&string },
|
|
| 4477 | 4881 |
|
| 4478 |
- 'distribution_type' => { value => \&string },
|
|
| 4479 |
- 'dynamic_config' => { value => \&boolean },
|
|
| 4882 |
+ sub clear_requirement {
|
|
| 4883 |
+ my ($self, $module) = @_; |
|
| 4480 | 4884 |
|
| 4481 |
- 'requires' => $module_map1, |
|
| 4482 |
- 'recommends' => $module_map1, |
|
| 4483 |
- 'build_requires' => $module_map1, |
|
| 4484 |
- 'conflicts' => $module_map2, |
|
| 4885 |
+ return $self unless $self->__entry_for($module); |
|
| 4485 | 4886 |
|
| 4486 |
- 'optional_features' => {
|
|
| 4487 |
- 'map' => {
|
|
| 4488 |
- ':key' => { name => \&string,
|
|
| 4489 |
- 'map' => { description => { value => \&string },
|
|
| 4490 |
- requires => $module_map1, |
|
| 4491 |
- recommends => $module_map1, |
|
| 4492 |
- build_requires => $module_map1, |
|
| 4493 |
- conflicts => $module_map2, |
|
| 4494 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4495 |
- } |
|
| 4496 |
- } |
|
| 4497 |
- } |
|
| 4498 |
- }, |
|
| 4887 |
+ Carp::confess("can't clear requirements on finalized requirements")
|
|
| 4888 |
+ if $self->is_finalized; |
|
| 4499 | 4889 |
|
| 4500 |
- 'provides' => {
|
|
| 4501 |
- 'map' => {
|
|
| 4502 |
- ':key' => { name => \&module,
|
|
| 4503 |
- 'map' => {
|
|
| 4504 |
- file => { mandatory => 1, value => \&file },
|
|
| 4505 |
- version => { value => \&version },
|
|
| 4506 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4507 |
- } |
|
| 4508 |
- } |
|
| 4509 |
- } |
|
| 4510 |
- }, |
|
| 4890 |
+ delete $self->{requirements}{ $module };
|
|
| 4511 | 4891 |
|
| 4892 |
+ return $self; |
|
| 4893 |
+ } |
|
| 4512 | 4894 |
|
| 4513 |
- 'no_index' => $no_index_1_3, |
|
| 4514 |
- 'private' => $no_index_1_3, |
|
| 4515 | 4895 |
|
| 4516 |
- 'keywords' => { list => { value => \&string } },
|
|
| 4896 |
+ sub requirements_for_module {
|
|
| 4897 |
+ my ($self, $module) = @_; |
|
| 4898 |
+ my $entry = $self->__entry_for($module); |
|
| 4899 |
+ return unless $entry; |
|
| 4900 |
+ return $entry->as_string; |
|
| 4901 |
+ } |
|
| 4517 | 4902 |
|
| 4518 |
- 'resources' => {
|
|
| 4519 |
- 'map' => { license => { value => \&url },
|
|
| 4520 |
- homepage => { value => \&url },
|
|
| 4521 |
- bugtracker => { value => \&url },
|
|
| 4522 |
- repository => { value => \&url },
|
|
| 4523 |
- ':key' => { value => \&string, name => \&custom_1 },
|
|
| 4524 |
- } |
|
| 4525 |
- }, |
|
| 4526 | 4903 |
|
| 4527 |
- # additional user defined key/value pairs |
|
| 4528 |
- # note we can only validate the key name, as the structure is user defined |
|
| 4529 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4530 |
- }, |
|
| 4904 |
+ sub required_modules { keys %{ $_[0]{requirements} } }
|
|
| 4531 | 4905 |
|
| 4532 |
- # v1.2 is misleading, it seems to assume that a number of fields where created |
|
| 4533 |
- # within v1.1, when they were created within v1.2. This may have been an |
|
| 4534 |
- # original mistake, and that a v1.1 was retro fitted into the timeline, when |
|
| 4535 |
- # v1.2 was originally slated as v1.1. But I could be wrong ;) |
|
| 4536 |
- '1.2' => {
|
|
| 4537 |
- 'meta-spec' => {
|
|
| 4538 |
- mandatory => 1, |
|
| 4539 |
- 'map' => {
|
|
| 4540 |
- version => { mandatory => 1, value => \&version},
|
|
| 4541 |
- url => { mandatory => 1, value => \&urlspec },
|
|
| 4542 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4543 |
- }, |
|
| 4544 |
- }, |
|
| 4545 | 4906 |
|
| 4907 |
+ sub clone {
|
|
| 4908 |
+ my ($self) = @_; |
|
| 4909 |
+ my $new = (ref $self)->new; |
|
| 4546 | 4910 |
|
| 4547 |
- 'name' => { mandatory => 1, value => \&string },
|
|
| 4548 |
- 'version' => { mandatory => 1, value => \&version },
|
|
| 4549 |
- 'license' => { mandatory => 1, value => \&license },
|
|
| 4550 |
- 'generated_by' => { mandatory => 1, value => \&string },
|
|
| 4551 |
- 'author' => { mandatory => 1, list => { value => \&string } },
|
|
| 4552 |
- 'abstract' => { mandatory => 1, value => \&string },
|
|
| 4911 |
+ return $new->add_requirements($self); |
|
| 4912 |
+ } |
|
| 4553 | 4913 |
|
| 4554 |
- 'distribution_type' => { value => \&string },
|
|
| 4555 |
- 'dynamic_config' => { value => \&boolean },
|
|
| 4914 |
+ sub __entry_for { $_[0]{requirements}{ $_[1] } }
|
|
| 4556 | 4915 |
|
| 4557 |
- 'keywords' => { list => { value => \&string } },
|
|
| 4916 |
+ sub __modify_entry_for {
|
|
| 4917 |
+ my ($self, $name, $method, $version) = @_; |
|
| 4558 | 4918 |
|
| 4559 |
- 'private' => $no_index_1_2, |
|
| 4560 |
- '$no_index' => $no_index_1_2, |
|
| 4919 |
+ my $fin = $self->is_finalized; |
|
| 4920 |
+ my $old = $self->__entry_for($name); |
|
| 4561 | 4921 |
|
| 4562 |
- 'requires' => $module_map1, |
|
| 4563 |
- 'recommends' => $module_map1, |
|
| 4564 |
- 'build_requires' => $module_map1, |
|
| 4565 |
- 'conflicts' => $module_map2, |
|
| 4922 |
+ Carp::confess("can't add new requirements to finalized requirements")
|
|
| 4923 |
+ if $fin and not $old; |
|
| 4566 | 4924 |
|
| 4567 |
- 'optional_features' => {
|
|
| 4568 |
- 'map' => {
|
|
| 4569 |
- ':key' => { name => \&string,
|
|
| 4570 |
- 'map' => { description => { value => \&string },
|
|
| 4571 |
- requires => $module_map1, |
|
| 4572 |
- recommends => $module_map1, |
|
| 4573 |
- build_requires => $module_map1, |
|
| 4574 |
- conflicts => $module_map2, |
|
| 4575 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4576 |
- } |
|
| 4577 |
- } |
|
| 4578 |
- } |
|
| 4579 |
- }, |
|
| 4925 |
+ my $new = ($old || 'CPAN::Meta::Requirements::_Range::Range') |
|
| 4926 |
+ ->$method($version); |
|
| 4580 | 4927 |
|
| 4581 |
- 'provides' => {
|
|
| 4582 |
- 'map' => {
|
|
| 4583 |
- ':key' => { name => \&module,
|
|
| 4584 |
- 'map' => {
|
|
| 4585 |
- file => { mandatory => 1, value => \&file },
|
|
| 4586 |
- version => { value => \&version },
|
|
| 4587 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4588 |
- } |
|
| 4589 |
- } |
|
| 4590 |
- } |
|
| 4591 |
- }, |
|
| 4928 |
+ Carp::confess("can't modify finalized requirements")
|
|
| 4929 |
+ if $fin and $old->as_string ne $new->as_string; |
|
| 4592 | 4930 |
|
| 4593 |
- 'resources' => {
|
|
| 4594 |
- 'map' => { license => { value => \&url },
|
|
| 4595 |
- homepage => { value => \&url },
|
|
| 4596 |
- bugtracker => { value => \&url },
|
|
| 4597 |
- repository => { value => \&url },
|
|
| 4598 |
- ':key' => { value => \&string, name => \&custom_1 },
|
|
| 4599 |
- } |
|
| 4600 |
- }, |
|
| 4931 |
+ $self->{requirements}{ $name } = $new;
|
|
| 4932 |
+ } |
|
| 4601 | 4933 |
|
| 4602 |
- # additional user defined key/value pairs |
|
| 4603 |
- # note we can only validate the key name, as the structure is user defined |
|
| 4604 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4605 |
- }, |
|
| 4606 | 4934 |
|
| 4607 |
- # note that the 1.1 spec only specifies 'version' as mandatory |
|
| 4608 |
- '1.1' => {
|
|
| 4609 |
- 'name' => { value => \&string },
|
|
| 4610 |
- 'version' => { mandatory => 1, value => \&version },
|
|
| 4611 |
- 'license' => { value => \&license },
|
|
| 4612 |
- 'generated_by' => { value => \&string },
|
|
| 4935 |
+ sub is_simple {
|
|
| 4936 |
+ my ($self) = @_; |
|
| 4937 |
+ for my $module ($self->required_modules) {
|
|
| 4938 |
+ # XXX: This is a complete hack, but also entirely correct. |
|
| 4939 |
+ return if $self->__entry_for($module)->as_string =~ /\s/; |
|
| 4940 |
+ } |
|
| 4613 | 4941 |
|
| 4614 |
- 'license_uri' => { value => \&url },
|
|
| 4615 |
- 'distribution_type' => { value => \&string },
|
|
| 4616 |
- 'dynamic_config' => { value => \&boolean },
|
|
| 4942 |
+ return 1; |
|
| 4943 |
+ } |
|
| 4617 | 4944 |
|
| 4618 |
- 'private' => $no_index_1_1, |
|
| 4619 | 4945 |
|
| 4620 |
- 'requires' => $module_map1, |
|
| 4621 |
- 'recommends' => $module_map1, |
|
| 4622 |
- 'build_requires' => $module_map1, |
|
| 4623 |
- 'conflicts' => $module_map2, |
|
| 4946 |
+ sub is_finalized { $_[0]{finalized} }
|
|
| 4624 | 4947 |
|
| 4625 |
- # additional user defined key/value pairs |
|
| 4626 |
- # note we can only validate the key name, as the structure is user defined |
|
| 4627 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4628 |
- }, |
|
| 4629 | 4948 |
|
| 4630 |
- # note that the 1.0 spec doesn't specify optional or mandatory fields |
|
| 4631 |
- # but we will treat version as mandatory since otherwise META 1.0 is |
|
| 4632 |
- # completely arbitrary and pointless |
|
| 4633 |
- '1.0' => {
|
|
| 4634 |
- 'name' => { value => \&string },
|
|
| 4635 |
- 'version' => { mandatory => 1, value => \&version },
|
|
| 4636 |
- 'license' => { value => \&license },
|
|
| 4637 |
- 'generated_by' => { value => \&string },
|
|
| 4949 |
+ sub finalize { $_[0]{finalized} = 1 }
|
|
| 4638 | 4950 |
|
| 4639 |
- 'license_uri' => { value => \&url },
|
|
| 4640 |
- 'distribution_type' => { value => \&string },
|
|
| 4641 |
- 'dynamic_config' => { value => \&boolean },
|
|
| 4642 | 4951 |
|
| 4643 |
- 'requires' => $module_map1, |
|
| 4644 |
- 'recommends' => $module_map1, |
|
| 4645 |
- 'build_requires' => $module_map1, |
|
| 4646 |
- 'conflicts' => $module_map2, |
|
| 4952 |
+ sub as_string_hash {
|
|
| 4953 |
+ my ($self) = @_; |
|
| 4647 | 4954 |
|
| 4648 |
- # additional user defined key/value pairs |
|
| 4649 |
- # note we can only validate the key name, as the structure is user defined |
|
| 4650 |
- ':key' => { name => \&string, value => \&anything },
|
|
| 4651 |
- }, |
|
| 4652 |
- ); |
|
| 4955 |
+ my %hash = map {; $_ => $self->{requirements}{$_}->as_string }
|
|
| 4956 |
+ $self->required_modules; |
|
| 4653 | 4957 |
|
| 4654 |
- #--------------------------------------------------------------------------# |
|
| 4655 |
- # Code |
|
| 4656 |
- #--------------------------------------------------------------------------# |
|
| 4958 |
+ return \%hash; |
|
| 4959 |
+ } |
|
| 4657 | 4960 |
|
| 4658 | 4961 |
|
| 4659 |
- sub new {
|
|
| 4660 |
- my ($class,$data) = @_; |
|
| 4962 |
+ my %methods_for_op = ( |
|
| 4963 |
+ '==' => [ qw(exact_version) ], |
|
| 4964 |
+ '!=' => [ qw(add_exclusion) ], |
|
| 4965 |
+ '>=' => [ qw(add_minimum) ], |
|
| 4966 |
+ '<=' => [ qw(add_maximum) ], |
|
| 4967 |
+ '>' => [ qw(add_minimum add_exclusion) ], |
|
| 4968 |
+ '<' => [ qw(add_maximum add_exclusion) ], |
|
| 4969 |
+ ); |
|
| 4661 | 4970 |
|
| 4662 |
- # create an attributes hash |
|
| 4663 |
- my $self = {
|
|
| 4664 |
- 'data' => $data, |
|
| 4665 |
- 'spec' => $data->{'meta-spec'}{'version'} || "1.0",
|
|
| 4666 |
- 'errors' => undef, |
|
| 4667 |
- }; |
|
| 4971 |
+ sub add_string_requirement {
|
|
| 4972 |
+ my ($self, $module, $req) = @_; |
|
| 4668 | 4973 |
|
| 4669 |
- # create the object |
|
| 4670 |
- return bless $self, $class; |
|
| 4671 |
- } |
|
| 4974 |
+ Carp::confess("No requirement string provided for $module")
|
|
| 4975 |
+ unless defined $req && length $req; |
|
| 4672 | 4976 |
|
| 4977 |
+ my @parts = split qr{\s*,\s*}, $req;
|
|
| 4673 | 4978 |
|
| 4674 |
- sub is_valid {
|
|
| 4675 |
- my $self = shift; |
|
| 4676 |
- my $data = $self->{data};
|
|
| 4677 |
- my $spec_version = $self->{spec};
|
|
| 4678 |
- $self->check_map($definitions{$spec_version},$data);
|
|
| 4679 |
- return ! $self->errors; |
|
| 4680 |
- } |
|
| 4681 | 4979 |
|
| 4980 |
+ for my $part (@parts) {
|
|
| 4981 |
+ my ($op, $ver) = $part =~ m{\A\s*(==|>=|>|<=|<|!=)\s*(.*)\z};
|
|
| 4682 | 4982 |
|
| 4683 |
- sub errors {
|
|
| 4684 |
- my $self = shift; |
|
| 4685 |
- return () unless(defined $self->{errors});
|
|
| 4686 |
- return @{$self->{errors}};
|
|
| 4983 |
+ if (! defined $op) {
|
|
| 4984 |
+ $self->add_minimum($module => $part); |
|
| 4985 |
+ } else {
|
|
| 4986 |
+ Carp::confess("illegal requirement string: $req")
|
|
| 4987 |
+ unless my $methods = $methods_for_op{ $op };
|
|
| 4988 |
+ |
|
| 4989 |
+ $self->$_($module => $ver) for @$methods; |
|
| 4990 |
+ } |
|
| 4991 |
+ } |
|
| 4687 | 4992 |
} |
| 4688 | 4993 |
|
| 4689 | 4994 |
|
| 4690 |
- my $spec_error = "Missing validation action in specification. " |
|
| 4691 |
- . "Must be one of 'map', 'list', 'lazylist', or 'value'"; |
|
| 4995 |
+ sub from_string_hash {
|
|
| 4996 |
+ my ($class, $hash) = @_; |
|
| 4692 | 4997 |
|
| 4693 |
- sub check_map {
|
|
| 4694 |
- my ($self,$spec,$data) = @_; |
|
| 4998 |
+ my $self = $class->new; |
|
| 4695 | 4999 |
|
| 4696 |
- if(ref($spec) ne 'HASH') {
|
|
| 4697 |
- $self->_error( "Unknown META specification, cannot validate." ); |
|
| 4698 |
- return; |
|
| 5000 |
+ for my $module (keys %$hash) {
|
|
| 5001 |
+ my $req = $hash->{$module};
|
|
| 5002 |
+ unless ( defined $req && length $req ) {
|
|
| 5003 |
+ $req = 0; |
|
| 5004 |
+ Carp::carp("Undefined requirement for $module treated as '0'");
|
|
| 4699 | 5005 |
} |
| 5006 |
+ $self->add_string_requirement($module, $req); |
|
| 5007 |
+ } |
|
| 4700 | 5008 |
|
| 4701 |
- if(ref($data) ne 'HASH') {
|
|
| 4702 |
- $self->_error( "Expected a map structure from string or file." ); |
|
| 4703 |
- return; |
|
| 4704 |
- } |
|
| 5009 |
+ return $self; |
|
| 5010 |
+ } |
|
| 4705 | 5011 |
|
| 4706 |
- for my $key (keys %$spec) {
|
|
| 4707 |
- next unless($spec->{$key}->{mandatory});
|
|
| 4708 |
- next if(defined $data->{$key});
|
|
| 4709 |
- push @{$self->{stack}}, $key;
|
|
| 4710 |
- $self->_error( "Missing mandatory field, '$key'" ); |
|
| 4711 |
- pop @{$self->{stack}};
|
|
| 4712 |
- } |
|
| 5012 |
+ ############################################################## |
|
| 4713 | 5013 |
|
| 4714 |
- for my $key (keys %$data) {
|
|
| 4715 |
- push @{$self->{stack}}, $key;
|
|
| 4716 |
- if($spec->{$key}) {
|
|
| 4717 |
- if($spec->{$key}{value}) {
|
|
| 4718 |
- $spec->{$key}{value}->($self,$key,$data->{$key});
|
|
| 4719 |
- } elsif($spec->{$key}{'map'}) {
|
|
| 4720 |
- $self->check_map($spec->{$key}{'map'},$data->{$key});
|
|
| 4721 |
- } elsif($spec->{$key}{'list'}) {
|
|
| 4722 |
- $self->check_list($spec->{$key}{'list'},$data->{$key});
|
|
| 4723 |
- } elsif($spec->{$key}{'lazylist'}) {
|
|
| 4724 |
- $self->check_lazylist($spec->{$key}{'lazylist'},$data->{$key});
|
|
| 4725 |
- } else {
|
|
| 4726 |
- $self->_error( "$spec_error for '$key'" ); |
|
| 4727 |
- } |
|
| 5014 |
+ {
|
|
| 5015 |
+ package |
|
| 5016 |
+ CPAN::Meta::Requirements::_Range::Exact; |
|
| 5017 |
+ sub _new { bless { version => $_[1] } => $_[0] }
|
|
| 4728 | 5018 |
|
| 4729 |
- } elsif ($spec->{':key'}) {
|
|
| 4730 |
- $spec->{':key'}{name}->($self,$key,$key);
|
|
| 4731 |
- if($spec->{':key'}{value}) {
|
|
| 4732 |
- $spec->{':key'}{value}->($self,$key,$data->{$key});
|
|
| 4733 |
- } elsif($spec->{':key'}{'map'}) {
|
|
| 4734 |
- $self->check_map($spec->{':key'}{'map'},$data->{$key});
|
|
| 4735 |
- } elsif($spec->{':key'}{'list'}) {
|
|
| 4736 |
- $self->check_list($spec->{':key'}{'list'},$data->{$key});
|
|
| 4737 |
- } elsif($spec->{':key'}{'lazylist'}) {
|
|
| 4738 |
- $self->check_lazylist($spec->{':key'}{'lazylist'},$data->{$key});
|
|
| 4739 |
- } else {
|
|
| 4740 |
- $self->_error( "$spec_error for ':key'" ); |
|
| 4741 |
- } |
|
| 5019 |
+ sub _accepts { return $_[0]{version} == $_[1] }
|
|
| 4742 | 5020 |
|
| 5021 |
+ sub as_string { return "== $_[0]{version}" }
|
|
| 4743 | 5022 |
|
| 4744 |
- } else {
|
|
| 4745 |
- $self->_error( "Unknown key, '$key', found in map structure" ); |
|
| 4746 |
- } |
|
| 4747 |
- pop @{$self->{stack}};
|
|
| 4748 |
- } |
|
| 4749 |
- } |
|
| 5023 |
+ sub as_modifiers { return [ [ exact_version => $_[0]{version} ] ] }
|
|
| 4750 | 5024 |
|
| 4751 |
- # if it's a string, make it into a list and check the list |
|
| 4752 |
- sub check_lazylist {
|
|
| 4753 |
- my ($self,$spec,$data) = @_; |
|
| 5025 |
+ sub _clone {
|
|
| 5026 |
+ (ref $_[0])->_new( version->new( $_[0]{version} ) )
|
|
| 5027 |
+ } |
|
| 4754 | 5028 |
|
| 4755 |
- if ( defined $data && ! ref($data) ) {
|
|
| 4756 |
- $data = [ $data ]; |
|
| 4757 |
- } |
|
| 5029 |
+ sub with_exact_version {
|
|
| 5030 |
+ my ($self, $version) = @_; |
|
| 4758 | 5031 |
|
| 4759 |
- $self->check_list($spec,$data); |
|
| 4760 |
- } |
|
| 5032 |
+ return $self->_clone if $self->_accepts($version); |
|
| 4761 | 5033 |
|
| 4762 |
- sub check_list {
|
|
| 4763 |
- my ($self,$spec,$data) = @_; |
|
| 5034 |
+ Carp::confess("illegal requirements: unequal exact version specified");
|
|
| 5035 |
+ } |
|
| 4764 | 5036 |
|
| 4765 |
- if(ref($data) ne 'ARRAY') {
|
|
| 4766 |
- $self->_error( "Expected a list structure" ); |
|
| 4767 |
- return; |
|
| 4768 |
- } |
|
| 5037 |
+ sub with_minimum {
|
|
| 5038 |
+ my ($self, $minimum) = @_; |
|
| 5039 |
+ return $self->_clone if $self->{version} >= $minimum;
|
|
| 5040 |
+ Carp::confess("illegal requirements: minimum above exact specification");
|
|
| 5041 |
+ } |
|
| 4769 | 5042 |
|
| 4770 |
- if(defined $spec->{mandatory}) {
|
|
| 4771 |
- if(!defined $data->[0]) {
|
|
| 4772 |
- $self->_error( "Missing entries from mandatory list" ); |
|
| 4773 |
- } |
|
| 4774 |
- } |
|
| 5043 |
+ sub with_maximum {
|
|
| 5044 |
+ my ($self, $maximum) = @_; |
|
| 5045 |
+ return $self->_clone if $self->{version} <= $maximum;
|
|
| 5046 |
+ Carp::confess("illegal requirements: maximum below exact specification");
|
|
| 5047 |
+ } |
|
| 4775 | 5048 |
|
| 4776 |
- for my $value (@$data) {
|
|
| 4777 |
- push @{$self->{stack}}, $value || "<undef>";
|
|
| 4778 |
- if(defined $spec->{value}) {
|
|
| 4779 |
- $spec->{value}->($self,'list',$value);
|
|
| 4780 |
- } elsif(defined $spec->{'map'}) {
|
|
| 4781 |
- $self->check_map($spec->{'map'},$value);
|
|
| 4782 |
- } elsif(defined $spec->{'list'}) {
|
|
| 4783 |
- $self->check_list($spec->{'list'},$value);
|
|
| 4784 |
- } elsif(defined $spec->{'lazylist'}) {
|
|
| 4785 |
- $self->check_lazylist($spec->{'lazylist'},$value);
|
|
| 4786 |
- } elsif ($spec->{':key'}) {
|
|
| 4787 |
- $self->check_map($spec,$value); |
|
| 4788 |
- } else {
|
|
| 4789 |
- $self->_error( "$spec_error associated with '$self->{stack}[-2]'" );
|
|
| 4790 |
- } |
|
| 4791 |
- pop @{$self->{stack}};
|
|
| 4792 |
- } |
|
| 5049 |
+ sub with_exclusion {
|
|
| 5050 |
+ my ($self, $exclusion) = @_; |
|
| 5051 |
+ return $self->_clone unless $exclusion == $self->{version};
|
|
| 5052 |
+ Carp::confess("illegal requirements: excluded exact specification");
|
|
| 5053 |
+ } |
|
| 4793 | 5054 |
} |
| 4794 | 5055 |
|
| 5056 |
+ ############################################################## |
|
| 4795 | 5057 |
|
| 4796 |
- sub header {
|
|
| 4797 |
- my ($self,$key,$value) = @_; |
|
| 4798 |
- if(defined $value) {
|
|
| 4799 |
- return 1 if($value && $value =~ /^--- #YAML:1.0/); |
|
| 4800 |
- } |
|
| 4801 |
- $self->_error( "file does not have a valid YAML header." ); |
|
| 4802 |
- return 0; |
|
| 4803 |
- } |
|
| 5058 |
+ {
|
|
| 5059 |
+ package |
|
| 5060 |
+ CPAN::Meta::Requirements::_Range::Range; |
|
| 4804 | 5061 |
|
| 4805 |
- sub release_status {
|
|
| 4806 |
- my ($self,$key,$value) = @_; |
|
| 4807 |
- if(defined $value) {
|
|
| 4808 |
- my $version = $self->{data}{version} || '';
|
|
| 4809 |
- if ( $version =~ /_/ ) {
|
|
| 4810 |
- return 1 if ( $value =~ /\A(?:testing|unstable)\z/ ); |
|
| 4811 |
- $self->_error( "'$value' for '$key' is invalid for version '$version'" ); |
|
| 4812 |
- } |
|
| 4813 |
- else {
|
|
| 4814 |
- return 1 if ( $value =~ /\A(?:stable|testing|unstable)\z/ ); |
|
| 4815 |
- $self->_error( "'$value' for '$key' is invalid" ); |
|
| 4816 |
- } |
|
| 5062 |
+ sub _self { ref($_[0]) ? $_[0] : (bless { } => $_[0]) }
|
|
| 5063 |
+ |
|
| 5064 |
+ sub _clone {
|
|
| 5065 |
+ return (bless { } => $_[0]) unless ref $_[0];
|
|
| 5066 |
+ |
|
| 5067 |
+ my ($s) = @_; |
|
| 5068 |
+ my %guts = ( |
|
| 5069 |
+ (exists $s->{minimum} ? (minimum => version->new($s->{minimum})) : ()),
|
|
| 5070 |
+ (exists $s->{maximum} ? (maximum => version->new($s->{maximum})) : ()),
|
|
| 5071 |
+ |
|
| 5072 |
+ (exists $s->{exclusions}
|
|
| 5073 |
+ ? (exclusions => [ map { version->new($_) } @{ $s->{exclusions} } ])
|
|
| 5074 |
+ : ()), |
|
| 5075 |
+ ); |
|
| 5076 |
+ |
|
| 5077 |
+ bless \%guts => ref($s); |
|
| 4817 | 5078 |
} |
| 4818 |
- else {
|
|
| 4819 |
- $self->_error( "'$key' is not defined" ); |
|
| 5079 |
+ |
|
| 5080 |
+ sub as_modifiers {
|
|
| 5081 |
+ my ($self) = @_; |
|
| 5082 |
+ my @mods; |
|
| 5083 |
+ push @mods, [ add_minimum => $self->{minimum} ] if exists $self->{minimum};
|
|
| 5084 |
+ push @mods, [ add_maximum => $self->{maximum} ] if exists $self->{maximum};
|
|
| 5085 |
+ push @mods, map {; [ add_exclusion => $_ ] } @{$self->{exclusions} || []};
|
|
| 5086 |
+ return \@mods; |
|
| 4820 | 5087 |
} |
| 4821 |
- return 0; |
|
| 4822 |
- } |
|
| 4823 | 5088 |
|
| 4824 |
- # _uri_split taken from URI::Split by Gisle Aas, Copyright 2003 |
|
| 4825 |
- sub _uri_split {
|
|
| 4826 |
- return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,; |
|
| 4827 |
- } |
|
| 5089 |
+ sub as_string {
|
|
| 5090 |
+ my ($self) = @_; |
|
| 4828 | 5091 |
|
| 4829 |
- sub url {
|
|
| 4830 |
- my ($self,$key,$value) = @_; |
|
| 4831 |
- if(defined $value) {
|
|
| 4832 |
- my ($scheme, $auth, $path, $query, $frag) = _uri_split($value); |
|
| 4833 |
- unless ( defined $scheme && length $scheme ) {
|
|
| 4834 |
- $self->_error( "'$value' for '$key' does not have a URL scheme" ); |
|
| 4835 |
- return 0; |
|
| 4836 |
- } |
|
| 4837 |
- unless ( defined $auth && length $auth ) {
|
|
| 4838 |
- $self->_error( "'$value' for '$key' does not have a URL authority" ); |
|
| 4839 |
- return 0; |
|
| 4840 |
- } |
|
| 4841 |
- return 1; |
|
| 4842 |
- } |
|
| 4843 |
- $value ||= ''; |
|
| 4844 |
- $self->_error( "'$value' for '$key' is not a valid URL." ); |
|
| 4845 |
- return 0; |
|
| 4846 |
- } |
|
| 5092 |
+ return 0 if ! keys %$self; |
|
| 4847 | 5093 |
|
| 4848 |
- sub urlspec {
|
|
| 4849 |
- my ($self,$key,$value) = @_; |
|
| 4850 |
- if(defined $value) {
|
|
| 4851 |
- return 1 if($value && $known_specs{$self->{spec}} eq $value);
|
|
| 4852 |
- if($value && $known_urls{$value}) {
|
|
| 4853 |
- $self->_error( 'META specification URL does not match version' ); |
|
| 4854 |
- return 0; |
|
| 4855 |
- } |
|
| 4856 |
- } |
|
| 4857 |
- $self->_error( 'Unknown META specification' ); |
|
| 4858 |
- return 0; |
|
| 4859 |
- } |
|
| 5094 |
+ return "$self->{minimum}" if (keys %$self) == 1 and exists $self->{minimum};
|
|
| 4860 | 5095 |
|
| 4861 |
- sub anything { return 1 }
|
|
| 5096 |
+ my @exclusions = @{ $self->{exclusions} || [] };
|
|
| 4862 | 5097 |
|
| 4863 |
- sub string {
|
|
| 4864 |
- my ($self,$key,$value) = @_; |
|
| 4865 |
- if(defined $value) {
|
|
| 4866 |
- return 1 if($value || $value =~ /^0$/); |
|
| 4867 |
- } |
|
| 4868 |
- $self->_error( "value is an undefined string" ); |
|
| 4869 |
- return 0; |
|
| 4870 |
- } |
|
| 5098 |
+ my @parts; |
|
| 4871 | 5099 |
|
| 4872 |
- sub string_or_undef {
|
|
| 4873 |
- my ($self,$key,$value) = @_; |
|
| 4874 |
- return 1 unless(defined $value); |
|
| 4875 |
- return 1 if($value || $value =~ /^0$/); |
|
| 4876 |
- $self->_error( "No string defined for '$key'" ); |
|
| 4877 |
- return 0; |
|
| 4878 |
- } |
|
| 5100 |
+ for my $pair ( |
|
| 5101 |
+ [ qw( >= > minimum ) ], |
|
| 5102 |
+ [ qw( <= < maximum ) ], |
|
| 5103 |
+ ) {
|
|
| 5104 |
+ my ($op, $e_op, $k) = @$pair; |
|
| 5105 |
+ if (exists $self->{$k}) {
|
|
| 5106 |
+ my @new_exclusions = grep { $_ != $self->{ $k } } @exclusions;
|
|
| 5107 |
+ if (@new_exclusions == @exclusions) {
|
|
| 5108 |
+ push @parts, "$op $self->{ $k }";
|
|
| 5109 |
+ } else {
|
|
| 5110 |
+ push @parts, "$e_op $self->{ $k }";
|
|
| 5111 |
+ @exclusions = @new_exclusions; |
|
| 5112 |
+ } |
|
| 5113 |
+ } |
|
| 5114 |
+ } |
|
| 4879 | 5115 |
|
| 4880 |
- sub file {
|
|
| 4881 |
- my ($self,$key,$value) = @_; |
|
| 4882 |
- return 1 if(defined $value); |
|
| 4883 |
- $self->_error( "No file defined for '$key'" ); |
|
| 4884 |
- return 0; |
|
| 4885 |
- } |
|
| 5116 |
+ push @parts, map {; "!= $_" } @exclusions;
|
|
| 4886 | 5117 |
|
| 4887 |
- sub exversion {
|
|
| 4888 |
- my ($self,$key,$value) = @_; |
|
| 4889 |
- if(defined $value && ($value || $value =~ /0/)) {
|
|
| 4890 |
- my $pass = 1; |
|
| 4891 |
- for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
|
|
| 4892 |
- return $pass; |
|
| 4893 |
- } |
|
| 4894 |
- $value = '<undef>' unless(defined $value); |
|
| 4895 |
- $self->_error( "'$value' for '$key' is not a valid version." ); |
|
| 4896 |
- return 0; |
|
| 4897 |
- } |
|
| 5118 |
+ return join q{, }, @parts;
|
|
| 5119 |
+ } |
|
| 4898 | 5120 |
|
| 4899 |
- sub version {
|
|
| 4900 |
- my ($self,$key,$value) = @_; |
|
| 4901 |
- if(defined $value) {
|
|
| 4902 |
- return 0 unless($value || $value =~ /0/); |
|
| 4903 |
- return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/); |
|
| 4904 |
- } else {
|
|
| 4905 |
- $value = '<undef>'; |
|
| 4906 |
- } |
|
| 4907 |
- $self->_error( "'$value' for '$key' is not a valid version." ); |
|
| 4908 |
- return 0; |
|
| 4909 |
- } |
|
| 5121 |
+ sub with_exact_version {
|
|
| 5122 |
+ my ($self, $version) = @_; |
|
| 5123 |
+ $self = $self->_clone; |
|
| 4910 | 5124 |
|
| 4911 |
- sub boolean {
|
|
| 4912 |
- my ($self,$key,$value) = @_; |
|
| 4913 |
- if(defined $value) {
|
|
| 4914 |
- return 1 if($value =~ /^(0|1|true|false)$/); |
|
| 4915 |
- } else {
|
|
| 4916 |
- $value = '<undef>'; |
|
| 4917 |
- } |
|
| 4918 |
- $self->_error( "'$value' for '$key' is not a boolean value." ); |
|
| 4919 |
- return 0; |
|
| 4920 |
- } |
|
| 5125 |
+ Carp::confess("illegal requirements: exact specification outside of range")
|
|
| 5126 |
+ unless $self->_accepts($version); |
|
| 4921 | 5127 |
|
| 4922 |
- my %v1_licenses = ( |
|
| 4923 |
- 'perl' => 'http://dev.perl.org/licenses/', |
|
| 4924 |
- 'gpl' => 'http://www.opensource.org/licenses/gpl-license.php', |
|
| 4925 |
- 'apache' => 'http://apache.org/licenses/LICENSE-2.0', |
|
| 4926 |
- 'artistic' => 'http://opensource.org/licenses/artistic-license.php', |
|
| 4927 |
- 'artistic_2' => 'http://opensource.org/licenses/artistic-license-2.0.php', |
|
| 4928 |
- 'lgpl' => 'http://www.opensource.org/licenses/lgpl-license.phpt', |
|
| 4929 |
- 'bsd' => 'http://www.opensource.org/licenses/bsd-license.php', |
|
| 4930 |
- 'gpl' => 'http://www.opensource.org/licenses/gpl-license.php', |
|
| 4931 |
- 'mit' => 'http://opensource.org/licenses/mit-license.php', |
|
| 4932 |
- 'mozilla' => 'http://opensource.org/licenses/mozilla1.1.php', |
|
| 4933 |
- 'open_source' => undef, |
|
| 4934 |
- 'unrestricted' => undef, |
|
| 4935 |
- 'restrictive' => undef, |
|
| 4936 |
- 'unknown' => undef, |
|
| 4937 |
- ); |
|
| 5128 |
+ return CPAN::Meta::Requirements::_Range::Exact->_new($version); |
|
| 5129 |
+ } |
|
| 4938 | 5130 |
|
| 4939 |
- my %v2_licenses = map { $_ => 1 } qw(
|
|
| 4940 |
- agpl_3 |
|
| 4941 |
- apache_1_1 |
|
| 4942 |
- apache_2_0 |
|
| 4943 |
- artistic_1 |
|
| 4944 |
- artistic_2 |
|
| 4945 |
- bsd |
|
| 4946 |
- freebsd |
|
| 4947 |
- gfdl_1_2 |
|
| 4948 |
- gfdl_1_3 |
|
| 4949 |
- gpl_1 |
|
| 4950 |
- gpl_2 |
|
| 4951 |
- gpl_3 |
|
| 4952 |
- lgpl_2_1 |
|
| 4953 |
- lgpl_3_0 |
|
| 4954 |
- mit |
|
| 4955 |
- mozilla_1_0 |
|
| 4956 |
- mozilla_1_1 |
|
| 4957 |
- openssl |
|
| 4958 |
- perl_5 |
|
| 4959 |
- qpl_1_0 |
|
| 4960 |
- ssleay |
|
| 4961 |
- sun |
|
| 4962 |
- zlib |
|
| 4963 |
- open_source |
|
| 4964 |
- restricted |
|
| 4965 |
- unrestricted |
|
| 4966 |
- unknown |
|
| 4967 |
- ); |
|
| 5131 |
+ sub _simplify {
|
|
| 5132 |
+ my ($self) = @_; |
|
| 4968 | 5133 |
|
| 4969 |
- sub license {
|
|
| 4970 |
- my ($self,$key,$value) = @_; |
|
| 4971 |
- my $licenses = $self->{spec} < 2 ? \%v1_licenses : \%v2_licenses;
|
|
| 4972 |
- if(defined $value) {
|
|
| 4973 |
- return 1 if($value && exists $licenses->{$value});
|
|
| 4974 |
- } else {
|
|
| 4975 |
- $value = '<undef>'; |
|
| 4976 |
- } |
|
| 4977 |
- $self->_error( "License '$value' is invalid" ); |
|
| 4978 |
- return 0; |
|
| 4979 |
- } |
|
| 5134 |
+ if (defined $self->{minimum} and defined $self->{maximum}) {
|
|
| 5135 |
+ if ($self->{minimum} == $self->{maximum}) {
|
|
| 5136 |
+ Carp::confess("illegal requirements: excluded all values")
|
|
| 5137 |
+ if grep { $_ == $self->{minimum} } @{ $self->{exclusions} || [] };
|
|
| 4980 | 5138 |
|
| 4981 |
- sub custom_1 {
|
|
| 4982 |
- my ($self,$key) = @_; |
|
| 4983 |
- if(defined $key) {
|
|
| 4984 |
- # a valid user defined key should be alphabetic |
|
| 4985 |
- # and contain at least one capital case letter. |
|
| 4986 |
- return 1 if($key && $key =~ /^[_a-z]+$/i && $key =~ /[A-Z]/); |
|
| 4987 |
- } else {
|
|
| 4988 |
- $key = '<undef>'; |
|
| 4989 |
- } |
|
| 4990 |
- $self->_error( "Custom resource '$key' must be in CamelCase." ); |
|
| 4991 |
- return 0; |
|
| 4992 |
- } |
|
| 5139 |
+ return CPAN::Meta::Requirements::_Range::Exact->_new($self->{minimum})
|
|
| 5140 |
+ } |
|
| 4993 | 5141 |
|
| 4994 |
- sub custom_2 {
|
|
| 4995 |
- my ($self,$key) = @_; |
|
| 4996 |
- if(defined $key) {
|
|
| 4997 |
- return 1 if($key && $key =~ /^x_/i); # user defined |
|
| 4998 |
- } else {
|
|
| 4999 |
- $key = '<undef>'; |
|
| 5142 |
+ Carp::confess("illegal requirements: minimum exceeds maximum")
|
|
| 5143 |
+ if $self->{minimum} > $self->{maximum};
|
|
| 5000 | 5144 |
} |
| 5001 |
- $self->_error( "Custom key '$key' must begin with 'x_' or 'X_'." ); |
|
| 5002 |
- return 0; |
|
| 5003 |
- } |
|
| 5004 | 5145 |
|
| 5005 |
- sub identifier {
|
|
| 5006 |
- my ($self,$key) = @_; |
|
| 5007 |
- if(defined $key) {
|
|
| 5008 |
- return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined |
|
| 5009 |
- } else {
|
|
| 5010 |
- $key = '<undef>'; |
|
| 5146 |
+ # eliminate irrelevant exclusions |
|
| 5147 |
+ if ($self->{exclusions}) {
|
|
| 5148 |
+ my %seen; |
|
| 5149 |
+ @{ $self->{exclusions} } = grep {
|
|
| 5150 |
+ (! defined $self->{minimum} or $_ >= $self->{minimum})
|
|
| 5151 |
+ and |
|
| 5152 |
+ (! defined $self->{maximum} or $_ <= $self->{maximum})
|
|
| 5153 |
+ and |
|
| 5154 |
+ ! $seen{$_}++
|
|
| 5155 |
+ } @{ $self->{exclusions} };
|
|
| 5011 | 5156 |
} |
| 5012 |
- $self->_error( "Key '$key' is not a legal identifier." ); |
|
| 5013 |
- return 0; |
|
| 5014 |
- } |
|
| 5015 | 5157 |
|
| 5016 |
- sub module {
|
|
| 5017 |
- my ($self,$key) = @_; |
|
| 5018 |
- if(defined $key) {
|
|
| 5019 |
- return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/); |
|
| 5020 |
- } else {
|
|
| 5021 |
- $key = '<undef>'; |
|
| 5022 |
- } |
|
| 5023 |
- $self->_error( "Key '$key' is not a legal module name." ); |
|
| 5024 |
- return 0; |
|
| 5025 |
- } |
|
| 5158 |
+ return $self; |
|
| 5159 |
+ } |
|
| 5026 | 5160 |
|
| 5027 |
- my @valid_phases = qw/ configure build test runtime develop /; |
|
| 5028 |
- sub phase {
|
|
| 5029 |
- my ($self,$key) = @_; |
|
| 5030 |
- if(defined $key) {
|
|
| 5031 |
- return 1 if( length $key && grep { $key eq $_ } @valid_phases );
|
|
| 5032 |
- return 1 if $key =~ /x_/i; |
|
| 5161 |
+ sub with_minimum {
|
|
| 5162 |
+ my ($self, $minimum) = @_; |
|
| 5163 |
+ $self = $self->_clone; |
|
| 5164 |
+ |
|
| 5165 |
+ if (defined (my $old_min = $self->{minimum})) {
|
|
| 5166 |
+ $self->{minimum} = (sort { $b cmp $a } ($minimum, $old_min))[0];
|
|
| 5033 | 5167 |
} else {
|
| 5034 |
- $key = '<undef>'; |
|
| 5168 |
+ $self->{minimum} = $minimum;
|
|
| 5035 | 5169 |
} |
| 5036 |
- $self->_error( "Key '$key' is not a legal phase." ); |
|
| 5037 |
- return 0; |
|
| 5038 |
- } |
|
| 5039 | 5170 |
|
| 5040 |
- my @valid_relations = qw/ requires recommends suggests conflicts /; |
|
| 5041 |
- sub relation {
|
|
| 5042 |
- my ($self,$key) = @_; |
|
| 5043 |
- if(defined $key) {
|
|
| 5044 |
- return 1 if( length $key && grep { $key eq $_ } @valid_relations );
|
|
| 5045 |
- return 1 if $key =~ /x_/i; |
|
| 5171 |
+ return $self->_simplify; |
|
| 5172 |
+ } |
|
| 5173 |
+ |
|
| 5174 |
+ sub with_maximum {
|
|
| 5175 |
+ my ($self, $maximum) = @_; |
|
| 5176 |
+ $self = $self->_clone; |
|
| 5177 |
+ |
|
| 5178 |
+ if (defined (my $old_max = $self->{maximum})) {
|
|
| 5179 |
+ $self->{maximum} = (sort { $a cmp $b } ($maximum, $old_max))[0];
|
|
| 5046 | 5180 |
} else {
|
| 5047 |
- $key = '<undef>'; |
|
| 5181 |
+ $self->{maximum} = $maximum;
|
|
| 5048 | 5182 |
} |
| 5049 |
- $self->_error( "Key '$key' is not a legal prereq relationship." ); |
|
| 5050 |
- return 0; |
|
| 5051 |
- } |
|
| 5052 | 5183 |
|
| 5053 |
- sub _error {
|
|
| 5054 |
- my $self = shift; |
|
| 5055 |
- my $mess = shift; |
|
| 5056 |
- |
|
| 5057 |
- $mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
|
|
| 5058 |
- $mess .= " [Validation: $self->{spec}]";
|
|
| 5184 |
+ return $self->_simplify; |
|
| 5185 |
+ } |
|
| 5059 | 5186 |
|
| 5060 |
- push @{$self->{errors}}, $mess;
|
|
| 5061 |
- } |
|
| 5187 |
+ sub with_exclusion {
|
|
| 5188 |
+ my ($self, $exclusion) = @_; |
|
| 5189 |
+ $self = $self->_clone; |
|
| 5062 | 5190 |
|
| 5063 |
- 1; |
|
| 5191 |
+ push @{ $self->{exclusions} ||= [] }, $exclusion;
|
|
| 5064 | 5192 |
|
| 5193 |
+ return $self->_simplify; |
|
| 5194 |
+ } |
|
| 5065 | 5195 |
|
| 5196 |
+ sub _accepts {
|
|
| 5197 |
+ my ($self, $version) = @_; |
|
| 5066 | 5198 |
|
| 5199 |
+ return if defined $self->{minimum} and $version < $self->{minimum};
|
|
| 5200 |
+ return if defined $self->{maximum} and $version > $self->{maximum};
|
|
| 5201 |
+ return if defined $self->{exclusions}
|
|
| 5202 |
+ and grep { $version == $_ } @{ $self->{exclusions} };
|
|
| 5067 | 5203 |
|
| 5068 |
- __END__ |
|
| 5204 |
+ return 1; |
|
| 5205 |
+ } |
|
| 5206 |
+ } |
|
| 5069 | 5207 |
|
| 5208 |
+ 1; |
|
| 5209 |
+ # vim: ts=2 sts=2 sw=2 et: |
|
| 5070 | 5210 |
|
| 5211 |
+ __END__ |
|
| 5212 |
+ =pod |
|
| 5071 | 5213 |
|
| 5072 |
-CPAN_META_VALIDATOR |
|
| 5214 |
+CPAN_META_REQUIREMENTS |
|
| 5073 | 5215 |
|
| 5074 |
-$fatpacked{"CPAN/Meta/YAML.pm"} = <<'CPAN_META_YAML';
|
|
| 5075 |
- package CPAN::Meta::YAML; |
|
| 5076 |
- BEGIN {
|
|
| 5077 |
- $CPAN::Meta::YAML::VERSION = '0.003'; |
|
| 5078 |
- } |
|
| 5079 |
- |
|
| 5216 |
+$fatpacked{"CPAN/Meta/Spec.pm"} = <<'CPAN_META_SPEC';
|
|
| 5217 |
+ # vi:tw=72 |
|
| 5218 |
+ use 5.006; |
|
| 5080 | 5219 |
use strict; |
| 5220 |
+ use warnings; |
|
| 5221 |
+ package CPAN::Meta::Spec; |
|
| 5222 |
+ our $VERSION = '2.120921'; # VERSION |
|
| 5081 | 5223 |
|
| 5082 |
- # UTF Support? |
|
| 5083 |
- sub HAVE_UTF8 () { $] >= 5.007003 }
|
|
| 5084 |
- BEGIN {
|
|
| 5085 |
- if ( HAVE_UTF8 ) {
|
|
| 5086 |
- # The string eval helps hide this from Test::MinimumVersion |
|
| 5087 |
- eval "require utf8;"; |
|
| 5088 |
- die "Failed to load UTF-8 support" if $@; |
|
| 5089 |
- } |
|
| 5090 |
- |
|
| 5091 |
- # Class structure |
|
| 5092 |
- require 5.004; |
|
| 5093 |
- require Exporter; |
|
| 5094 |
- require Carp; |
|
| 5095 |
- @CPAN::Meta::YAML::ISA = qw{ Exporter };
|
|
| 5096 |
- @CPAN::Meta::YAML::EXPORT = qw{ Load Dump };
|
|
| 5097 |
- @CPAN::Meta::YAML::EXPORT_OK = qw{ LoadFile DumpFile freeze thaw };
|
|
| 5224 |
+ 1; |
|
| 5098 | 5225 |
|
| 5099 |
- # Error storage |
|
| 5100 |
- $CPAN::Meta::YAML::errstr = ''; |
|
| 5101 |
- } |
|
| 5226 |
+ # ABSTRACT: specification for CPAN distribution metadata |
|
| 5102 | 5227 |
|
| 5103 |
- # The character class of all characters we need to escape |
|
| 5104 |
- # NOTE: Inlined, since it's only used once |
|
| 5105 |
- # my $RE_ESCAPE = '[\\x00-\\x08\\x0b-\\x0d\\x0e-\\x1f\"\n]'; |
|
| 5106 | 5228 |
|
| 5107 |
- # Printed form of the unprintable characters in the lowest range |
|
| 5108 |
- # of ASCII characters, listed by ASCII ordinal position. |
|
| 5109 |
- my @UNPRINTABLE = qw( |
|
| 5110 |
- z x01 x02 x03 x04 x05 x06 a |
|
| 5111 |
- x08 t n v f r x0e x0f |
|
| 5112 |
- x10 x11 x12 x13 x14 x15 x16 x17 |
|
| 5113 |
- x18 x19 x1a e x1c x1d x1e x1f |
|
| 5114 |
- ); |
|
| 5115 | 5229 |
|
| 5116 |
- # Printable characters for escapes |
|
| 5117 |
- my %UNESCAPES = ( |
|
| 5118 |
- z => "\x00", a => "\x07", t => "\x09", |
|
| 5119 |
- n => "\x0a", v => "\x0b", f => "\x0c", |
|
| 5120 |
- r => "\x0d", e => "\x1b", '\\' => '\\', |
|
| 5121 |
- ); |
|
| 5230 |
+ __END__ |
|
| 5231 |
+ =pod |
|
| 5122 | 5232 |
|
| 5123 |
- # Special magic boolean words |
|
| 5124 |
- my %QUOTE = map { $_ => 1 } qw{
|
|
| 5125 |
- null Null NULL |
|
| 5126 |
- y Y yes Yes YES n N no No NO |
|
| 5127 |
- true True TRUE false False FALSE |
|
| 5128 |
- on On ON off Off OFF |
|
| 5129 |
- }; |
|
| 5233 |
+CPAN_META_SPEC |
|
| 5234 |
+ |
|
| 5235 |
+$fatpacked{"CPAN/Meta/Validator.pm"} = <<'CPAN_META_VALIDATOR';
|
|
| 5236 |
+ use 5.006; |
|
| 5237 |
+ use strict; |
|
| 5238 |
+ use warnings; |
|
| 5239 |
+ package CPAN::Meta::Validator; |
|
| 5240 |
+ our $VERSION = '2.120921'; # VERSION |
|
| 5130 | 5241 |
|
| 5131 | 5242 |
|
| 5243 |
+ #--------------------------------------------------------------------------# |
|
| 5244 |
+ # This code copied and adapted from Test::CPAN::Meta |
|
| 5245 |
+ # by Barbie, <barbie@cpan.org> for Miss Barbell Productions, |
|
| 5246 |
+ # L<http://www.missbarbell.co.uk> |
|
| 5247 |
+ #--------------------------------------------------------------------------# |
|
| 5132 | 5248 |
|
| 5249 |
+ #--------------------------------------------------------------------------# |
|
| 5250 |
+ # Specification Definitions |
|
| 5251 |
+ #--------------------------------------------------------------------------# |
|
| 5133 | 5252 |
|
| 5253 |
+ my %known_specs = ( |
|
| 5254 |
+ '1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html', |
|
| 5255 |
+ '1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html', |
|
| 5256 |
+ '1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html', |
|
| 5257 |
+ '1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html', |
|
| 5258 |
+ '1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html' |
|
| 5259 |
+ ); |
|
| 5260 |
+ my %known_urls = map {$known_specs{$_} => $_} keys %known_specs;
|
|
| 5134 | 5261 |
|
| 5135 |
- ##################################################################### |
|
| 5136 |
- # Implementation |
|
| 5262 |
+ my $module_map1 = { 'map' => { ':key' => { name => \&module, value => \&exversion } } };
|
|
| 5137 | 5263 |
|
| 5138 |
- # Create an empty CPAN::Meta::YAML object |
|
| 5139 |
- sub new {
|
|
| 5140 |
- my $class = shift; |
|
| 5141 |
- bless [ @_ ], $class; |
|
| 5142 |
- } |
|
| 5264 |
+ my $module_map2 = { 'map' => { ':key' => { name => \&module, value => \&version } } };
|
|
| 5143 | 5265 |
|
| 5144 |
- # Create an object from a file |
|
| 5145 |
- sub read {
|
|
| 5146 |
- my $class = ref $_[0] ? ref shift : shift; |
|
| 5266 |
+ my $no_index_2 = {
|
|
| 5267 |
+ 'map' => { file => { list => { value => \&string } },
|
|
| 5268 |
+ directory => { list => { value => \&string } },
|
|
| 5269 |
+ 'package' => { list => { value => \&string } },
|
|
| 5270 |
+ namespace => { list => { value => \&string } },
|
|
| 5271 |
+ ':key' => { name => \&custom_2, value => \&anything },
|
|
| 5272 |
+ } |
|
| 5273 |
+ }; |
|
| 5147 | 5274 |
|
| 5148 |
- # Check the file |
|
| 5149 |
- my $file = shift or return $class->_error( 'You did not specify a file name' ); |
|
| 5150 |
- return $class->_error( "File '$file' does not exist" ) unless -e $file; |
|
| 5151 |
- return $class->_error( "'$file' is a directory, not a file" ) unless -f _; |
|
| 5152 |
- return $class->_error( "Insufficient permissions to read '$file'" ) unless -r _; |
|
| 5275 |
+ my $no_index_1_3 = {
|
|
| 5276 |
+ 'map' => { file => { list => { value => \&string } },
|
|
| 5277 |
+ directory => { list => { value => \&string } },
|
|
| 5278 |
+ 'package' => { list => { value => \&string } },
|
|
| 5279 |
+ namespace => { list => { value => \&string } },
|
|
| 5280 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5281 |
+ } |
|
| 5282 |
+ }; |
|
| 5153 | 5283 |
|
| 5154 |
- # Slurp in the file |
|
| 5155 |
- local $/ = undef; |
|
| 5156 |
- local *CFG; |
|
| 5157 |
- unless ( open(CFG, $file) ) {
|
|
| 5158 |
- return $class->_error("Failed to open file '$file': $!");
|
|
| 5159 |
- } |
|
| 5160 |
- my $contents = <CFG>; |
|
| 5161 |
- unless ( close(CFG) ) {
|
|
| 5162 |
- return $class->_error("Failed to close file '$file': $!");
|
|
| 5163 |
- } |
|
| 5284 |
+ my $no_index_1_2 = {
|
|
| 5285 |
+ 'map' => { file => { list => { value => \&string } },
|
|
| 5286 |
+ dir => { list => { value => \&string } },
|
|
| 5287 |
+ 'package' => { list => { value => \&string } },
|
|
| 5288 |
+ namespace => { list => { value => \&string } },
|
|
| 5289 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5290 |
+ } |
|
| 5291 |
+ }; |
|
| 5164 | 5292 |
|
| 5165 |
- $class->read_string( $contents ); |
|
| 5166 |
- } |
|
| 5293 |
+ my $no_index_1_1 = {
|
|
| 5294 |
+ 'map' => { ':key' => { name => \&string, list => { value => \&string } },
|
|
| 5295 |
+ } |
|
| 5296 |
+ }; |
|
| 5167 | 5297 |
|
| 5168 |
- # Create an object from a string |
|
| 5169 |
- sub read_string {
|
|
| 5170 |
- my $class = ref $_[0] ? ref shift : shift; |
|
| 5171 |
- my $self = bless [], $class; |
|
| 5172 |
- my $string = $_[0]; |
|
| 5173 |
- eval {
|
|
| 5174 |
- unless ( defined $string ) {
|
|
| 5175 |
- die \"Did not provide a string to load"; |
|
| 5176 |
- } |
|
| 5298 |
+ my $prereq_map = {
|
|
| 5299 |
+ map => {
|
|
| 5300 |
+ ':key' => {
|
|
| 5301 |
+ name => \&phase, |
|
| 5302 |
+ 'map' => {
|
|
| 5303 |
+ ':key' => {
|
|
| 5304 |
+ name => \&relation, |
|
| 5305 |
+ %$module_map1, |
|
| 5306 |
+ }, |
|
| 5307 |
+ }, |
|
| 5308 |
+ } |
|
| 5309 |
+ }, |
|
| 5310 |
+ }; |
|
| 5177 | 5311 |
|
| 5178 |
- # Byte order marks |
|
| 5179 |
- # NOTE: Keeping this here to educate maintainers |
|
| 5180 |
- # my %BOM = ( |
|
| 5181 |
- # "\357\273\277" => 'UTF-8', |
|
| 5182 |
- # "\376\377" => 'UTF-16BE', |
|
| 5183 |
- # "\377\376" => 'UTF-16LE', |
|
| 5184 |
- # "\377\376\0\0" => 'UTF-32LE' |
|
| 5185 |
- # "\0\0\376\377" => 'UTF-32BE', |
|
| 5186 |
- # ); |
|
| 5187 |
- if ( $string =~ /^(?:\376\377|\377\376|\377\376\0\0|\0\0\376\377)/ ) {
|
|
| 5188 |
- die \"Stream has a non UTF-8 BOM"; |
|
| 5189 |
- } else {
|
|
| 5190 |
- # Strip UTF-8 bom if found, we'll just ignore it |
|
| 5191 |
- $string =~ s/^\357\273\277//; |
|
| 5192 |
- } |
|
| 5312 |
+ my %definitions = ( |
|
| 5313 |
+ '2' => {
|
|
| 5314 |
+ # REQUIRED |
|
| 5315 |
+ 'abstract' => { mandatory => 1, value => \&string },
|
|
| 5316 |
+ 'author' => { mandatory => 1, lazylist => { value => \&string } },
|
|
| 5317 |
+ 'dynamic_config' => { mandatory => 1, value => \&boolean },
|
|
| 5318 |
+ 'generated_by' => { mandatory => 1, value => \&string },
|
|
| 5319 |
+ 'license' => { mandatory => 1, lazylist => { value => \&license } },
|
|
| 5320 |
+ 'meta-spec' => {
|
|
| 5321 |
+ mandatory => 1, |
|
| 5322 |
+ 'map' => {
|
|
| 5323 |
+ version => { mandatory => 1, value => \&version},
|
|
| 5324 |
+ url => { value => \&url },
|
|
| 5325 |
+ ':key' => { name => \&custom_2, value => \&anything },
|
|
| 5326 |
+ } |
|
| 5327 |
+ }, |
|
| 5328 |
+ 'name' => { mandatory => 1, value => \&string },
|
|
| 5329 |
+ 'release_status' => { mandatory => 1, value => \&release_status },
|
|
| 5330 |
+ 'version' => { mandatory => 1, value => \&version },
|
|
| 5193 | 5331 |
|
| 5194 |
- # Try to decode as utf8 |
|
| 5195 |
- utf8::decode($string) if HAVE_UTF8; |
|
| 5332 |
+ # OPTIONAL |
|
| 5333 |
+ 'description' => { value => \&string },
|
|
| 5334 |
+ 'keywords' => { lazylist => { value => \&string } },
|
|
| 5335 |
+ 'no_index' => $no_index_2, |
|
| 5336 |
+ 'optional_features' => {
|
|
| 5337 |
+ 'map' => {
|
|
| 5338 |
+ ':key' => {
|
|
| 5339 |
+ name => \&string, |
|
| 5340 |
+ 'map' => {
|
|
| 5341 |
+ description => { value => \&string },
|
|
| 5342 |
+ prereqs => $prereq_map, |
|
| 5343 |
+ ':key' => { name => \&custom_2, value => \&anything },
|
|
| 5344 |
+ } |
|
| 5345 |
+ } |
|
| 5346 |
+ } |
|
| 5347 |
+ }, |
|
| 5348 |
+ 'prereqs' => $prereq_map, |
|
| 5349 |
+ 'provides' => {
|
|
| 5350 |
+ 'map' => {
|
|
| 5351 |
+ ':key' => {
|
|
| 5352 |
+ name => \&module, |
|
| 5353 |
+ 'map' => {
|
|
| 5354 |
+ file => { mandatory => 1, value => \&file },
|
|
| 5355 |
+ version => { value => \&version },
|
|
| 5356 |
+ ':key' => { name => \&custom_2, value => \&anything },
|
|
| 5357 |
+ } |
|
| 5358 |
+ } |
|
| 5359 |
+ } |
|
| 5360 |
+ }, |
|
| 5361 |
+ 'resources' => {
|
|
| 5362 |
+ 'map' => {
|
|
| 5363 |
+ license => { lazylist => { value => \&url } },
|
|
| 5364 |
+ homepage => { value => \&url },
|
|
| 5365 |
+ bugtracker => {
|
|
| 5366 |
+ 'map' => {
|
|
| 5367 |
+ web => { value => \&url },
|
|
| 5368 |
+ mailto => { value => \&string},
|
|
| 5369 |
+ ':key' => { name => \&custom_2, value => \&anything },
|
|
| 5370 |
+ } |
|
| 5371 |
+ }, |
|
| 5372 |
+ repository => {
|
|
| 5373 |
+ 'map' => {
|
|
| 5374 |
+ web => { value => \&url },
|
|
| 5375 |
+ url => { value => \&url },
|
|
| 5376 |
+ type => { value => \&string },
|
|
| 5377 |
+ ':key' => { name => \&custom_2, value => \&anything },
|
|
| 5378 |
+ } |
|
| 5379 |
+ }, |
|
| 5380 |
+ ':key' => { value => \&string, name => \&custom_2 },
|
|
| 5381 |
+ } |
|
| 5382 |
+ }, |
|
| 5196 | 5383 |
|
| 5197 |
- # Check for some special cases |
|
| 5198 |
- return $self unless length $string; |
|
| 5199 |
- unless ( $string =~ /[\012\015]+\z/ ) {
|
|
| 5200 |
- die \"Stream does not end with newline character"; |
|
| 5201 |
- } |
|
| 5384 |
+ # CUSTOM -- additional user defined key/value pairs |
|
| 5385 |
+ # note we can only validate the key name, as the structure is user defined |
|
| 5386 |
+ ':key' => { name => \&custom_2, value => \&anything },
|
|
| 5387 |
+ }, |
|
| 5202 | 5388 |
|
| 5203 |
- # Split the file into lines |
|
| 5204 |
- my @lines = grep { ! /^\s*(?:\#.*)?\z/ }
|
|
| 5205 |
- split /(?:\015{1,2}\012|\015|\012)/, $string;
|
|
| 5206 |
- |
|
| 5207 |
- # Strip the initial YAML header |
|
| 5208 |
- @lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines; |
|
| 5209 |
- |
|
| 5210 |
- # A nibbling parser |
|
| 5211 |
- while ( @lines ) {
|
|
| 5212 |
- # Do we have a document header? |
|
| 5213 |
- if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
|
|
| 5214 |
- # Handle scalar documents |
|
| 5215 |
- shift @lines; |
|
| 5216 |
- if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {
|
|
| 5217 |
- push @$self, $self->_read_scalar( "$1", [ undef ], \@lines ); |
|
| 5218 |
- next; |
|
| 5219 |
- } |
|
| 5220 |
- } |
|
| 5389 |
+ '1.4' => {
|
|
| 5390 |
+ 'meta-spec' => {
|
|
| 5391 |
+ mandatory => 1, |
|
| 5392 |
+ 'map' => {
|
|
| 5393 |
+ version => { mandatory => 1, value => \&version},
|
|
| 5394 |
+ url => { mandatory => 1, value => \&urlspec },
|
|
| 5395 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5396 |
+ }, |
|
| 5397 |
+ }, |
|
| 5221 | 5398 |
|
| 5222 |
- if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
| 5223 |
- # A naked document |
|
| 5224 |
- push @$self, undef; |
|
| 5225 |
- while ( @lines and $lines[0] !~ /^---/ ) {
|
|
| 5226 |
- shift @lines; |
|
| 5227 |
- } |
|
| 5399 |
+ 'name' => { mandatory => 1, value => \&string },
|
|
| 5400 |
+ 'version' => { mandatory => 1, value => \&version },
|
|
| 5401 |
+ 'abstract' => { mandatory => 1, value => \&string },
|
|
| 5402 |
+ 'author' => { mandatory => 1, list => { value => \&string } },
|
|
| 5403 |
+ 'license' => { mandatory => 1, value => \&license },
|
|
| 5404 |
+ 'generated_by' => { mandatory => 1, value => \&string },
|
|
| 5228 | 5405 |
|
| 5229 |
- } elsif ( $lines[0] =~ /^\s*\-/ ) {
|
|
| 5230 |
- # An array at the root |
|
| 5231 |
- my $document = [ ]; |
|
| 5232 |
- push @$self, $document; |
|
| 5233 |
- $self->_read_array( $document, [ 0 ], \@lines ); |
|
| 5406 |
+ 'distribution_type' => { value => \&string },
|
|
| 5407 |
+ 'dynamic_config' => { value => \&boolean },
|
|
| 5234 | 5408 |
|
| 5235 |
- } elsif ( $lines[0] =~ /^(\s*)\S/ ) {
|
|
| 5236 |
- # A hash at the root |
|
| 5237 |
- my $document = { };
|
|
| 5238 |
- push @$self, $document; |
|
| 5239 |
- $self->_read_hash( $document, [ length($1) ], \@lines ); |
|
| 5409 |
+ 'requires' => $module_map1, |
|
| 5410 |
+ 'recommends' => $module_map1, |
|
| 5411 |
+ 'build_requires' => $module_map1, |
|
| 5412 |
+ 'configure_requires' => $module_map1, |
|
| 5413 |
+ 'conflicts' => $module_map2, |
|
| 5240 | 5414 |
|
| 5241 |
- } else {
|
|
| 5242 |
- die \"CPAN::Meta::YAML failed to classify the line '$lines[0]'"; |
|
| 5243 |
- } |
|
| 5244 |
- } |
|
| 5245 |
- }; |
|
| 5246 |
- if ( ref $@ eq 'SCALAR' ) {
|
|
| 5247 |
- return $self->_error(${$@});
|
|
| 5248 |
- } elsif ( $@ ) {
|
|
| 5249 |
- require Carp; |
|
| 5250 |
- Carp::croak($@); |
|
| 5251 |
- } |
|
| 5415 |
+ 'optional_features' => {
|
|
| 5416 |
+ 'map' => {
|
|
| 5417 |
+ ':key' => { name => \&string,
|
|
| 5418 |
+ 'map' => { description => { value => \&string },
|
|
| 5419 |
+ requires => $module_map1, |
|
| 5420 |
+ recommends => $module_map1, |
|
| 5421 |
+ build_requires => $module_map1, |
|
| 5422 |
+ conflicts => $module_map2, |
|
| 5423 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5424 |
+ } |
|
| 5425 |
+ } |
|
| 5426 |
+ } |
|
| 5427 |
+ }, |
|
| 5252 | 5428 |
|
| 5253 |
- return $self; |
|
| 5254 |
- } |
|
| 5429 |
+ 'provides' => {
|
|
| 5430 |
+ 'map' => {
|
|
| 5431 |
+ ':key' => { name => \&module,
|
|
| 5432 |
+ 'map' => {
|
|
| 5433 |
+ file => { mandatory => 1, value => \&file },
|
|
| 5434 |
+ version => { value => \&version },
|
|
| 5435 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5436 |
+ } |
|
| 5437 |
+ } |
|
| 5438 |
+ } |
|
| 5439 |
+ }, |
|
| 5255 | 5440 |
|
| 5256 |
- # Deparse a scalar string to the actual scalar |
|
| 5257 |
- sub _read_scalar {
|
|
| 5258 |
- my ($self, $string, $indent, $lines) = @_; |
|
| 5441 |
+ 'no_index' => $no_index_1_3, |
|
| 5442 |
+ 'private' => $no_index_1_3, |
|
| 5259 | 5443 |
|
| 5260 |
- # Trim trailing whitespace |
|
| 5261 |
- $string =~ s/\s*\z//; |
|
| 5444 |
+ 'keywords' => { list => { value => \&string } },
|
|
| 5262 | 5445 |
|
| 5263 |
- # Explitic null/undef |
|
| 5264 |
- return undef if $string eq '~'; |
|
| 5446 |
+ 'resources' => {
|
|
| 5447 |
+ 'map' => { license => { value => \&url },
|
|
| 5448 |
+ homepage => { value => \&url },
|
|
| 5449 |
+ bugtracker => { value => \&url },
|
|
| 5450 |
+ repository => { value => \&url },
|
|
| 5451 |
+ ':key' => { value => \&string, name => \&custom_1 },
|
|
| 5452 |
+ } |
|
| 5453 |
+ }, |
|
| 5265 | 5454 |
|
| 5266 |
- # Single quote |
|
| 5267 |
- if ( $string =~ /^\'(.*?)\'(?:\s+\#.*)?\z/ ) {
|
|
| 5268 |
- return '' unless defined $1; |
|
| 5269 |
- $string = $1; |
|
| 5270 |
- $string =~ s/\'\'/\'/g; |
|
| 5271 |
- return $string; |
|
| 5272 |
- } |
|
| 5455 |
+ # additional user defined key/value pairs |
|
| 5456 |
+ # note we can only validate the key name, as the structure is user defined |
|
| 5457 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5458 |
+ }, |
|
| 5273 | 5459 |
|
| 5274 |
- # Double quote. |
|
| 5275 |
- # The commented out form is simpler, but overloaded the Perl regex |
|
| 5276 |
- # engine due to recursion and backtracking problems on strings |
|
| 5277 |
- # larger than 32,000ish characters. Keep it for reference purposes. |
|
| 5278 |
- # if ( $string =~ /^\"((?:\\.|[^\"])*)\"\z/ ) {
|
|
| 5279 |
- if ( $string =~ /^\"([^\\"]*(?:\\.[^\\"]*)*)\"(?:\s+\#.*)?\z/ ) {
|
|
| 5280 |
- # Reusing the variable is a little ugly, |
|
| 5281 |
- # but avoids a new variable and a string copy. |
|
| 5282 |
- $string = $1; |
|
| 5283 |
- $string =~ s/\\"/"/g; |
|
| 5284 |
- $string =~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}/gex;
|
|
| 5285 |
- return $string; |
|
| 5286 |
- } |
|
| 5460 |
+ '1.3' => {
|
|
| 5461 |
+ 'meta-spec' => {
|
|
| 5462 |
+ mandatory => 1, |
|
| 5463 |
+ 'map' => {
|
|
| 5464 |
+ version => { mandatory => 1, value => \&version},
|
|
| 5465 |
+ url => { mandatory => 1, value => \&urlspec },
|
|
| 5466 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5467 |
+ }, |
|
| 5468 |
+ }, |
|
| 5287 | 5469 |
|
| 5288 |
- # Special cases |
|
| 5289 |
- if ( $string =~ /^[\'\"!&]/ ) {
|
|
| 5290 |
- die \"CPAN::Meta::YAML does not support a feature in line '$string'"; |
|
| 5291 |
- } |
|
| 5292 |
- return {} if $string =~ /^{}(?:\s+\#.*)?\z/;
|
|
| 5293 |
- return [] if $string =~ /^\[\](?:\s+\#.*)?\z/; |
|
| 5470 |
+ 'name' => { mandatory => 1, value => \&string },
|
|
| 5471 |
+ 'version' => { mandatory => 1, value => \&version },
|
|
| 5472 |
+ 'abstract' => { mandatory => 1, value => \&string },
|
|
| 5473 |
+ 'author' => { mandatory => 1, list => { value => \&string } },
|
|
| 5474 |
+ 'license' => { mandatory => 1, value => \&license },
|
|
| 5475 |
+ 'generated_by' => { mandatory => 1, value => \&string },
|
|
| 5294 | 5476 |
|
| 5295 |
- # Regular unquoted string |
|
| 5296 |
- if ( $string !~ /^[>|]/ ) {
|
|
| 5297 |
- if ( |
|
| 5298 |
- $string =~ /^(?:-(?:\s|$)|[\@\%\`])/ |
|
| 5299 |
- or |
|
| 5300 |
- $string =~ /:(?:\s|$)/ |
|
| 5301 |
- ) {
|
|
| 5302 |
- die \"CPAN::Meta::YAML found illegal characters in plain scalar: '$string'"; |
|
| 5303 |
- } |
|
| 5304 |
- $string =~ s/\s+#.*\z//; |
|
| 5305 |
- return $string; |
|
| 5306 |
- } |
|
| 5477 |
+ 'distribution_type' => { value => \&string },
|
|
| 5478 |
+ 'dynamic_config' => { value => \&boolean },
|
|
| 5307 | 5479 |
|
| 5308 |
- # Error |
|
| 5309 |
- die \"CPAN::Meta::YAML failed to find multi-line scalar content" unless @$lines; |
|
| 5480 |
+ 'requires' => $module_map1, |
|
| 5481 |
+ 'recommends' => $module_map1, |
|
| 5482 |
+ 'build_requires' => $module_map1, |
|
| 5483 |
+ 'conflicts' => $module_map2, |
|
| 5310 | 5484 |
|
| 5311 |
- # Check the indent depth |
|
| 5312 |
- $lines->[0] =~ /^(\s*)/; |
|
| 5313 |
- $indent->[-1] = length("$1");
|
|
| 5314 |
- if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
|
|
| 5315 |
- die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; |
|
| 5316 |
- } |
|
| 5485 |
+ 'optional_features' => {
|
|
| 5486 |
+ 'map' => {
|
|
| 5487 |
+ ':key' => { name => \&string,
|
|
| 5488 |
+ 'map' => { description => { value => \&string },
|
|
| 5489 |
+ requires => $module_map1, |
|
| 5490 |
+ recommends => $module_map1, |
|
| 5491 |
+ build_requires => $module_map1, |
|
| 5492 |
+ conflicts => $module_map2, |
|
| 5493 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5494 |
+ } |
|
| 5495 |
+ } |
|
| 5496 |
+ } |
|
| 5497 |
+ }, |
|
| 5317 | 5498 |
|
| 5318 |
- # Pull the lines |
|
| 5319 |
- my @multiline = (); |
|
| 5320 |
- while ( @$lines ) {
|
|
| 5321 |
- $lines->[0] =~ /^(\s*)/; |
|
| 5322 |
- last unless length($1) >= $indent->[-1]; |
|
| 5323 |
- push @multiline, substr(shift(@$lines), length($1)); |
|
| 5324 |
- } |
|
| 5499 |
+ 'provides' => {
|
|
| 5500 |
+ 'map' => {
|
|
| 5501 |
+ ':key' => { name => \&module,
|
|
| 5502 |
+ 'map' => {
|
|
| 5503 |
+ file => { mandatory => 1, value => \&file },
|
|
| 5504 |
+ version => { value => \&version },
|
|
| 5505 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5506 |
+ } |
|
| 5507 |
+ } |
|
| 5508 |
+ } |
|
| 5509 |
+ }, |
|
| 5325 | 5510 |
|
| 5326 |
- my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n"; |
|
| 5327 |
- my $t = (substr($string, 1, 1) eq '-') ? '' : "\n"; |
|
| 5328 |
- return join( $j, @multiline ) . $t; |
|
| 5329 |
- } |
|
| 5330 | 5511 |
|
| 5331 |
- # Parse an array |
|
| 5332 |
- sub _read_array {
|
|
| 5333 |
- my ($self, $array, $indent, $lines) = @_; |
|
| 5512 |
+ 'no_index' => $no_index_1_3, |
|
| 5513 |
+ 'private' => $no_index_1_3, |
|
| 5334 | 5514 |
|
| 5335 |
- while ( @$lines ) {
|
|
| 5336 |
- # Check for a new document |
|
| 5337 |
- if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
| 5338 |
- while ( @$lines and $lines->[0] !~ /^---/ ) {
|
|
| 5339 |
- shift @$lines; |
|
| 5340 |
- } |
|
| 5341 |
- return 1; |
|
| 5342 |
- } |
|
| 5515 |
+ 'keywords' => { list => { value => \&string } },
|
|
| 5343 | 5516 |
|
| 5344 |
- # Check the indent level |
|
| 5345 |
- $lines->[0] =~ /^(\s*)/; |
|
| 5346 |
- if ( length($1) < $indent->[-1] ) {
|
|
| 5347 |
- return 1; |
|
| 5348 |
- } elsif ( length($1) > $indent->[-1] ) {
|
|
| 5349 |
- die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; |
|
| 5350 |
- } |
|
| 5517 |
+ 'resources' => {
|
|
| 5518 |
+ 'map' => { license => { value => \&url },
|
|
| 5519 |
+ homepage => { value => \&url },
|
|
| 5520 |
+ bugtracker => { value => \&url },
|
|
| 5521 |
+ repository => { value => \&url },
|
|
| 5522 |
+ ':key' => { value => \&string, name => \&custom_1 },
|
|
| 5523 |
+ } |
|
| 5524 |
+ }, |
|
| 5351 | 5525 |
|
| 5352 |
- if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
|
|
| 5353 |
- # Inline nested hash |
|
| 5354 |
- my $indent2 = length("$1");
|
|
| 5355 |
- $lines->[0] =~ s/-/ /; |
|
| 5356 |
- push @$array, { };
|
|
| 5357 |
- $self->_read_hash( $array->[-1], [ @$indent, $indent2 ], $lines ); |
|
| 5526 |
+ # additional user defined key/value pairs |
|
| 5527 |
+ # note we can only validate the key name, as the structure is user defined |
|
| 5528 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5529 |
+ }, |
|
| 5358 | 5530 |
|
| 5359 |
- } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
|
|
| 5360 |
- # Array entry with a value |
|
| 5361 |
- shift @$lines; |
|
| 5362 |
- push @$array, $self->_read_scalar( "$2", [ @$indent, undef ], $lines ); |
|
| 5531 |
+ # v1.2 is misleading, it seems to assume that a number of fields where created |
|
| 5532 |
+ # within v1.1, when they were created within v1.2. This may have been an |
|
| 5533 |
+ # original mistake, and that a v1.1 was retro fitted into the timeline, when |
|
| 5534 |
+ # v1.2 was originally slated as v1.1. But I could be wrong ;) |
|
| 5535 |
+ '1.2' => {
|
|
| 5536 |
+ 'meta-spec' => {
|
|
| 5537 |
+ mandatory => 1, |
|
| 5538 |
+ 'map' => {
|
|
| 5539 |
+ version => { mandatory => 1, value => \&version},
|
|
| 5540 |
+ url => { mandatory => 1, value => \&urlspec },
|
|
| 5541 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5542 |
+ }, |
|
| 5543 |
+ }, |
|
| 5363 | 5544 |
|
| 5364 |
- } elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {
|
|
| 5365 |
- shift @$lines; |
|
| 5366 |
- unless ( @$lines ) {
|
|
| 5367 |
- push @$array, undef; |
|
| 5368 |
- return 1; |
|
| 5369 |
- } |
|
| 5370 |
- if ( $lines->[0] =~ /^(\s*)\-/ ) {
|
|
| 5371 |
- my $indent2 = length("$1");
|
|
| 5372 |
- if ( $indent->[-1] == $indent2 ) {
|
|
| 5373 |
- # Null array entry |
|
| 5374 |
- push @$array, undef; |
|
| 5375 |
- } else {
|
|
| 5376 |
- # Naked indenter |
|
| 5377 |
- push @$array, [ ]; |
|
| 5378 |
- $self->_read_array( $array->[-1], [ @$indent, $indent2 ], $lines ); |
|
| 5379 |
- } |
|
| 5380 | 5545 |
|
| 5381 |
- } elsif ( $lines->[0] =~ /^(\s*)\S/ ) {
|
|
| 5382 |
- push @$array, { };
|
|
| 5383 |
- $self->_read_hash( $array->[-1], [ @$indent, length("$1") ], $lines );
|
|
| 5546 |
+ 'name' => { mandatory => 1, value => \&string },
|
|
| 5547 |
+ 'version' => { mandatory => 1, value => \&version },
|
|
| 5548 |
+ 'license' => { mandatory => 1, value => \&license },
|
|
| 5549 |
+ 'generated_by' => { mandatory => 1, value => \&string },
|
|
| 5550 |
+ 'author' => { mandatory => 1, list => { value => \&string } },
|
|
| 5551 |
+ 'abstract' => { mandatory => 1, value => \&string },
|
|
| 5384 | 5552 |
|
| 5385 |
- } else {
|
|
| 5386 |
- die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; |
|
| 5387 |
- } |
|
| 5553 |
+ 'distribution_type' => { value => \&string },
|
|
| 5554 |
+ 'dynamic_config' => { value => \&boolean },
|
|
| 5388 | 5555 |
|
| 5389 |
- } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
|
|
| 5390 |
- # This is probably a structure like the following... |
|
| 5391 |
- # --- |
|
| 5392 |
- # foo: |
|
| 5393 |
- # - list |
|
| 5394 |
- # bar: value |
|
| 5395 |
- # |
|
| 5396 |
- # ... so lets return and let the hash parser handle it |
|
| 5397 |
- return 1; |
|
| 5556 |
+ 'keywords' => { list => { value => \&string } },
|
|
| 5398 | 5557 |
|
| 5399 |
- } else {
|
|
| 5400 |
- die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; |
|
| 5401 |
- } |
|
| 5402 |
- } |
|
| 5558 |
+ 'private' => $no_index_1_2, |
|
| 5559 |
+ '$no_index' => $no_index_1_2, |
|
| 5403 | 5560 |
|
| 5404 |
- return 1; |
|
| 5405 |
- } |
|
| 5561 |
+ 'requires' => $module_map1, |
|
| 5562 |
+ 'recommends' => $module_map1, |
|
| 5563 |
+ 'build_requires' => $module_map1, |
|
| 5564 |
+ 'conflicts' => $module_map2, |
|
| 5406 | 5565 |
|
| 5407 |
- # Parse an array |
|
| 5408 |
- sub _read_hash {
|
|
| 5409 |
- my ($self, $hash, $indent, $lines) = @_; |
|
| 5566 |
+ 'optional_features' => {
|
|
| 5567 |
+ 'map' => {
|
|
| 5568 |
+ ':key' => { name => \&string,
|
|
| 5569 |
+ 'map' => { description => { value => \&string },
|
|
| 5570 |
+ requires => $module_map1, |
|
| 5571 |
+ recommends => $module_map1, |
|
| 5572 |
+ build_requires => $module_map1, |
|
| 5573 |
+ conflicts => $module_map2, |
|
| 5574 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5575 |
+ } |
|
| 5576 |
+ } |
|
| 5577 |
+ } |
|
| 5578 |
+ }, |
|
| 5410 | 5579 |
|
| 5411 |
- while ( @$lines ) {
|
|
| 5412 |
- # Check for a new document |
|
| 5413 |
- if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
| 5414 |
- while ( @$lines and $lines->[0] !~ /^---/ ) {
|
|
| 5415 |
- shift @$lines; |
|
| 5416 |
- } |
|
| 5417 |
- return 1; |
|
| 5418 |
- } |
|
| 5580 |
+ 'provides' => {
|
|
| 5581 |
+ 'map' => {
|
|
| 5582 |
+ ':key' => { name => \&module,
|
|
| 5583 |
+ 'map' => {
|
|
| 5584 |
+ file => { mandatory => 1, value => \&file },
|
|
| 5585 |
+ version => { value => \&version },
|
|
| 5586 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5587 |
+ } |
|
| 5588 |
+ } |
|
| 5589 |
+ } |
|
| 5590 |
+ }, |
|
| 5419 | 5591 |
|
| 5420 |
- # Check the indent level |
|
| 5421 |
- $lines->[0] =~ /^(\s*)/; |
|
| 5422 |
- if ( length($1) < $indent->[-1] ) {
|
|
| 5423 |
- return 1; |
|
| 5424 |
- } elsif ( length($1) > $indent->[-1] ) {
|
|
| 5425 |
- die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; |
|
| 5426 |
- } |
|
| 5592 |
+ 'resources' => {
|
|
| 5593 |
+ 'map' => { license => { value => \&url },
|
|
| 5594 |
+ homepage => { value => \&url },
|
|
| 5595 |
+ bugtracker => { value => \&url },
|
|
| 5596 |
+ repository => { value => \&url },
|
|
| 5597 |
+ ':key' => { value => \&string, name => \&custom_1 },
|
|
| 5598 |
+ } |
|
| 5599 |
+ }, |
|
| 5427 | 5600 |
|
| 5428 |
- # Get the key |
|
| 5429 |
- unless ( $lines->[0] =~ s/^\s*([^\'\" ][^\n]*?)\s*:(\s+(?:\#.*)?|$)// ) {
|
|
| 5430 |
- if ( $lines->[0] =~ /^\s*[?\'\"]/ ) {
|
|
| 5431 |
- die \"CPAN::Meta::YAML does not support a feature in line '$lines->[0]'"; |
|
| 5432 |
- } |
|
| 5433 |
- die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; |
|
| 5434 |
- } |
|
| 5435 |
- my $key = $1; |
|
| 5601 |
+ # additional user defined key/value pairs |
|
| 5602 |
+ # note we can only validate the key name, as the structure is user defined |
|
| 5603 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5604 |
+ }, |
|
| 5436 | 5605 |
|
| 5437 |
- # Do we have a value? |
|
| 5438 |
- if ( length $lines->[0] ) {
|
|
| 5439 |
- # Yes |
|
| 5440 |
- $hash->{$key} = $self->_read_scalar( shift(@$lines), [ @$indent, undef ], $lines );
|
|
| 5441 |
- } else {
|
|
| 5442 |
- # An indent |
|
| 5443 |
- shift @$lines; |
|
| 5444 |
- unless ( @$lines ) {
|
|
| 5445 |
- $hash->{$key} = undef;
|
|
| 5446 |
- return 1; |
|
| 5447 |
- } |
|
| 5448 |
- if ( $lines->[0] =~ /^(\s*)-/ ) {
|
|
| 5449 |
- $hash->{$key} = [];
|
|
| 5450 |
- $self->_read_array( $hash->{$key}, [ @$indent, length($1) ], $lines );
|
|
| 5451 |
- } elsif ( $lines->[0] =~ /^(\s*)./ ) {
|
|
| 5452 |
- my $indent2 = length("$1");
|
|
| 5453 |
- if ( $indent->[-1] >= $indent2 ) {
|
|
| 5454 |
- # Null hash entry |
|
| 5455 |
- $hash->{$key} = undef;
|
|
| 5456 |
- } else {
|
|
| 5457 |
- $hash->{$key} = {};
|
|
| 5458 |
- $self->_read_hash( $hash->{$key}, [ @$indent, length($1) ], $lines );
|
|
| 5459 |
- } |
|
| 5460 |
- } |
|
| 5461 |
- } |
|
| 5462 |
- } |
|
| 5606 |
+ # note that the 1.1 spec only specifies 'version' as mandatory |
|
| 5607 |
+ '1.1' => {
|
|
| 5608 |
+ 'name' => { value => \&string },
|
|
| 5609 |
+ 'version' => { mandatory => 1, value => \&version },
|
|
| 5610 |
+ 'license' => { value => \&license },
|
|
| 5611 |
+ 'generated_by' => { value => \&string },
|
|
| 5463 | 5612 |
|
| 5464 |
- return 1; |
|
| 5465 |
- } |
|
| 5613 |
+ 'license_uri' => { value => \&url },
|
|
| 5614 |
+ 'distribution_type' => { value => \&string },
|
|
| 5615 |
+ 'dynamic_config' => { value => \&boolean },
|
|
| 5466 | 5616 |
|
| 5467 |
- # Save an object to a file |
|
| 5468 |
- sub write {
|
|
| 5469 |
- my $self = shift; |
|
| 5470 |
- my $file = shift or return $self->_error('No file name provided');
|
|
| 5617 |
+ 'private' => $no_index_1_1, |
|
| 5471 | 5618 |
|
| 5472 |
- # Write it to the file |
|
| 5473 |
- open( CFG, '>' . $file ) or return $self->_error( |
|
| 5474 |
- "Failed to open file '$file' for writing: $!" |
|
| 5475 |
- ); |
|
| 5476 |
- print CFG $self->write_string; |
|
| 5477 |
- close CFG; |
|
| 5619 |
+ 'requires' => $module_map1, |
|
| 5620 |
+ 'recommends' => $module_map1, |
|
| 5621 |
+ 'build_requires' => $module_map1, |
|
| 5622 |
+ 'conflicts' => $module_map2, |
|
| 5478 | 5623 |
|
| 5479 |
- return 1; |
|
| 5480 |
- } |
|
| 5624 |
+ # additional user defined key/value pairs |
|
| 5625 |
+ # note we can only validate the key name, as the structure is user defined |
|
| 5626 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5627 |
+ }, |
|
| 5481 | 5628 |
|
| 5482 |
- # Save an object to a string |
|
| 5483 |
- sub write_string {
|
|
| 5484 |
- my $self = shift; |
|
| 5485 |
- return '' unless @$self; |
|
| 5629 |
+ # note that the 1.0 spec doesn't specify optional or mandatory fields |
|
| 5630 |
+ # but we will treat version as mandatory since otherwise META 1.0 is |
|
| 5631 |
+ # completely arbitrary and pointless |
|
| 5632 |
+ '1.0' => {
|
|
| 5633 |
+ 'name' => { value => \&string },
|
|
| 5634 |
+ 'version' => { mandatory => 1, value => \&version },
|
|
| 5635 |
+ 'license' => { value => \&license },
|
|
| 5636 |
+ 'generated_by' => { value => \&string },
|
|
| 5486 | 5637 |
|
| 5487 |
- # Iterate over the documents |
|
| 5488 |
- my $indent = 0; |
|
| 5489 |
- my @lines = (); |
|
| 5490 |
- foreach my $cursor ( @$self ) {
|
|
| 5491 |
- push @lines, '---'; |
|
| 5638 |
+ 'license_uri' => { value => \&url },
|
|
| 5639 |
+ 'distribution_type' => { value => \&string },
|
|
| 5640 |
+ 'dynamic_config' => { value => \&boolean },
|
|
| 5492 | 5641 |
|
| 5493 |
- # An empty document |
|
| 5494 |
- if ( ! defined $cursor ) {
|
|
| 5495 |
- # Do nothing |
|
| 5642 |
+ 'requires' => $module_map1, |
|
| 5643 |
+ 'recommends' => $module_map1, |
|
| 5644 |
+ 'build_requires' => $module_map1, |
|
| 5645 |
+ 'conflicts' => $module_map2, |
|
| 5496 | 5646 |
|
| 5497 |
- # A scalar document |
|
| 5498 |
- } elsif ( ! ref $cursor ) {
|
|
| 5499 |
- $lines[-1] .= ' ' . $self->_write_scalar( $cursor, $indent ); |
|
| 5647 |
+ # additional user defined key/value pairs |
|
| 5648 |
+ # note we can only validate the key name, as the structure is user defined |
|
| 5649 |
+ ':key' => { name => \&string, value => \&anything },
|
|
| 5650 |
+ }, |
|
| 5651 |
+ ); |
|
| 5500 | 5652 |
|
| 5501 |
- # A list at the root |
|
| 5502 |
- } elsif ( ref $cursor eq 'ARRAY' ) {
|
|
| 5503 |
- unless ( @$cursor ) {
|
|
| 5504 |
- $lines[-1] .= ' []'; |
|
| 5505 |
- next; |
|
| 5506 |
- } |
|
| 5507 |
- push @lines, $self->_write_array( $cursor, $indent, {} );
|
|
| 5653 |
+ #--------------------------------------------------------------------------# |
|
| 5654 |
+ # Code |
|
| 5655 |
+ #--------------------------------------------------------------------------# |
|
| 5508 | 5656 |
|
| 5509 |
- # A hash at the root |
|
| 5510 |
- } elsif ( ref $cursor eq 'HASH' ) {
|
|
| 5511 |
- unless ( %$cursor ) {
|
|
| 5512 |
- $lines[-1] .= ' {}';
|
|
| 5513 |
- next; |
|
| 5514 |
- } |
|
| 5515 |
- push @lines, $self->_write_hash( $cursor, $indent, {} );
|
|
| 5516 | 5657 |
|
| 5517 |
- } else {
|
|
| 5518 |
- Carp::croak("Cannot serialize " . ref($cursor));
|
|
| 5519 |
- } |
|
| 5520 |
- } |
|
| 5658 |
+ sub new {
|
|
| 5659 |
+ my ($class,$data) = @_; |
|
| 5521 | 5660 |
|
| 5522 |
- join '', map { "$_\n" } @lines;
|
|
| 5661 |
+ # create an attributes hash |
|
| 5662 |
+ my $self = {
|
|
| 5663 |
+ 'data' => $data, |
|
| 5664 |
+ 'spec' => $data->{'meta-spec'}{'version'} || "1.0",
|
|
| 5665 |
+ 'errors' => undef, |
|
| 5666 |
+ }; |
|
| 5667 |
+ |
|
| 5668 |
+ # create the object |
|
| 5669 |
+ return bless $self, $class; |
|
| 5523 | 5670 |
} |
| 5524 | 5671 |
|
| 5525 |
- sub _write_scalar {
|
|
| 5526 |
- my $string = $_[1]; |
|
| 5527 |
- return '~' unless defined $string; |
|
| 5528 |
- return "''" unless length $string; |
|
| 5529 |
- if ( $string =~ /[\x00-\x08\x0b-\x0d\x0e-\x1f\"\'\n]/ ) {
|
|
| 5530 |
- $string =~ s/\\/\\\\/g; |
|
| 5531 |
- $string =~ s/"/\\"/g; |
|
| 5532 |
- $string =~ s/\n/\\n/g; |
|
| 5533 |
- $string =~ s/([\x00-\x1f])/\\$UNPRINTABLE[ord($1)]/g; |
|
| 5534 |
- return qq|"$string"|; |
|
| 5535 |
- } |
|
| 5536 |
- if ( $string =~ /(?:^\W|\s)/ or $QUOTE{$string} ) {
|
|
| 5537 |
- return "'$string'"; |
|
| 5538 |
- } |
|
| 5539 |
- return $string; |
|
| 5672 |
+ |
|
| 5673 |
+ sub is_valid {
|
|
| 5674 |
+ my $self = shift; |
|
| 5675 |
+ my $data = $self->{data};
|
|
| 5676 |
+ my $spec_version = $self->{spec};
|
|
| 5677 |
+ $self->check_map($definitions{$spec_version},$data);
|
|
| 5678 |
+ return ! $self->errors; |
|
| 5540 | 5679 |
} |
| 5541 | 5680 |
|
| 5542 |
- sub _write_array {
|
|
| 5543 |
- my ($self, $array, $indent, $seen) = @_; |
|
| 5544 |
- if ( $seen->{refaddr($array)}++ ) {
|
|
| 5545 |
- die "CPAN::Meta::YAML does not support circular references"; |
|
| 5546 |
- } |
|
| 5547 |
- my @lines = (); |
|
| 5548 |
- foreach my $el ( @$array ) {
|
|
| 5549 |
- my $line = (' ' x $indent) . '-';
|
|
| 5550 |
- my $type = ref $el; |
|
| 5551 |
- if ( ! $type ) {
|
|
| 5552 |
- $line .= ' ' . $self->_write_scalar( $el, $indent + 1 ); |
|
| 5553 |
- push @lines, $line; |
|
| 5554 | 5681 |
|
| 5555 |
- } elsif ( $type eq 'ARRAY' ) {
|
|
| 5556 |
- if ( @$el ) {
|
|
| 5557 |
- push @lines, $line; |
|
| 5558 |
- push @lines, $self->_write_array( $el, $indent + 1, $seen ); |
|
| 5559 |
- } else {
|
|
| 5560 |
- $line .= ' []'; |
|
| 5561 |
- push @lines, $line; |
|
| 5562 |
- } |
|
| 5682 |
+ sub errors {
|
|
| 5683 |
+ my $self = shift; |
|
| 5684 |
+ return () unless(defined $self->{errors});
|
|
| 5685 |
+ return @{$self->{errors}};
|
|
| 5686 |
+ } |
|
| 5563 | 5687 |
|
| 5564 |
- } elsif ( $type eq 'HASH' ) {
|
|
| 5565 |
- if ( keys %$el ) {
|
|
| 5566 |
- push @lines, $line; |
|
| 5567 |
- push @lines, $self->_write_hash( $el, $indent + 1, $seen ); |
|
| 5568 |
- } else {
|
|
| 5569 |
- $line .= ' {}';
|
|
| 5570 |
- push @lines, $line; |
|
| 5571 |
- } |
|
| 5572 | 5688 |
|
| 5573 |
- } else {
|
|
| 5574 |
- die "CPAN::Meta::YAML does not support $type references"; |
|
| 5575 |
- } |
|
| 5576 |
- } |
|
| 5689 |
+ my $spec_error = "Missing validation action in specification. " |
|
| 5690 |
+ . "Must be one of 'map', 'list', 'lazylist', or 'value'"; |
|
| 5577 | 5691 |
|
| 5578 |
- @lines; |
|
| 5579 |
- } |
|
| 5692 |
+ sub check_map {
|
|
| 5693 |
+ my ($self,$spec,$data) = @_; |
|
| 5580 | 5694 |
|
| 5581 |
- sub _write_hash {
|
|
| 5582 |
- my ($self, $hash, $indent, $seen) = @_; |
|
| 5583 |
- if ( $seen->{refaddr($hash)}++ ) {
|
|
| 5584 |
- die "CPAN::Meta::YAML does not support circular references"; |
|
| 5585 |
- } |
|
| 5586 |
- my @lines = (); |
|
| 5587 |
- foreach my $name ( sort keys %$hash ) {
|
|
| 5588 |
- my $el = $hash->{$name};
|
|
| 5589 |
- my $line = (' ' x $indent) . "$name:";
|
|
| 5590 |
- my $type = ref $el; |
|
| 5591 |
- if ( ! $type ) {
|
|
| 5592 |
- $line .= ' ' . $self->_write_scalar( $el, $indent + 1 ); |
|
| 5593 |
- push @lines, $line; |
|
| 5695 |
+ if(ref($spec) ne 'HASH') {
|
|
| 5696 |
+ $self->_error( "Unknown META specification, cannot validate." ); |
|
| 5697 |
+ return; |
|
| 5698 |
+ } |
|
| 5594 | 5699 |
|
| 5595 |
- } elsif ( $type eq 'ARRAY' ) {
|
|
| 5596 |
- if ( @$el ) {
|
|
| 5597 |
- push @lines, $line; |
|
| 5598 |
- push @lines, $self->_write_array( $el, $indent + 1, $seen ); |
|
| 5599 |
- } else {
|
|
| 5600 |
- $line .= ' []'; |
|
| 5601 |
- push @lines, $line; |
|
| 5602 |
- } |
|
| 5700 |
+ if(ref($data) ne 'HASH') {
|
|
| 5701 |
+ $self->_error( "Expected a map structure from string or file." ); |
|
| 5702 |
+ return; |
|
| 5703 |
+ } |
|
| 5603 | 5704 |
|
| 5604 |
- } elsif ( $type eq 'HASH' ) {
|
|
| 5605 |
- if ( keys %$el ) {
|
|
| 5606 |
- push @lines, $line; |
|
| 5607 |
- push @lines, $self->_write_hash( $el, $indent + 1, $seen ); |
|
| 5608 |
- } else {
|
|
| 5609 |
- $line .= ' {}';
|
|
| 5610 |
- push @lines, $line; |
|
| 5611 |
- } |
|
| 5705 |
+ for my $key (keys %$spec) {
|
|
| 5706 |
+ next unless($spec->{$key}->{mandatory});
|
|
| 5707 |
+ next if(defined $data->{$key});
|
|
| 5708 |
+ push @{$self->{stack}}, $key;
|
|
| 5709 |
+ $self->_error( "Missing mandatory field, '$key'" ); |
|
| 5710 |
+ pop @{$self->{stack}};
|
|
| 5711 |
+ } |
|
| 5612 | 5712 |
|
| 5613 |
- } else {
|
|
| 5614 |
- die "CPAN::Meta::YAML does not support $type references"; |
|
| 5615 |
- } |
|
| 5616 |
- } |
|
| 5713 |
+ for my $key (keys %$data) {
|
|
| 5714 |
+ push @{$self->{stack}}, $key;
|
|
| 5715 |
+ if($spec->{$key}) {
|
|
| 5716 |
+ if($spec->{$key}{value}) {
|
|
| 5717 |
+ $spec->{$key}{value}->($self,$key,$data->{$key});
|
|
| 5718 |
+ } elsif($spec->{$key}{'map'}) {
|
|
| 5719 |
+ $self->check_map($spec->{$key}{'map'},$data->{$key});
|
|
| 5720 |
+ } elsif($spec->{$key}{'list'}) {
|
|
| 5721 |
+ $self->check_list($spec->{$key}{'list'},$data->{$key});
|
|
| 5722 |
+ } elsif($spec->{$key}{'lazylist'}) {
|
|
| 5723 |
+ $self->check_lazylist($spec->{$key}{'lazylist'},$data->{$key});
|
|
| 5724 |
+ } else {
|
|
| 5725 |
+ $self->_error( "$spec_error for '$key'" ); |
|
| 5726 |
+ } |
|
| 5617 | 5727 |
|
| 5618 |
- @lines; |
|
| 5619 |
- } |
|
| 5728 |
+ } elsif ($spec->{':key'}) {
|
|
| 5729 |
+ $spec->{':key'}{name}->($self,$key,$key);
|
|
| 5730 |
+ if($spec->{':key'}{value}) {
|
|
| 5731 |
+ $spec->{':key'}{value}->($self,$key,$data->{$key});
|
|
| 5732 |
+ } elsif($spec->{':key'}{'map'}) {
|
|
| 5733 |
+ $self->check_map($spec->{':key'}{'map'},$data->{$key});
|
|
| 5734 |
+ } elsif($spec->{':key'}{'list'}) {
|
|
| 5735 |
+ $self->check_list($spec->{':key'}{'list'},$data->{$key});
|
|
| 5736 |
+ } elsif($spec->{':key'}{'lazylist'}) {
|
|
| 5737 |
+ $self->check_lazylist($spec->{':key'}{'lazylist'},$data->{$key});
|
|
| 5738 |
+ } else {
|
|
| 5739 |
+ $self->_error( "$spec_error for ':key'" ); |
|
| 5740 |
+ } |
|
| 5620 | 5741 |
|
| 5621 |
- # Set error |
|
| 5622 |
- sub _error {
|
|
| 5623 |
- $CPAN::Meta::YAML::errstr = $_[1]; |
|
| 5624 |
- undef; |
|
| 5625 |
- } |
|
| 5626 | 5742 |
|
| 5627 |
- # Retrieve error |
|
| 5628 |
- sub errstr {
|
|
| 5629 |
- $CPAN::Meta::YAML::errstr; |
|
| 5743 |
+ } else {
|
|
| 5744 |
+ $self->_error( "Unknown key, '$key', found in map structure" ); |
|
| 5745 |
+ } |
|
| 5746 |
+ pop @{$self->{stack}};
|
|
| 5747 |
+ } |
|
| 5630 | 5748 |
} |
| 5631 | 5749 |
|
| 5750 |
+ # if it's a string, make it into a list and check the list |
|
| 5751 |
+ sub check_lazylist {
|
|
| 5752 |
+ my ($self,$spec,$data) = @_; |
|
| 5632 | 5753 |
|
| 5754 |
+ if ( defined $data && ! ref($data) ) {
|
|
| 5755 |
+ $data = [ $data ]; |
|
| 5756 |
+ } |
|
| 5633 | 5757 |
|
| 5758 |
+ $self->check_list($spec,$data); |
|
| 5759 |
+ } |
|
| 5634 | 5760 |
|
| 5761 |
+ sub check_list {
|
|
| 5762 |
+ my ($self,$spec,$data) = @_; |
|
| 5635 | 5763 |
|
| 5636 |
- ##################################################################### |
|
| 5637 |
- # YAML Compatibility |
|
| 5764 |
+ if(ref($data) ne 'ARRAY') {
|
|
| 5765 |
+ $self->_error( "Expected a list structure" ); |
|
| 5766 |
+ return; |
|
| 5767 |
+ } |
|
| 5638 | 5768 |
|
| 5639 |
- sub Dump {
|
|
| 5640 |
- CPAN::Meta::YAML->new(@_)->write_string; |
|
| 5641 |
- } |
|
| 5769 |
+ if(defined $spec->{mandatory}) {
|
|
| 5770 |
+ if(!defined $data->[0]) {
|
|
| 5771 |
+ $self->_error( "Missing entries from mandatory list" ); |
|
| 5772 |
+ } |
|
| 5773 |
+ } |
|
| 5642 | 5774 |
|
| 5643 |
- sub Load {
|
|
| 5644 |
- my $self = CPAN::Meta::YAML->read_string(@_); |
|
| 5645 |
- unless ( $self ) {
|
|
| 5646 |
- Carp::croak("Failed to load YAML document from string");
|
|
| 5647 |
- } |
|
| 5648 |
- if ( wantarray ) {
|
|
| 5649 |
- return @$self; |
|
| 5650 |
- } else {
|
|
| 5651 |
- # To match YAML.pm, return the last document |
|
| 5652 |
- return $self->[-1]; |
|
| 5653 |
- } |
|
| 5775 |
+ for my $value (@$data) {
|
|
| 5776 |
+ push @{$self->{stack}}, $value || "<undef>";
|
|
| 5777 |
+ if(defined $spec->{value}) {
|
|
| 5778 |
+ $spec->{value}->($self,'list',$value);
|
|
| 5779 |
+ } elsif(defined $spec->{'map'}) {
|
|
| 5780 |
+ $self->check_map($spec->{'map'},$value);
|
|
| 5781 |
+ } elsif(defined $spec->{'list'}) {
|
|
| 5782 |
+ $self->check_list($spec->{'list'},$value);
|
|
| 5783 |
+ } elsif(defined $spec->{'lazylist'}) {
|
|
| 5784 |
+ $self->check_lazylist($spec->{'lazylist'},$value);
|
|
| 5785 |
+ } elsif ($spec->{':key'}) {
|
|
| 5786 |
+ $self->check_map($spec,$value); |
|
| 5787 |
+ } else {
|
|
| 5788 |
+ $self->_error( "$spec_error associated with '$self->{stack}[-2]'" );
|
|
| 5789 |
+ } |
|
| 5790 |
+ pop @{$self->{stack}};
|
|
| 5791 |
+ } |
|
| 5654 | 5792 |
} |
| 5655 | 5793 |
|
| 5656 |
- BEGIN {
|
|
| 5657 |
- *freeze = *Dump; |
|
| 5658 |
- *thaw = *Load; |
|
| 5659 |
- } |
|
| 5660 | 5794 |
|
| 5661 |
- sub DumpFile {
|
|
| 5662 |
- my $file = shift; |
|
| 5663 |
- CPAN::Meta::YAML->new(@_)->write($file); |
|
| 5795 |
+ sub header {
|
|
| 5796 |
+ my ($self,$key,$value) = @_; |
|
| 5797 |
+ if(defined $value) {
|
|
| 5798 |
+ return 1 if($value && $value =~ /^--- #YAML:1.0/); |
|
| 5799 |
+ } |
|
| 5800 |
+ $self->_error( "file does not have a valid YAML header." ); |
|
| 5801 |
+ return 0; |
|
| 5664 | 5802 |
} |
| 5665 | 5803 |
|
| 5666 |
- sub LoadFile {
|
|
| 5667 |
- my $self = CPAN::Meta::YAML->read($_[0]); |
|
| 5668 |
- unless ( $self ) {
|
|
| 5669 |
- Carp::croak("Failed to load YAML document from '" . ($_[0] || '') . "'");
|
|
| 5670 |
- } |
|
| 5671 |
- if ( wantarray ) {
|
|
| 5672 |
- return @$self; |
|
| 5673 |
- } else {
|
|
| 5674 |
- # Return only the last document to match YAML.pm, |
|
| 5675 |
- return $self->[-1]; |
|
| 5676 |
- } |
|
| 5804 |
+ sub release_status {
|
|
| 5805 |
+ my ($self,$key,$value) = @_; |
|
| 5806 |
+ if(defined $value) {
|
|
| 5807 |
+ my $version = $self->{data}{version} || '';
|
|
| 5808 |
+ if ( $version =~ /_/ ) {
|
|
| 5809 |
+ return 1 if ( $value =~ /\A(?:testing|unstable)\z/ ); |
|
| 5810 |
+ $self->_error( "'$value' for '$key' is invalid for version '$version'" ); |
|
| 5811 |
+ } |
|
| 5812 |
+ else {
|
|
| 5813 |
+ return 1 if ( $value =~ /\A(?:stable|testing|unstable)\z/ ); |
|
| 5814 |
+ $self->_error( "'$value' for '$key' is invalid" ); |
|
| 5815 |
+ } |
|
| 5816 |
+ } |
|
| 5817 |
+ else {
|
|
| 5818 |
+ $self->_error( "'$key' is not defined" ); |
|
| 5819 |
+ } |
|
| 5820 |
+ return 0; |
|
| 5677 | 5821 |
} |
| 5678 | 5822 |
|
| 5823 |
+ # _uri_split taken from URI::Split by Gisle Aas, Copyright 2003 |
|
| 5824 |
+ sub _uri_split {
|
|
| 5825 |
+ return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,; |
|
| 5826 |
+ } |
|
| 5679 | 5827 |
|
| 5680 |
- |
|
| 5681 |
- |
|
| 5682 |
- |
|
| 5683 |
- ##################################################################### |
|
| 5684 |
- # Use Scalar::Util if possible, otherwise emulate it |
|
| 5685 |
- |
|
| 5686 |
- BEGIN {
|
|
| 5687 |
- eval {
|
|
| 5688 |
- require Scalar::Util; |
|
| 5689 |
- *refaddr = *Scalar::Util::refaddr; |
|
| 5690 |
- }; |
|
| 5691 |
- eval <<'END_PERL' if $@; |
|
| 5692 |
- # Failed to load Scalar::Util |
|
| 5693 |
- sub refaddr {
|
|
| 5694 |
- my $pkg = ref($_[0]) or return undef; |
|
| 5695 |
- if ( !! UNIVERSAL::can($_[0], 'can') ) {
|
|
| 5696 |
- bless $_[0], 'Scalar::Util::Fake'; |
|
| 5697 |
- } else {
|
|
| 5698 |
- $pkg = undef; |
|
| 5699 |
- } |
|
| 5700 |
- "$_[0]" =~ /0x(\w+)/; |
|
| 5701 |
- my $i = do { local $^W; hex $1 };
|
|
| 5702 |
- bless $_[0], $pkg if defined $pkg; |
|
| 5703 |
- $i; |
|
| 5828 |
+ sub url {
|
|
| 5829 |
+ my ($self,$key,$value) = @_; |
|
| 5830 |
+ if(defined $value) {
|
|
| 5831 |
+ my ($scheme, $auth, $path, $query, $frag) = _uri_split($value); |
|
| 5832 |
+ unless ( defined $scheme && length $scheme ) {
|
|
| 5833 |
+ $self->_error( "'$value' for '$key' does not have a URL scheme" ); |
|
| 5834 |
+ return 0; |
|
| 5835 |
+ } |
|
| 5836 |
+ unless ( defined $auth && length $auth ) {
|
|
| 5837 |
+ $self->_error( "'$value' for '$key' does not have a URL authority" ); |
|
| 5838 |
+ return 0; |
|
| 5839 |
+ } |
|
| 5840 |
+ return 1; |
|
| 5841 |
+ } |
|
| 5842 |
+ $value ||= ''; |
|
| 5843 |
+ $self->_error( "'$value' for '$key' is not a valid URL." ); |
|
| 5844 |
+ return 0; |
|
| 5704 | 5845 |
} |
| 5705 |
- END_PERL |
|
| 5706 | 5846 |
|
| 5847 |
+ sub urlspec {
|
|
| 5848 |
+ my ($self,$key,$value) = @_; |
|
| 5849 |
+ if(defined $value) {
|
|
| 5850 |
+ return 1 if($value && $known_specs{$self->{spec}} eq $value);
|
|
| 5851 |
+ if($value && $known_urls{$value}) {
|
|
| 5852 |
+ $self->_error( 'META specification URL does not match version' ); |
|
| 5853 |
+ return 0; |
|
| 5854 |
+ } |
|
| 5855 |
+ } |
|
| 5856 |
+ $self->_error( 'Unknown META specification' ); |
|
| 5857 |
+ return 0; |
|
| 5707 | 5858 |
} |
| 5708 | 5859 |
|
| 5709 |
- 1; |
|
| 5710 |
- |
|
| 5711 |
- |
|
| 5712 |
- |
|
| 5713 |
- |
|
| 5714 |
- __END__ |
|
| 5715 |
- |
|
| 5716 |
- |
|
| 5717 |
- # ABSTRACT: Read and write a subset of YAML for CPAN Meta files |
|
| 5718 |
- |
|
| 5860 |
+ sub anything { return 1 }
|
|
| 5719 | 5861 |
|
| 5720 |
-CPAN_META_YAML |
|
| 5721 |
- |
|
| 5722 |
-$fatpacked{"HTTP/Tiny.pm"} = <<'HTTP_TINY';
|
|
| 5723 |
- # vim: ts=4 sts=4 sw=4 et: |
|
| 5724 |
- # |
|
| 5725 |
- # This file is part of HTTP-Tiny |
|
| 5726 |
- # |
|
| 5727 |
- # This software is copyright (c) 2011 by Christian Hansen. |
|
| 5728 |
- # |
|
| 5729 |
- # This is free software; you can redistribute it and/or modify it under |
|
| 5730 |
- # the same terms as the Perl 5 programming language system itself. |
|
| 5731 |
- # |
|
| 5732 |
- package HTTP::Tiny; |
|
| 5733 |
- BEGIN {
|
|
| 5734 |
- $HTTP::Tiny::VERSION = '0.009'; |
|
| 5862 |
+ sub string {
|
|
| 5863 |
+ my ($self,$key,$value) = @_; |
|
| 5864 |
+ if(defined $value) {
|
|
| 5865 |
+ return 1 if($value || $value =~ /^0$/); |
|
| 5866 |
+ } |
|
| 5867 |
+ $self->_error( "value is an undefined string" ); |
|
| 5868 |
+ return 0; |
|
| 5735 | 5869 |
} |
| 5736 |
- use strict; |
|
| 5737 |
- use warnings; |
|
| 5738 |
- # ABSTRACT: A small, simple, correct HTTP/1.1 client |
|
| 5739 |
- |
|
| 5740 |
- use Carp (); |
|
| 5741 | 5870 |
|
| 5871 |
+ sub string_or_undef {
|
|
| 5872 |
+ my ($self,$key,$value) = @_; |
|
| 5873 |
+ return 1 unless(defined $value); |
|
| 5874 |
+ return 1 if($value || $value =~ /^0$/); |
|
| 5875 |
+ $self->_error( "No string defined for '$key'" ); |
|
| 5876 |
+ return 0; |
|
| 5877 |
+ } |
|
| 5742 | 5878 |
|
| 5743 |
- my @attributes; |
|
| 5744 |
- BEGIN {
|
|
| 5745 |
- @attributes = qw(agent default_headers max_redirect max_size proxy timeout); |
|
| 5746 |
- no strict 'refs'; |
|
| 5747 |
- for my $accessor ( @attributes ) {
|
|
| 5748 |
- *{$accessor} = sub {
|
|
| 5749 |
- @_ > 1 ? $_[0]->{$accessor} = $_[1] : $_[0]->{$accessor};
|
|
| 5750 |
- }; |
|
| 5751 |
- } |
|
| 5879 |
+ sub file {
|
|
| 5880 |
+ my ($self,$key,$value) = @_; |
|
| 5881 |
+ return 1 if(defined $value); |
|
| 5882 |
+ $self->_error( "No file defined for '$key'" ); |
|
| 5883 |
+ return 0; |
|
| 5752 | 5884 |
} |
| 5753 | 5885 |
|
| 5754 |
- sub new {
|
|
| 5755 |
- my($class, %args) = @_; |
|
| 5756 |
- (my $agent = $class) =~ s{::}{-}g;
|
|
| 5757 |
- my $self = {
|
|
| 5758 |
- agent => $agent . "/" . ($class->VERSION || 0), |
|
| 5759 |
- max_redirect => 5, |
|
| 5760 |
- timeout => 60, |
|
| 5761 |
- }; |
|
| 5762 |
- for my $key ( @attributes ) {
|
|
| 5763 |
- $self->{$key} = $args{$key} if exists $args{$key}
|
|
| 5886 |
+ sub exversion {
|
|
| 5887 |
+ my ($self,$key,$value) = @_; |
|
| 5888 |
+ if(defined $value && ($value || $value =~ /0/)) {
|
|
| 5889 |
+ my $pass = 1; |
|
| 5890 |
+ for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
|
|
| 5891 |
+ return $pass; |
|
| 5764 | 5892 |
} |
| 5765 |
- return bless $self, $class; |
|
| 5893 |
+ $value = '<undef>' unless(defined $value); |
|
| 5894 |
+ $self->_error( "'$value' for '$key' is not a valid version." ); |
|
| 5895 |
+ return 0; |
|
| 5766 | 5896 |
} |
| 5767 | 5897 |
|
| 5768 |
- |
|
| 5769 |
- sub get {
|
|
| 5770 |
- my ($self, $url, $args) = @_; |
|
| 5771 |
- @_ == 2 || (@_ == 3 && ref $args eq 'HASH') |
|
| 5772 |
- or Carp::croak(q/Usage: $http->get(URL, [HASHREF])/); |
|
| 5773 |
- return $self->request('GET', $url, $args || {});
|
|
| 5898 |
+ sub version {
|
|
| 5899 |
+ my ($self,$key,$value) = @_; |
|
| 5900 |
+ if(defined $value) {
|
|
| 5901 |
+ return 0 unless($value || $value =~ /0/); |
|
| 5902 |
+ return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/); |
|
| 5903 |
+ } else {
|
|
| 5904 |
+ $value = '<undef>'; |
|
| 5905 |
+ } |
|
| 5906 |
+ $self->_error( "'$value' for '$key' is not a valid version." ); |
|
| 5907 |
+ return 0; |
|
| 5774 | 5908 |
} |
| 5775 | 5909 |
|
| 5776 |
- |
|
| 5777 |
- sub mirror {
|
|
| 5778 |
- my ($self, $url, $file, $args) = @_; |
|
| 5779 |
- @_ == 3 || (@_ == 4 && ref $args eq 'HASH') |
|
| 5780 |
- or Carp::croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/); |
|
| 5781 |
- if ( -e $file and my $mtime = (stat($file))[9] ) {
|
|
| 5782 |
- $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
|
|
| 5783 |
- } |
|
| 5784 |
- my $tempfile = $file . int(rand(2**31)); |
|
| 5785 |
- open my $fh, ">", $tempfile |
|
| 5786 |
- or Carp::croak(qq/Error: Could not open temporary file $tempfile for downloading: $!/); |
|
| 5787 |
- $args->{data_callback} = sub { print {$fh} $_[0] };
|
|
| 5788 |
- my $response = $self->request('GET', $url, $args);
|
|
| 5789 |
- close $fh |
|
| 5790 |
- or Carp::croak(qq/Error: Could not close temporary file $tempfile: $!/); |
|
| 5791 |
- if ( $response->{success} ) {
|
|
| 5792 |
- rename $tempfile, $file |
|
| 5793 |
- or Carp::croak "Error replacing $file with $tempfile: $!\n"; |
|
| 5794 |
- my $lm = $response->{headers}{'last-modified'};
|
|
| 5795 |
- if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
|
|
| 5796 |
- utime $mtime, $mtime, $file; |
|
| 5797 |
- } |
|
| 5910 |
+ sub boolean {
|
|
| 5911 |
+ my ($self,$key,$value) = @_; |
|
| 5912 |
+ if(defined $value) {
|
|
| 5913 |
+ return 1 if($value =~ /^(0|1|true|false)$/); |
|
| 5914 |
+ } else {
|
|
| 5915 |
+ $value = '<undef>'; |
|
| 5798 | 5916 |
} |
| 5799 |
- $response->{success} ||= $response->{status} eq '304';
|
|
| 5800 |
- unlink $tempfile; |
|
| 5801 |
- return $response; |
|
| 5917 |
+ $self->_error( "'$value' for '$key' is not a boolean value." ); |
|
| 5918 |
+ return 0; |
|
| 5802 | 5919 |
} |
| 5803 | 5920 |
|
| 5921 |
+ my %v1_licenses = ( |
|
| 5922 |
+ 'perl' => 'http://dev.perl.org/licenses/', |
|
| 5923 |
+ 'gpl' => 'http://www.opensource.org/licenses/gpl-license.php', |
|
| 5924 |
+ 'apache' => 'http://apache.org/licenses/LICENSE-2.0', |
|
| 5925 |
+ 'artistic' => 'http://opensource.org/licenses/artistic-license.php', |
|
| 5926 |
+ 'artistic_2' => 'http://opensource.org/licenses/artistic-license-2.0.php', |
|
| 5927 |
+ 'lgpl' => 'http://www.opensource.org/licenses/lgpl-license.php', |
|
| 5928 |
+ 'bsd' => 'http://www.opensource.org/licenses/bsd-license.php', |
|
| 5929 |
+ 'gpl' => 'http://www.opensource.org/licenses/gpl-license.php', |
|
| 5930 |
+ 'mit' => 'http://opensource.org/licenses/mit-license.php', |
|
| 5931 |
+ 'mozilla' => 'http://opensource.org/licenses/mozilla1.1.php', |
|
| 5932 |
+ 'open_source' => undef, |
|
| 5933 |
+ 'unrestricted' => undef, |
|
| 5934 |
+ 'restrictive' => undef, |
|
| 5935 |
+ 'unknown' => undef, |
|
| 5936 |
+ ); |
|
| 5804 | 5937 |
|
| 5805 |
- my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
|
|
| 5938 |
+ my %v2_licenses = map { $_ => 1 } qw(
|
|
| 5939 |
+ agpl_3 |
|
| 5940 |
+ apache_1_1 |
|
| 5941 |
+ apache_2_0 |
|
| 5942 |
+ artistic_1 |
|
| 5943 |
+ artistic_2 |
|
| 5944 |
+ bsd |
|
| 5945 |
+ freebsd |
|
| 5946 |
+ gfdl_1_2 |
|
| 5947 |
+ gfdl_1_3 |
|
| 5948 |
+ gpl_1 |
|
| 5949 |
+ gpl_2 |
|
| 5950 |
+ gpl_3 |
|
| 5951 |
+ lgpl_2_1 |
|
| 5952 |
+ lgpl_3_0 |
|
| 5953 |
+ mit |
|
| 5954 |
+ mozilla_1_0 |
|
| 5955 |
+ mozilla_1_1 |
|
| 5956 |
+ openssl |
|
| 5957 |
+ perl_5 |
|
| 5958 |
+ qpl_1_0 |
|
| 5959 |
+ ssleay |
|
| 5960 |
+ sun |
|
| 5961 |
+ zlib |
|
| 5962 |
+ open_source |
|
| 5963 |
+ restricted |
|
| 5964 |
+ unrestricted |
|
| 5965 |
+ unknown |
|
| 5966 |
+ ); |
|
| 5806 | 5967 |
|
| 5807 |
- sub request {
|
|
| 5808 |
- my ($self, $method, $url, $args) = @_; |
|
| 5809 |
- @_ == 3 || (@_ == 4 && ref $args eq 'HASH') |
|
| 5810 |
- or Carp::croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/); |
|
| 5811 |
- $args ||= {}; # we keep some state in this during _request
|
|
| 5968 |
+ sub license {
|
|
| 5969 |
+ my ($self,$key,$value) = @_; |
|
| 5970 |
+ my $licenses = $self->{spec} < 2 ? \%v1_licenses : \%v2_licenses;
|
|
| 5971 |
+ if(defined $value) {
|
|
| 5972 |
+ return 1 if($value && exists $licenses->{$value});
|
|
| 5973 |
+ } else {
|
|
| 5974 |
+ $value = '<undef>'; |
|
| 5975 |
+ } |
|
| 5976 |
+ $self->_error( "License '$value' is invalid" ); |
|
| 5977 |
+ return 0; |
|
| 5978 |
+ } |
|
| 5812 | 5979 |
|
| 5813 |
- # RFC 2616 Section 8.1.4 mandates a single retry on broken socket |
|
| 5814 |
- my $response; |
|
| 5815 |
- for ( 0 .. 1 ) {
|
|
| 5816 |
- $response = eval { $self->_request($method, $url, $args) };
|
|
| 5817 |
- last unless $@ && $idempotent{$method}
|
|
| 5818 |
- && $@ =~ m{^(?:Socket closed|Unexpected end)};
|
|
| 5980 |
+ sub custom_1 {
|
|
| 5981 |
+ my ($self,$key) = @_; |
|
| 5982 |
+ if(defined $key) {
|
|
| 5983 |
+ # a valid user defined key should be alphabetic |
|
| 5984 |
+ # and contain at least one capital case letter. |
|
| 5985 |
+ return 1 if($key && $key =~ /^[_a-z]+$/i && $key =~ /[A-Z]/); |
|
| 5986 |
+ } else {
|
|
| 5987 |
+ $key = '<undef>'; |
|
| 5819 | 5988 |
} |
| 5989 |
+ $self->_error( "Custom resource '$key' must be in CamelCase." ); |
|
| 5990 |
+ return 0; |
|
| 5991 |
+ } |
|
| 5820 | 5992 |
|
| 5821 |
- if (my $e = "$@") {
|
|
| 5822 |
- $response = {
|
|
| 5823 |
- success => q{},
|
|
| 5824 |
- status => 599, |
|
| 5825 |
- reason => 'Internal Exception', |
|
| 5826 |
- content => $e, |
|
| 5827 |
- headers => {
|
|
| 5828 |
- 'content-type' => 'text/plain', |
|
| 5829 |
- 'content-length' => length $e, |
|
| 5830 |
- } |
|
| 5831 |
- }; |
|
| 5993 |
+ sub custom_2 {
|
|
| 5994 |
+ my ($self,$key) = @_; |
|
| 5995 |
+ if(defined $key) {
|
|
| 5996 |
+ return 1 if($key && $key =~ /^x_/i); # user defined |
|
| 5997 |
+ } else {
|
|
| 5998 |
+ $key = '<undef>'; |
|
| 5832 | 5999 |
} |
| 5833 |
- return $response; |
|
| 6000 |
+ $self->_error( "Custom key '$key' must begin with 'x_' or 'X_'." ); |
|
| 6001 |
+ return 0; |
|
| 5834 | 6002 |
} |
| 5835 | 6003 |
|
| 5836 |
- my %DefaultPort = ( |
|
| 5837 |
- http => 80, |
|
| 5838 |
- https => 443, |
|
| 5839 |
- ); |
|
| 5840 |
- |
|
| 5841 |
- sub _request {
|
|
| 5842 |
- my ($self, $method, $url, $args) = @_; |
|
| 5843 |
- |
|
| 5844 |
- my ($scheme, $host, $port, $path_query) = $self->_split_url($url); |
|
| 5845 |
- |
|
| 5846 |
- my $request = {
|
|
| 5847 |
- method => $method, |
|
| 5848 |
- scheme => $scheme, |
|
| 5849 |
- host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
|
|
| 5850 |
- uri => $path_query, |
|
| 5851 |
- headers => {},
|
|
| 5852 |
- }; |
|
| 5853 |
- |
|
| 5854 |
- my $handle = HTTP::Tiny::Handle->new(timeout => $self->{timeout});
|
|
| 5855 |
- |
|
| 5856 |
- if ($self->{proxy}) {
|
|
| 5857 |
- $request->{uri} = "$scheme://$request->{host_port}$path_query";
|
|
| 5858 |
- croak(qq/HTTPS via proxy is not supported/) |
|
| 5859 |
- if $request->{scheme} eq 'https';
|
|
| 5860 |
- $handle->connect(($self->_split_url($self->{proxy}))[0..2]);
|
|
| 5861 |
- } |
|
| 5862 |
- else {
|
|
| 5863 |
- $handle->connect($scheme, $host, $port); |
|
| 5864 |
- } |
|
| 5865 |
- |
|
| 5866 |
- $self->_prepare_headers_and_cb($request, $args); |
|
| 5867 |
- $handle->write_request($request); |
|
| 5868 |
- |
|
| 5869 |
- my $response; |
|
| 5870 |
- do { $response = $handle->read_response_header }
|
|
| 5871 |
- until (substr($response->{status},0,1) ne '1');
|
|
| 5872 |
- |
|
| 5873 |
- if ( my @redir_args = $self->_maybe_redirect($request, $response, $args) ) {
|
|
| 5874 |
- $handle->close; |
|
| 5875 |
- return $self->_request(@redir_args, $args); |
|
| 6004 |
+ sub identifier {
|
|
| 6005 |
+ my ($self,$key) = @_; |
|
| 6006 |
+ if(defined $key) {
|
|
| 6007 |
+ return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined |
|
| 6008 |
+ } else {
|
|
| 6009 |
+ $key = '<undef>'; |
|
| 5876 | 6010 |
} |
| 6011 |
+ $self->_error( "Key '$key' is not a legal identifier." ); |
|
| 6012 |
+ return 0; |
|
| 6013 |
+ } |
|
| 5877 | 6014 |
|
| 5878 |
- if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
|
|
| 5879 |
- # response has no message body |
|
| 5880 |
- } |
|
| 5881 |
- else {
|
|
| 5882 |
- my $data_cb = $self->_prepare_data_cb($response, $args); |
|
| 5883 |
- $handle->read_body($data_cb, $response); |
|
| 6015 |
+ sub module {
|
|
| 6016 |
+ my ($self,$key) = @_; |
|
| 6017 |
+ if(defined $key) {
|
|
| 6018 |
+ return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/); |
|
| 6019 |
+ } else {
|
|
| 6020 |
+ $key = '<undef>'; |
|
| 5884 | 6021 |
} |
| 5885 |
- |
|
| 5886 |
- $handle->close; |
|
| 5887 |
- $response->{success} = substr($response->{status},0,1) eq '2';
|
|
| 5888 |
- return $response; |
|
| 6022 |
+ $self->_error( "Key '$key' is not a legal module name." ); |
|
| 6023 |
+ return 0; |
|
| 5889 | 6024 |
} |
| 5890 | 6025 |
|
| 5891 |
- sub _prepare_headers_and_cb {
|
|
| 5892 |
- my ($self, $request, $args) = @_; |
|
| 5893 |
- |
|
| 5894 |
- for ($self->{default_headers}, $args->{headers}) {
|
|
| 5895 |
- next unless defined; |
|
| 5896 |
- while (my ($k, $v) = each %$_) {
|
|
| 5897 |
- $request->{headers}{lc $k} = $v;
|
|
| 5898 |
- } |
|
| 6026 |
+ my @valid_phases = qw/ configure build test runtime develop /; |
|
| 6027 |
+ sub phase {
|
|
| 6028 |
+ my ($self,$key) = @_; |
|
| 6029 |
+ if(defined $key) {
|
|
| 6030 |
+ return 1 if( length $key && grep { $key eq $_ } @valid_phases );
|
|
| 6031 |
+ return 1 if $key =~ /x_/i; |
|
| 6032 |
+ } else {
|
|
| 6033 |
+ $key = '<undef>'; |
|
| 5899 | 6034 |
} |
| 5900 |
- $request->{headers}{'host'} = $request->{host_port};
|
|
| 5901 |
- $request->{headers}{'connection'} = "close";
|
|
| 5902 |
- $request->{headers}{'user-agent'} ||= $self->{agent};
|
|
| 6035 |
+ $self->_error( "Key '$key' is not a legal phase." ); |
|
| 6036 |
+ return 0; |
|
| 6037 |
+ } |
|
| 5903 | 6038 |
|
| 5904 |
- if (defined $args->{content}) {
|
|
| 5905 |
- $request->{headers}{'content-type'} ||= "application/octet-stream";
|
|
| 5906 |
- if (ref $args->{content} eq 'CODE') {
|
|
| 5907 |
- $request->{headers}{'transfer-encoding'} = 'chunked'
|
|
| 5908 |
- unless $request->{headers}{'content-length'}
|
|
| 5909 |
- || $request->{headers}{'transfer-encoding'};
|
|
| 5910 |
- $request->{cb} = $args->{content};
|
|
| 5911 |
- } |
|
| 5912 |
- else {
|
|
| 5913 |
- my $content = $args->{content};
|
|
| 5914 |
- if ( $] ge '5.008' ) {
|
|
| 5915 |
- utf8::downgrade($content, 1) |
|
| 5916 |
- or Carp::croak(q/Wide character in request message body/); |
|
| 5917 |
- } |
|
| 5918 |
- $request->{headers}{'content-length'} = length $content
|
|
| 5919 |
- unless $request->{headers}{'content-length'}
|
|
| 5920 |
- || $request->{headers}{'transfer-encoding'};
|
|
| 5921 |
- $request->{cb} = sub { substr $content, 0, length $content, '' };
|
|
| 5922 |
- } |
|
| 5923 |
- $request->{trailer_cb} = $args->{trailer_callback}
|
|
| 5924 |
- if ref $args->{trailer_callback} eq 'CODE';
|
|
| 6039 |
+ my @valid_relations = qw/ requires recommends suggests conflicts /; |
|
| 6040 |
+ sub relation {
|
|
| 6041 |
+ my ($self,$key) = @_; |
|
| 6042 |
+ if(defined $key) {
|
|
| 6043 |
+ return 1 if( length $key && grep { $key eq $_ } @valid_relations );
|
|
| 6044 |
+ return 1 if $key =~ /x_/i; |
|
| 6045 |
+ } else {
|
|
| 6046 |
+ $key = '<undef>'; |
|
| 5925 | 6047 |
} |
| 5926 |
- return; |
|
| 6048 |
+ $self->_error( "Key '$key' is not a legal prereq relationship." ); |
|
| 6049 |
+ return 0; |
|
| 5927 | 6050 |
} |
| 5928 | 6051 |
|
| 5929 |
- sub _prepare_data_cb {
|
|
| 5930 |
- my ($self, $response, $args) = @_; |
|
| 5931 |
- my $data_cb = $args->{data_callback};
|
|
| 5932 |
- $response->{content} = '';
|
|
| 6052 |
+ sub _error {
|
|
| 6053 |
+ my $self = shift; |
|
| 6054 |
+ my $mess = shift; |
|
| 5933 | 6055 |
|
| 5934 |
- if (!$data_cb || $response->{status} !~ /^2/) {
|
|
| 5935 |
- if (defined $self->{max_size}) {
|
|
| 5936 |
- $data_cb = sub {
|
|
| 5937 |
- $_[1]->{content} .= $_[0];
|
|
| 5938 |
- die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
|
|
| 5939 |
- if length $_[1]->{content} > $self->{max_size};
|
|
| 5940 |
- }; |
|
| 5941 |
- } |
|
| 5942 |
- else {
|
|
| 5943 |
- $data_cb = sub { $_[1]->{content} .= $_[0] };
|
|
| 5944 |
- } |
|
| 5945 |
- } |
|
| 5946 |
- return $data_cb; |
|
| 5947 |
- } |
|
| 6056 |
+ $mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
|
|
| 6057 |
+ $mess .= " [Validation: $self->{spec}]";
|
|
| 5948 | 6058 |
|
| 5949 |
- sub _maybe_redirect {
|
|
| 5950 |
- my ($self, $request, $response, $args) = @_; |
|
| 5951 |
- my $headers = $response->{headers};
|
|
| 5952 |
- my ($status, $method) = ($response->{status}, $request->{method});
|
|
| 5953 |
- if (($status eq '303' or ($status =~ /^30[127]/ && $method =~ /^GET|HEAD$/)) |
|
| 5954 |
- and $headers->{location}
|
|
| 5955 |
- and ++$args->{redirects} <= $self->{max_redirect}
|
|
| 5956 |
- ) {
|
|
| 5957 |
- my $location = ($headers->{location} =~ /^\//)
|
|
| 5958 |
- ? "$request->{scheme}://$request->{host_port}$headers->{location}"
|
|
| 5959 |
- : $headers->{location} ;
|
|
| 5960 |
- return (($status eq '303' ? 'GET' : $method), $location); |
|
| 5961 |
- } |
|
| 5962 |
- return; |
|
| 6059 |
+ push @{$self->{errors}}, $mess;
|
|
| 5963 | 6060 |
} |
| 5964 | 6061 |
|
| 5965 |
- sub _split_url {
|
|
| 5966 |
- my $url = pop; |
|
| 6062 |
+ 1; |
|
| 5967 | 6063 |
|
| 5968 |
- # URI regex adapted from the URI module |
|
| 5969 |
- my ($scheme, $authority, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)> |
|
| 5970 |
- or Carp::croak(qq/Cannot parse URL: '$url'/); |
|
| 6064 |
+ # ABSTRACT: validate CPAN distribution metadata structures |
|
| 5971 | 6065 |
|
| 5972 |
- $scheme = lc $scheme; |
|
| 5973 |
- $path_query = "/$path_query" unless $path_query =~ m<\A/>; |
|
| 5974 | 6066 |
|
| 5975 |
- my $host = (length($authority)) ? lc $authority : 'localhost'; |
|
| 5976 |
- $host =~ s/\A[^@]*@//; # userinfo |
|
| 5977 |
- my $port = do {
|
|
| 5978 |
- $host =~ s/:([0-9]*)\z// && length $1 |
|
| 5979 |
- ? $1 |
|
| 5980 |
- : ($scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef); |
|
| 5981 |
- }; |
|
| 5982 | 6067 |
|
| 5983 |
- return ($scheme, $host, $port, $path_query); |
|
| 5984 |
- } |
|
| 5985 | 6068 |
|
| 5986 |
- # Date conversions adapted from HTTP::Date |
|
| 5987 |
- my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat"; |
|
| 5988 |
- my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"; |
|
| 5989 |
- sub _http_date {
|
|
| 5990 |
- my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]); |
|
| 5991 |
- return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
|
|
| 5992 |
- substr($DoW,$wday*4,3), |
|
| 5993 |
- $mday, substr($MoY,$mon*4,3), $year+1900, |
|
| 5994 |
- $hour, $min, $sec |
|
| 5995 |
- ); |
|
| 5996 |
- } |
|
| 6069 |
+ __END__ |
|
| 5997 | 6070 |
|
| 5998 |
- sub _parse_http_date {
|
|
| 5999 |
- my ($self, $str) = @_; |
|
| 6000 |
- require Time::Local; |
|
| 6001 |
- my @tl_parts; |
|
| 6002 |
- if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
|
|
| 6003 |
- @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); |
|
| 6004 |
- } |
|
| 6005 |
- elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
|
|
| 6006 |
- @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); |
|
| 6007 |
- } |
|
| 6008 |
- elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
|
|
| 6009 |
- @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6); |
|
| 6010 |
- } |
|
| 6011 |
- return eval {
|
|
| 6012 |
- my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1; |
|
| 6013 |
- $t < 0 ? undef : $t; |
|
| 6014 |
- }; |
|
| 6015 |
- } |
|
| 6016 | 6071 |
|
| 6017 |
- package |
|
| 6018 |
- HTTP::Tiny::Handle; # hide from PAUSE/indexers |
|
| 6019 |
- use strict; |
|
| 6020 |
- use warnings; |
|
| 6021 | 6072 |
|
| 6022 |
- use Carp qw[croak]; |
|
| 6023 |
- use Errno qw[EINTR EPIPE]; |
|
| 6024 |
- use IO::Socket qw[SOCK_STREAM]; |
|
| 6073 |
+CPAN_META_VALIDATOR |
|
| 6074 |
+ |
|
| 6075 |
+$fatpacked{"CPAN/Meta/YAML.pm"} = <<'CPAN_META_YAML';
|
|
| 6076 |
+ package CPAN::Meta::YAML; |
|
| 6077 |
+ {
|
|
| 6078 |
+ $CPAN::Meta::YAML::VERSION = '0.008'; |
|
| 6079 |
+ } |
|
| 6025 | 6080 |
|
| 6026 |
- sub BUFSIZE () { 32768 }
|
|
| 6081 |
+ use strict; |
|
| 6027 | 6082 |
|
| 6028 |
- my $Printable = sub {
|
|
| 6029 |
- local $_ = shift; |
|
| 6030 |
- s/\r/\\r/g; |
|
| 6031 |
- s/\n/\\n/g; |
|
| 6032 |
- s/\t/\\t/g; |
|
| 6033 |
- s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
|
|
| 6034 |
- $_; |
|
| 6035 |
- }; |
|
| 6083 |
+ # UTF Support? |
|
| 6084 |
+ sub HAVE_UTF8 () { $] >= 5.007003 }
|
|
| 6085 |
+ BEGIN {
|
|
| 6086 |
+ if ( HAVE_UTF8 ) {
|
|
| 6087 |
+ # The string eval helps hide this from Test::MinimumVersion |
|
| 6088 |
+ eval "require utf8;"; |
|
| 6089 |
+ die "Failed to load UTF-8 support" if $@; |
|
| 6090 |
+ } |
|
| 6036 | 6091 |
|
| 6037 |
- my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/; |
|
| 6092 |
+ # Class structure |
|
| 6093 |
+ require 5.004; |
|
| 6094 |
+ require Exporter; |
|
| 6095 |
+ require Carp; |
|
| 6096 |
+ @CPAN::Meta::YAML::ISA = qw{ Exporter };
|
|
| 6097 |
+ @CPAN::Meta::YAML::EXPORT = qw{ Load Dump };
|
|
| 6098 |
+ @CPAN::Meta::YAML::EXPORT_OK = qw{ LoadFile DumpFile freeze thaw };
|
|
| 6038 | 6099 |
|
| 6039 |
- sub new {
|
|
| 6040 |
- my ($class, %args) = @_; |
|
| 6041 |
- return bless {
|
|
| 6042 |
- rbuf => '', |
|
| 6043 |
- timeout => 60, |
|
| 6044 |
- max_line_size => 16384, |
|
| 6045 |
- max_header_lines => 64, |
|
| 6046 |
- %args |
|
| 6047 |
- }, $class; |
|
| 6100 |
+ # Error storage |
|
| 6101 |
+ $CPAN::Meta::YAML::errstr = ''; |
|
| 6048 | 6102 |
} |
| 6049 | 6103 |
|
| 6050 |
- my $ssl_verify_args = {
|
|
| 6051 |
- check_cn => "when_only", |
|
| 6052 |
- wildcards_in_alt => "anywhere", |
|
| 6053 |
- wildcards_in_cn => "anywhere" |
|
| 6054 |
- }; |
|
| 6055 |
- |
|
| 6056 |
- sub connect {
|
|
| 6057 |
- @_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/); |
|
| 6058 |
- my ($self, $scheme, $host, $port) = @_; |
|
| 6104 |
+ # The character class of all characters we need to escape |
|
| 6105 |
+ # NOTE: Inlined, since it's only used once |
|
| 6106 |
+ # my $RE_ESCAPE = '[\\x00-\\x08\\x0b-\\x0d\\x0e-\\x1f\"\n]'; |
|
| 6059 | 6107 |
|
| 6060 |
- if ( $scheme eq 'https' ) {
|
|
| 6061 |
- eval "require IO::Socket::SSL" |
|
| 6062 |
- unless exists $INC{'IO/Socket/SSL.pm'};
|
|
| 6063 |
- croak(qq/IO::Socket::SSL must be installed for https support\n/) |
|
| 6064 |
- unless $INC{'IO/Socket/SSL.pm'};
|
|
| 6065 |
- } |
|
| 6066 |
- elsif ( $scheme ne 'http' ) {
|
|
| 6067 |
- croak(qq/Unsupported URL scheme '$scheme'/); |
|
| 6068 |
- } |
|
| 6069 |
- |
|
| 6070 |
- $self->{fh} = 'IO::Socket::INET'->new(
|
|
| 6071 |
- PeerHost => $host, |
|
| 6072 |
- PeerPort => $port, |
|
| 6073 |
- Proto => 'tcp', |
|
| 6074 |
- Type => SOCK_STREAM, |
|
| 6075 |
- Timeout => $self->{timeout}
|
|
| 6076 |
- ) or croak(qq/Could not connect to '$host:$port': $@/); |
|
| 6077 |
- |
|
| 6078 |
- binmode($self->{fh})
|
|
| 6079 |
- or croak(qq/Could not binmode() socket: '$!'/); |
|
| 6080 |
- |
|
| 6081 |
- if ( $scheme eq 'https') {
|
|
| 6082 |
- IO::Socket::SSL->start_SSL($self->{fh});
|
|
| 6083 |
- ref($self->{fh}) eq 'IO::Socket::SSL'
|
|
| 6084 |
- or die(qq/SSL connection failed for $host\n/); |
|
| 6085 |
- $self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
|
| 6086 |
- or die(qq/SSL certificate not valid for $host\n/); |
|
| 6087 |
- } |
|
| 6108 |
+ # Printed form of the unprintable characters in the lowest range |
|
| 6109 |
+ # of ASCII characters, listed by ASCII ordinal position. |
|
| 6110 |
+ my @UNPRINTABLE = qw( |
|
| 6111 |
+ z x01 x02 x03 x04 x05 x06 a |
|
| 6112 |
+ x08 t n v f r x0e x0f |
|
| 6113 |
+ x10 x11 x12 x13 x14 x15 x16 x17 |
|
| 6114 |
+ x18 x19 x1a e x1c x1d x1e x1f |
|
| 6115 |
+ ); |
|
| 6088 | 6116 |
|
| 6089 |
- $self->{host} = $host;
|
|
| 6090 |
- $self->{port} = $port;
|
|
| 6117 |
+ # Printable characters for escapes |
|
| 6118 |
+ my %UNESCAPES = ( |
|
| 6119 |
+ z => "\x00", a => "\x07", t => "\x09", |
|
| 6120 |
+ n => "\x0a", v => "\x0b", f => "\x0c", |
|
| 6121 |
+ r => "\x0d", e => "\x1b", '\\' => '\\', |
|
| 6122 |
+ ); |
|
| 6091 | 6123 |
|
| 6092 |
- return $self; |
|
| 6093 |
- } |
|
| 6124 |
+ # Special magic boolean words |
|
| 6125 |
+ my %QUOTE = map { $_ => 1 } qw{
|
|
| 6126 |
+ null Null NULL |
|
| 6127 |
+ y Y yes Yes YES n N no No NO |
|
| 6128 |
+ true True TRUE false False FALSE |
|
| 6129 |
+ on On ON off Off OFF |
|
| 6130 |
+ }; |
|
| 6094 | 6131 |
|
| 6095 |
- sub close {
|
|
| 6096 |
- @_ == 1 || croak(q/Usage: $handle->close()/); |
|
| 6097 |
- my ($self) = @_; |
|
| 6098 |
- CORE::close($self->{fh})
|
|
| 6099 |
- or croak(qq/Could not close socket: '$!'/); |
|
| 6100 |
- } |
|
| 6101 | 6132 |
|
| 6102 |
- sub write {
|
|
| 6103 |
- @_ == 2 || croak(q/Usage: $handle->write(buf)/); |
|
| 6104 |
- my ($self, $buf) = @_; |
|
| 6105 | 6133 |
|
| 6106 |
- if ( $] ge '5.008' ) {
|
|
| 6107 |
- utf8::downgrade($buf, 1) |
|
| 6108 |
- or croak(q/Wide character in write()/); |
|
| 6109 |
- } |
|
| 6110 | 6134 |
|
| 6111 |
- my $len = length $buf; |
|
| 6112 |
- my $off = 0; |
|
| 6113 | 6135 |
|
| 6114 |
- local $SIG{PIPE} = 'IGNORE';
|
|
| 6136 |
+ ##################################################################### |
|
| 6137 |
+ # Implementation |
|
| 6115 | 6138 |
|
| 6116 |
- while () {
|
|
| 6117 |
- $self->can_write |
|
| 6118 |
- or croak(q/Timed out while waiting for socket to become ready for writing/); |
|
| 6119 |
- my $r = syswrite($self->{fh}, $buf, $len, $off);
|
|
| 6120 |
- if (defined $r) {
|
|
| 6121 |
- $len -= $r; |
|
| 6122 |
- $off += $r; |
|
| 6123 |
- last unless $len > 0; |
|
| 6124 |
- } |
|
| 6125 |
- elsif ($! == EPIPE) {
|
|
| 6126 |
- croak(qq/Socket closed by remote server: $!/); |
|
| 6127 |
- } |
|
| 6128 |
- elsif ($! != EINTR) {
|
|
| 6129 |
- croak(qq/Could not write to socket: '$!'/); |
|
| 6130 |
- } |
|
| 6131 |
- } |
|
| 6132 |
- return $off; |
|
| 6139 |
+ # Create an empty CPAN::Meta::YAML object |
|
| 6140 |
+ sub new {
|
|
| 6141 |
+ my $class = shift; |
|
| 6142 |
+ bless [ @_ ], $class; |
|
| 6133 | 6143 |
} |
| 6134 | 6144 |
|
| 6145 |
+ # Create an object from a file |
|
| 6135 | 6146 |
sub read {
|
| 6136 |
- @_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len [, allow_partial])/); |
|
| 6137 |
- my ($self, $len, $allow_partial) = @_; |
|
| 6147 |
+ my $class = ref $_[0] ? ref shift : shift; |
|
| 6138 | 6148 |
|
| 6139 |
- my $buf = ''; |
|
| 6140 |
- my $got = length $self->{rbuf};
|
|
| 6149 |
+ # Check the file |
|
| 6150 |
+ my $file = shift or return $class->_error( 'You did not specify a file name' ); |
|
| 6151 |
+ return $class->_error( "File '$file' does not exist" ) unless -e $file; |
|
| 6152 |
+ return $class->_error( "'$file' is a directory, not a file" ) unless -f _; |
|
| 6153 |
+ return $class->_error( "Insufficient permissions to read '$file'" ) unless -r _; |
|
| 6141 | 6154 |
|
| 6142 |
- if ($got) {
|
|
| 6143 |
- my $take = ($got < $len) ? $got : $len; |
|
| 6144 |
- $buf = substr($self->{rbuf}, 0, $take, '');
|
|
| 6145 |
- $len -= $take; |
|
| 6146 |
- } |
|
| 6155 |
+ # Slurp in the file |
|
| 6156 |
+ local $/ = undef; |
|
| 6157 |
+ local *CFG; |
|
| 6158 |
+ unless ( open(CFG, $file) ) {
|
|
| 6159 |
+ return $class->_error("Failed to open file '$file': $!");
|
|
| 6160 |
+ } |
|
| 6161 |
+ my $contents = <CFG>; |
|
| 6162 |
+ unless ( close(CFG) ) {
|
|
| 6163 |
+ return $class->_error("Failed to close file '$file': $!");
|
|
| 6164 |
+ } |
|
| 6147 | 6165 |
|
| 6148 |
- while ($len > 0) {
|
|
| 6149 |
- $self->can_read |
|
| 6150 |
- or croak(q/Timed out while waiting for socket to become ready for reading/); |
|
| 6151 |
- my $r = sysread($self->{fh}, $buf, $len, length $buf);
|
|
| 6152 |
- if (defined $r) {
|
|
| 6153 |
- last unless $r; |
|
| 6154 |
- $len -= $r; |
|
| 6155 |
- } |
|
| 6156 |
- elsif ($! != EINTR) {
|
|
| 6157 |
- croak(qq/Could not read from socket: '$!'/); |
|
| 6158 |
- } |
|
| 6159 |
- } |
|
| 6160 |
- if ($len && !$allow_partial) {
|
|
| 6161 |
- croak(q/Unexpected end of stream/); |
|
| 6162 |
- } |
|
| 6163 |
- return $buf; |
|
| 6166 |
+ $class->read_string( $contents ); |
|
| 6164 | 6167 |
} |
| 6165 | 6168 |
|
| 6166 |
- sub readline {
|
|
| 6167 |
- @_ == 1 || croak(q/Usage: $handle->readline()/); |
|
| 6168 |
- my ($self) = @_; |
|
| 6169 |
+ # Create an object from a string |
|
| 6170 |
+ sub read_string {
|
|
| 6171 |
+ my $class = ref $_[0] ? ref shift : shift; |
|
| 6172 |
+ my $self = bless [], $class; |
|
| 6173 |
+ my $string = $_[0]; |
|
| 6174 |
+ eval {
|
|
| 6175 |
+ unless ( defined $string ) {
|
|
| 6176 |
+ die \"Did not provide a string to load"; |
|
| 6177 |
+ } |
|
| 6169 | 6178 |
|
| 6170 |
- while () {
|
|
| 6171 |
- if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
|
|
| 6172 |
- return $1; |
|
| 6173 |
- } |
|
| 6174 |
- if (length $self->{rbuf} >= $self->{max_line_size}) {
|
|
| 6175 |
- croak(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}/);
|
|
| 6176 |
- } |
|
| 6177 |
- $self->can_read |
|
| 6178 |
- or croak(q/Timed out while waiting for socket to become ready for reading/); |
|
| 6179 |
- my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
|
|
| 6180 |
- if (defined $r) {
|
|
| 6181 |
- last unless $r; |
|
| 6182 |
- } |
|
| 6183 |
- elsif ($! != EINTR) {
|
|
| 6184 |
- croak(qq/Could not read from socket: '$!'/); |
|
| 6185 |
- } |
|
| 6186 |
- } |
|
| 6187 |
- croak(q/Unexpected end of stream while looking for line/); |
|
| 6188 |
- } |
|
| 6179 |
+ # Byte order marks |
|
| 6180 |
+ # NOTE: Keeping this here to educate maintainers |
|
| 6181 |
+ # my %BOM = ( |
|
| 6182 |
+ # "\357\273\277" => 'UTF-8', |
|
| 6183 |
+ # "\376\377" => 'UTF-16BE', |
|
| 6184 |
+ # "\377\376" => 'UTF-16LE', |
|
| 6185 |
+ # "\377\376\0\0" => 'UTF-32LE' |
|
| 6186 |
+ # "\0\0\376\377" => 'UTF-32BE', |
|
| 6187 |
+ # ); |
|
| 6188 |
+ if ( $string =~ /^(?:\376\377|\377\376|\377\376\0\0|\0\0\376\377)/ ) {
|
|
| 6189 |
+ die \"Stream has a non UTF-8 BOM"; |
|
| 6190 |
+ } else {
|
|
| 6191 |
+ # Strip UTF-8 bom if found, we'll just ignore it |
|
| 6192 |
+ $string =~ s/^\357\273\277//; |
|
| 6193 |
+ } |
|
| 6189 | 6194 |
|
| 6190 |
- sub read_header_lines {
|
|
| 6191 |
- @_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/); |
|
| 6192 |
- my ($self, $headers) = @_; |
|
| 6193 |
- $headers ||= {};
|
|
| 6194 |
- my $lines = 0; |
|
| 6195 |
- my $val; |
|
| 6195 |
+ # Try to decode as utf8 |
|
| 6196 |
+ utf8::decode($string) if HAVE_UTF8; |
|
| 6196 | 6197 |
|
| 6197 |
- while () {
|
|
| 6198 |
- my $line = $self->readline; |
|
| 6198 |
+ # Check for some special cases |
|
| 6199 |
+ return $self unless length $string; |
|
| 6200 |
+ unless ( $string =~ /[\012\015]+\z/ ) {
|
|
| 6201 |
+ die \"Stream does not end with newline character"; |
|
| 6202 |
+ } |
|
| 6199 | 6203 |
|
| 6200 |
- if (++$lines >= $self->{max_header_lines}) {
|
|
| 6201 |
- croak(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}/);
|
|
| 6202 |
- } |
|
| 6203 |
- elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
|
|
| 6204 |
- my ($field_name) = lc $1; |
|
| 6205 |
- if (exists $headers->{$field_name}) {
|
|
| 6206 |
- for ($headers->{$field_name}) {
|
|
| 6207 |
- $_ = [$_] unless ref $_ eq "ARRAY"; |
|
| 6208 |
- push @$_, $2; |
|
| 6209 |
- $val = \$_->[-1]; |
|
| 6210 |
- } |
|
| 6211 |
- } |
|
| 6212 |
- else {
|
|
| 6213 |
- $val = \($headers->{$field_name} = $2);
|
|
| 6214 |
- } |
|
| 6215 |
- } |
|
| 6216 |
- elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
|
|
| 6217 |
- $val |
|
| 6218 |
- or croak(q/Unexpected header continuation line/); |
|
| 6219 |
- next unless length $1; |
|
| 6220 |
- $$val .= ' ' if length $$val; |
|
| 6221 |
- $$val .= $1; |
|
| 6222 |
- } |
|
| 6223 |
- elsif ($line =~ /\A \x0D?\x0A \z/x) {
|
|
| 6224 |
- last; |
|
| 6225 |
- } |
|
| 6226 |
- else {
|
|
| 6227 |
- croak(q/Malformed header line: / . $Printable->($line)); |
|
| 6228 |
- } |
|
| 6229 |
- } |
|
| 6230 |
- return $headers; |
|
| 6231 |
- } |
|
| 6204 |
+ # Split the file into lines |
|
| 6205 |
+ my @lines = grep { ! /^\s*(?:\#.*)?\z/ }
|
|
| 6206 |
+ split /(?:\015{1,2}\012|\015|\012)/, $string;
|
|
| 6232 | 6207 |
|
| 6233 |
- sub write_request {
|
|
| 6234 |
- @_ == 2 || croak(q/Usage: $handle->write_request(request)/); |
|
| 6235 |
- my($self, $request) = @_; |
|
| 6236 |
- $self->write_request_header(@{$request}{qw/method uri headers/});
|
|
| 6237 |
- $self->write_body($request) if $request->{cb};
|
|
| 6238 |
- return; |
|
| 6239 |
- } |
|
| 6208 |
+ # Strip the initial YAML header |
|
| 6209 |
+ @lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines; |
|
| 6240 | 6210 |
|
| 6241 |
- my %HeaderCase = ( |
|
| 6242 |
- 'content-md5' => 'Content-MD5', |
|
| 6243 |
- 'etag' => 'ETag', |
|
| 6244 |
- 'te' => 'TE', |
|
| 6245 |
- 'www-authenticate' => 'WWW-Authenticate', |
|
| 6246 |
- 'x-xss-protection' => 'X-XSS-Protection', |
|
| 6247 |
- ); |
|
| 6211 |
+ # A nibbling parser |
|
| 6212 |
+ while ( @lines ) {
|
|
| 6213 |
+ # Do we have a document header? |
|
| 6214 |
+ if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
|
|
| 6215 |
+ # Handle scalar documents |
|
| 6216 |
+ shift @lines; |
|
| 6217 |
+ if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {
|
|
| 6218 |
+ push @$self, $self->_read_scalar( "$1", [ undef ], \@lines ); |
|
| 6219 |
+ next; |
|
| 6220 |
+ } |
|
| 6221 |
+ } |
|
| 6248 | 6222 |
|
| 6249 |
- sub write_header_lines {
|
|
| 6250 |
- (@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/); |
|
| 6251 |
- my($self, $headers) = @_; |
|
| 6223 |
+ if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
| 6224 |
+ # A naked document |
|
| 6225 |
+ push @$self, undef; |
|
| 6226 |
+ while ( @lines and $lines[0] !~ /^---/ ) {
|
|
| 6227 |
+ shift @lines; |
|
| 6228 |
+ } |
|
| 6252 | 6229 |
|
| 6253 |
- my $buf = ''; |
|
| 6254 |
- while (my ($k, $v) = each %$headers) {
|
|
| 6255 |
- my $field_name = lc $k; |
|
| 6256 |
- if (exists $HeaderCase{$field_name}) {
|
|
| 6257 |
- $field_name = $HeaderCase{$field_name};
|
|
| 6258 |
- } |
|
| 6259 |
- else {
|
|
| 6260 |
- $field_name =~ /\A $Token+ \z/xo |
|
| 6261 |
- or croak(q/Invalid HTTP header field name: / . $Printable->($field_name)); |
|
| 6262 |
- $field_name =~ s/\b(\w)/\u$1/g; |
|
| 6263 |
- $HeaderCase{lc $field_name} = $field_name;
|
|
| 6264 |
- } |
|
| 6265 |
- for (ref $v eq 'ARRAY' ? @$v : $v) {
|
|
| 6266 |
- /[^\x0D\x0A]/ |
|
| 6267 |
- or croak(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_)); |
|
| 6268 |
- $buf .= "$field_name: $_\x0D\x0A"; |
|
| 6269 |
- } |
|
| 6270 |
- } |
|
| 6271 |
- $buf .= "\x0D\x0A"; |
|
| 6272 |
- return $self->write($buf); |
|
| 6273 |
- } |
|
| 6230 |
+ } elsif ( $lines[0] =~ /^\s*\-/ ) {
|
|
| 6231 |
+ # An array at the root |
|
| 6232 |
+ my $document = [ ]; |
|
| 6233 |
+ push @$self, $document; |
|
| 6234 |
+ $self->_read_array( $document, [ 0 ], \@lines ); |
|
| 6274 | 6235 |
|
| 6275 |
- sub read_body {
|
|
| 6276 |
- @_ == 3 || croak(q/Usage: $handle->read_body(callback, response)/); |
|
| 6277 |
- my ($self, $cb, $response) = @_; |
|
| 6278 |
- my $te = $response->{headers}{'transfer-encoding'} || '';
|
|
| 6279 |
- if ( grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ) {
|
|
| 6280 |
- $self->read_chunked_body($cb, $response); |
|
| 6281 |
- } |
|
| 6282 |
- else {
|
|
| 6283 |
- $self->read_content_body($cb, $response); |
|
| 6284 |
- } |
|
| 6285 |
- return; |
|
| 6286 |
- } |
|
| 6287 |
- |
|
| 6288 |
- sub write_body {
|
|
| 6289 |
- @_ == 2 || croak(q/Usage: $handle->write_body(request)/); |
|
| 6290 |
- my ($self, $request) = @_; |
|
| 6291 |
- if ($request->{headers}{'content-length'}) {
|
|
| 6292 |
- return $self->write_content_body($request); |
|
| 6293 |
- } |
|
| 6294 |
- else {
|
|
| 6295 |
- return $self->write_chunked_body($request); |
|
| 6296 |
- } |
|
| 6297 |
- } |
|
| 6298 |
- |
|
| 6299 |
- sub read_content_body {
|
|
| 6300 |
- @_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/); |
|
| 6301 |
- my ($self, $cb, $response, $content_length) = @_; |
|
| 6302 |
- $content_length ||= $response->{headers}{'content-length'};
|
|
| 6236 |
+ } elsif ( $lines[0] =~ /^(\s*)\S/ ) {
|
|
| 6237 |
+ # A hash at the root |
|
| 6238 |
+ my $document = { };
|
|
| 6239 |
+ push @$self, $document; |
|
| 6240 |
+ $self->_read_hash( $document, [ length($1) ], \@lines ); |
|
| 6303 | 6241 |
|
| 6304 |
- if ( $content_length ) {
|
|
| 6305 |
- my $len = $content_length; |
|
| 6306 |
- while ($len > 0) {
|
|
| 6307 |
- my $read = ($len > BUFSIZE) ? BUFSIZE : $len; |
|
| 6308 |
- $cb->($self->read($read, 0), $response); |
|
| 6309 |
- $len -= $read; |
|
| 6310 |
- } |
|
| 6311 |
- } |
|
| 6312 |
- else {
|
|
| 6313 |
- my $chunk; |
|
| 6314 |
- $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) ); |
|
| 6315 |
- } |
|
| 6242 |
+ } else {
|
|
| 6243 |
+ die \"CPAN::Meta::YAML failed to classify the line '$lines[0]'"; |
|
| 6244 |
+ } |
|
| 6245 |
+ } |
|
| 6246 |
+ }; |
|
| 6247 |
+ if ( ref $@ eq 'SCALAR' ) {
|
|
| 6248 |
+ return $self->_error(${$@});
|
|
| 6249 |
+ } elsif ( $@ ) {
|
|
| 6250 |
+ require Carp; |
|
| 6251 |
+ Carp::croak($@); |
|
| 6252 |
+ } |
|
| 6316 | 6253 |
|
| 6317 |
- return; |
|
| 6254 |
+ return $self; |
|
| 6318 | 6255 |
} |
| 6319 | 6256 |
|
| 6320 |
- sub write_content_body {
|
|
| 6321 |
- @_ == 2 || croak(q/Usage: $handle->write_content_body(request)/); |
|
| 6322 |
- my ($self, $request) = @_; |
|
| 6323 |
- |
|
| 6324 |
- my ($len, $content_length) = (0, $request->{headers}{'content-length'});
|
|
| 6325 |
- while () {
|
|
| 6326 |
- my $data = $request->{cb}->();
|
|
| 6327 |
- |
|
| 6328 |
- defined $data && length $data |
|
| 6329 |
- or last; |
|
| 6257 |
+ # Deparse a scalar string to the actual scalar |
|
| 6258 |
+ sub _read_scalar {
|
|
| 6259 |
+ my ($self, $string, $indent, $lines) = @_; |
|
| 6330 | 6260 |
|
| 6331 |
- if ( $] ge '5.008' ) {
|
|
| 6332 |
- utf8::downgrade($data, 1) |
|
| 6333 |
- or croak(q/Wide character in write_content()/); |
|
| 6334 |
- } |
|
| 6261 |
+ # Trim trailing whitespace |
|
| 6262 |
+ $string =~ s/\s*\z//; |
|
| 6335 | 6263 |
|
| 6336 |
- $len += $self->write($data); |
|
| 6337 |
- } |
|
| 6264 |
+ # Explitic null/undef |
|
| 6265 |
+ return undef if $string eq '~'; |
|
| 6338 | 6266 |
|
| 6339 |
- $len == $content_length |
|
| 6340 |
- or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/); |
|
| 6267 |
+ # Single quote |
|
| 6268 |
+ if ( $string =~ /^\'(.*?)\'(?:\s+\#.*)?\z/ ) {
|
|
| 6269 |
+ return '' unless defined $1; |
|
| 6270 |
+ $string = $1; |
|
| 6271 |
+ $string =~ s/\'\'/\'/g; |
|
| 6272 |
+ return $string; |
|
| 6273 |
+ } |
|
| 6341 | 6274 |
|
| 6342 |
- return $len; |
|
| 6343 |
- } |
|
| 6275 |
+ # Double quote. |
|
| 6276 |
+ # The commented out form is simpler, but overloaded the Perl regex |
|
| 6277 |
+ # engine due to recursion and backtracking problems on strings |
|
| 6278 |
+ # larger than 32,000ish characters. Keep it for reference purposes. |
|
| 6279 |
+ # if ( $string =~ /^\"((?:\\.|[^\"])*)\"\z/ ) {
|
|
| 6280 |
+ if ( $string =~ /^\"([^\\"]*(?:\\.[^\\"]*)*)\"(?:\s+\#.*)?\z/ ) {
|
|
| 6281 |
+ # Reusing the variable is a little ugly, |
|
| 6282 |
+ # but avoids a new variable and a string copy. |
|
| 6283 |
+ $string = $1; |
|
| 6284 |
+ $string =~ s/\\"/"/g; |
|
| 6285 |
+ $string =~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}/gex;
|
|
| 6286 |
+ return $string; |
|
| 6287 |
+ } |
|
| 6344 | 6288 |
|
| 6345 |
- sub read_chunked_body {
|
|
| 6346 |
- @_ == 3 || croak(q/Usage: $handle->read_chunked_body(callback, $response)/); |
|
| 6347 |
- my ($self, $cb, $response) = @_; |
|
| 6289 |
+ # Special cases |
|
| 6290 |
+ if ( $string =~ /^[\'\"!&]/ ) {
|
|
| 6291 |
+ die \"CPAN::Meta::YAML does not support a feature in line '$string'"; |
|
| 6292 |
+ } |
|
| 6293 |
+ return {} if $string =~ /^{}(?:\s+\#.*)?\z/;
|
|
| 6294 |
+ return [] if $string =~ /^\[\](?:\s+\#.*)?\z/; |
|
| 6348 | 6295 |
|
| 6349 |
- while () {
|
|
| 6350 |
- my $head = $self->readline; |
|
| 6296 |
+ # Regular unquoted string |
|
| 6297 |
+ if ( $string !~ /^[>|]/ ) {
|
|
| 6298 |
+ if ( |
|
| 6299 |
+ $string =~ /^(?:-(?:\s|$)|[\@\%\`])/ |
|
| 6300 |
+ or |
|
| 6301 |
+ $string =~ /:(?:\s|$)/ |
|
| 6302 |
+ ) {
|
|
| 6303 |
+ die \"CPAN::Meta::YAML found illegal characters in plain scalar: '$string'"; |
|
| 6304 |
+ } |
|
| 6305 |
+ $string =~ s/\s+#.*\z//; |
|
| 6306 |
+ return $string; |
|
| 6307 |
+ } |
|
| 6351 | 6308 |
|
| 6352 |
- $head =~ /\A ([A-Fa-f0-9]+)/x |
|
| 6353 |
- or croak(q/Malformed chunk head: / . $Printable->($head)); |
|
| 6309 |
+ # Error |
|
| 6310 |
+ die \"CPAN::Meta::YAML failed to find multi-line scalar content" unless @$lines; |
|
| 6354 | 6311 |
|
| 6355 |
- my $len = hex($1) |
|
| 6356 |
- or last; |
|
| 6312 |
+ # Check the indent depth |
|
| 6313 |
+ $lines->[0] =~ /^(\s*)/; |
|
| 6314 |
+ $indent->[-1] = length("$1");
|
|
| 6315 |
+ if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
|
|
| 6316 |
+ die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; |
|
| 6317 |
+ } |
|
| 6357 | 6318 |
|
| 6358 |
- $self->read_content_body($cb, $response, $len); |
|
| 6319 |
+ # Pull the lines |
|
| 6320 |
+ my @multiline = (); |
|
| 6321 |
+ while ( @$lines ) {
|
|
| 6322 |
+ $lines->[0] =~ /^(\s*)/; |
|
| 6323 |
+ last unless length($1) >= $indent->[-1]; |
|
| 6324 |
+ push @multiline, substr(shift(@$lines), length($1)); |
|
| 6325 |
+ } |
|
| 6359 | 6326 |
|
| 6360 |
- $self->read(2) eq "\x0D\x0A" |
|
| 6361 |
- or croak(q/Malformed chunk: missing CRLF after chunk data/); |
|
| 6362 |
- } |
|
| 6363 |
- $self->read_header_lines($response->{headers});
|
|
| 6364 |
- return; |
|
| 6327 |
+ my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n"; |
|
| 6328 |
+ my $t = (substr($string, 1, 1) eq '-') ? '' : "\n"; |
|
| 6329 |
+ return join( $j, @multiline ) . $t; |
|
| 6365 | 6330 |
} |
| 6366 | 6331 |
|
| 6367 |
- sub write_chunked_body {
|
|
| 6368 |
- @_ == 2 || croak(q/Usage: $handle->write_chunked_body(request)/); |
|
| 6369 |
- my ($self, $request) = @_; |
|
| 6370 |
- |
|
| 6371 |
- my $len = 0; |
|
| 6372 |
- while () {
|
|
| 6373 |
- my $data = $request->{cb}->();
|
|
| 6374 |
- |
|
| 6375 |
- defined $data && length $data |
|
| 6376 |
- or last; |
|
| 6332 |
+ # Parse an array |
|
| 6333 |
+ sub _read_array {
|
|
| 6334 |
+ my ($self, $array, $indent, $lines) = @_; |
|
| 6377 | 6335 |
|
| 6378 |
- if ( $] ge '5.008' ) {
|
|
| 6379 |
- utf8::downgrade($data, 1) |
|
| 6380 |
- or croak(q/Wide character in write_chunked_body()/); |
|
| 6381 |
- } |
|
| 6336 |
+ while ( @$lines ) {
|
|
| 6337 |
+ # Check for a new document |
|
| 6338 |
+ if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
| 6339 |
+ while ( @$lines and $lines->[0] !~ /^---/ ) {
|
|
| 6340 |
+ shift @$lines; |
|
| 6341 |
+ } |
|
| 6342 |
+ return 1; |
|
| 6343 |
+ } |
|
| 6382 | 6344 |
|
| 6383 |
- $len += length $data; |
|
| 6345 |
+ # Check the indent level |
|
| 6346 |
+ $lines->[0] =~ /^(\s*)/; |
|
| 6347 |
+ if ( length($1) < $indent->[-1] ) {
|
|
| 6348 |
+ return 1; |
|
| 6349 |
+ } elsif ( length($1) > $indent->[-1] ) {
|
|
| 6350 |
+ die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; |
|
| 6351 |
+ } |
|
| 6384 | 6352 |
|
| 6385 |
- my $chunk = sprintf '%X', length $data; |
|
| 6386 |
- $chunk .= "\x0D\x0A"; |
|
| 6387 |
- $chunk .= $data; |
|
| 6388 |
- $chunk .= "\x0D\x0A"; |
|
| 6353 |
+ if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
|
|
| 6354 |
+ # Inline nested hash |
|
| 6355 |
+ my $indent2 = length("$1");
|
|
| 6356 |
+ $lines->[0] =~ s/-/ /; |
|
| 6357 |
+ push @$array, { };
|
|
| 6358 |
+ $self->_read_hash( $array->[-1], [ @$indent, $indent2 ], $lines ); |
|
| 6389 | 6359 |
|
| 6390 |
- $self->write($chunk); |
|
| 6391 |
- } |
|
| 6392 |
- $self->write("0\x0D\x0A");
|
|
| 6393 |
- $self->write_header_lines($request->{trailer_cb}->())
|
|
| 6394 |
- if ref $request->{trailer_cb} eq 'CODE';
|
|
| 6395 |
- return $len; |
|
| 6396 |
- } |
|
| 6360 |
+ } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
|
|
| 6361 |
+ # Array entry with a value |
|
| 6362 |
+ shift @$lines; |
|
| 6363 |
+ push @$array, $self->_read_scalar( "$2", [ @$indent, undef ], $lines ); |
|
| 6397 | 6364 |
|
| 6398 |
- sub read_response_header {
|
|
| 6399 |
- @_ == 1 || croak(q/Usage: $handle->read_response_header()/); |
|
| 6400 |
- my ($self) = @_; |
|
| 6365 |
+ } elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {
|
|
| 6366 |
+ shift @$lines; |
|
| 6367 |
+ unless ( @$lines ) {
|
|
| 6368 |
+ push @$array, undef; |
|
| 6369 |
+ return 1; |
|
| 6370 |
+ } |
|
| 6371 |
+ if ( $lines->[0] =~ /^(\s*)\-/ ) {
|
|
| 6372 |
+ my $indent2 = length("$1");
|
|
| 6373 |
+ if ( $indent->[-1] == $indent2 ) {
|
|
| 6374 |
+ # Null array entry |
|
| 6375 |
+ push @$array, undef; |
|
| 6376 |
+ } else {
|
|
| 6377 |
+ # Naked indenter |
|
| 6378 |
+ push @$array, [ ]; |
|
| 6379 |
+ $self->_read_array( $array->[-1], [ @$indent, $indent2 ], $lines ); |
|
| 6380 |
+ } |
|
| 6401 | 6381 |
|
| 6402 |
- my $line = $self->readline; |
|
| 6382 |
+ } elsif ( $lines->[0] =~ /^(\s*)\S/ ) {
|
|
| 6383 |
+ push @$array, { };
|
|
| 6384 |
+ $self->_read_hash( $array->[-1], [ @$indent, length("$1") ], $lines );
|
|
| 6403 | 6385 |
|
| 6404 |
- $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
|
|
| 6405 |
- or croak(q/Malformed Status-Line: / . $Printable->($line)); |
|
| 6386 |
+ } else {
|
|
| 6387 |
+ die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; |
|
| 6388 |
+ } |
|
| 6406 | 6389 |
|
| 6407 |
- my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4); |
|
| 6390 |
+ } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
|
|
| 6391 |
+ # This is probably a structure like the following... |
|
| 6392 |
+ # --- |
|
| 6393 |
+ # foo: |
|
| 6394 |
+ # - list |
|
| 6395 |
+ # bar: value |
|
| 6396 |
+ # |
|
| 6397 |
+ # ... so lets return and let the hash parser handle it |
|
| 6398 |
+ return 1; |
|
| 6408 | 6399 |
|
| 6409 |
- croak (qq/Unsupported HTTP protocol: $protocol/) |
|
| 6410 |
- unless $version =~ /0*1\.0*[01]/; |
|
| 6400 |
+ } else {
|
|
| 6401 |
+ die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; |
|
| 6402 |
+ } |
|
| 6403 |
+ } |
|
| 6411 | 6404 |
|
| 6412 |
- return {
|
|
| 6413 |
- status => $status, |
|
| 6414 |
- reason => $reason, |
|
| 6415 |
- headers => $self->read_header_lines, |
|
| 6416 |
- protocol => $protocol, |
|
| 6417 |
- }; |
|
| 6405 |
+ return 1; |
|
| 6418 | 6406 |
} |
| 6419 | 6407 |
|
| 6420 |
- sub write_request_header {
|
|
| 6421 |
- @_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/); |
|
| 6422 |
- my ($self, $method, $request_uri, $headers) = @_; |
|
| 6423 |
- |
|
| 6424 |
- return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
|
|
| 6425 |
- + $self->write_header_lines($headers); |
|
| 6426 |
- } |
|
| 6408 |
+ # Parse an array |
|
| 6409 |
+ sub _read_hash {
|
|
| 6410 |
+ my ($self, $hash, $indent, $lines) = @_; |
|
| 6427 | 6411 |
|
| 6428 |
- sub _do_timeout {
|
|
| 6429 |
- my ($self, $type, $timeout) = @_; |
|
| 6430 |
- $timeout = $self->{timeout}
|
|
| 6431 |
- unless defined $timeout && $timeout >= 0; |
|
| 6432 |
- |
|
| 6433 |
- my $fd = fileno $self->{fh};
|
|
| 6434 |
- defined $fd && $fd >= 0 |
|
| 6435 |
- or croak(q/select(2): 'Bad file descriptor'/); |
|
| 6412 |
+ while ( @$lines ) {
|
|
| 6413 |
+ # Check for a new document |
|
| 6414 |
+ if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
|
|
| 6415 |
+ while ( @$lines and $lines->[0] !~ /^---/ ) {
|
|
| 6416 |
+ shift @$lines; |
|
| 6417 |
+ } |
|
| 6418 |
+ return 1; |
|
| 6419 |
+ } |
|
| 6436 | 6420 |
|
| 6437 |
- my $initial = time; |
|
| 6438 |
- my $pending = $timeout; |
|
| 6439 |
- my $nfound; |
|
| 6421 |
+ # Check the indent level |
|
| 6422 |
+ $lines->[0] =~ /^(\s*)/; |
|
| 6423 |
+ if ( length($1) < $indent->[-1] ) {
|
|
| 6424 |
+ return 1; |
|
| 6425 |
+ } elsif ( length($1) > $indent->[-1] ) {
|
|
| 6426 |
+ die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; |
|
| 6427 |
+ } |
|
| 6440 | 6428 |
|
| 6441 |
- vec(my $fdset = '', $fd, 1) = 1; |
|
| 6429 |
+ # Get the key |
|
| 6430 |
+ unless ( $lines->[0] =~ s/^\s*([^\'\" ][^\n]*?)\s*:(\s+(?:\#.*)?|$)// ) {
|
|
| 6431 |
+ if ( $lines->[0] =~ /^\s*[?\'\"]/ ) {
|
|
| 6432 |
+ die \"CPAN::Meta::YAML does not support a feature in line '$lines->[0]'"; |
|
| 6433 |
+ } |
|
| 6434 |
+ die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; |
|
| 6435 |
+ } |
|
| 6436 |
+ my $key = $1; |
|
| 6442 | 6437 |
|
| 6443 |
- while () {
|
|
| 6444 |
- $nfound = ($type eq 'read') |
|
| 6445 |
- ? select($fdset, undef, undef, $pending) |
|
| 6446 |
- : select(undef, $fdset, undef, $pending) ; |
|
| 6447 |
- if ($nfound == -1) {
|
|
| 6448 |
- $! == EINTR |
|
| 6449 |
- or croak(qq/select(2): '$!'/); |
|
| 6450 |
- redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; |
|
| 6451 |
- $nfound = 0; |
|
| 6452 |
- } |
|
| 6453 |
- last; |
|
| 6454 |
- } |
|
| 6455 |
- $! = 0; |
|
| 6456 |
- return $nfound; |
|
| 6457 |
- } |
|
| 6438 |
+ # Do we have a value? |
|
| 6439 |
+ if ( length $lines->[0] ) {
|
|
| 6440 |
+ # Yes |
|
| 6441 |
+ $hash->{$key} = $self->_read_scalar( shift(@$lines), [ @$indent, undef ], $lines );
|
|
| 6442 |
+ } else {
|
|
| 6443 |
+ # An indent |
|
| 6444 |
+ shift @$lines; |
|
| 6445 |
+ unless ( @$lines ) {
|
|
| 6446 |
+ $hash->{$key} = undef;
|
|
| 6447 |
+ return 1; |
|
| 6448 |
+ } |
|
| 6449 |
+ if ( $lines->[0] =~ /^(\s*)-/ ) {
|
|
| 6450 |
+ $hash->{$key} = [];
|
|
| 6451 |
+ $self->_read_array( $hash->{$key}, [ @$indent, length($1) ], $lines );
|
|
| 6452 |
+ } elsif ( $lines->[0] =~ /^(\s*)./ ) {
|
|
| 6453 |
+ my $indent2 = length("$1");
|
|
| 6454 |
+ if ( $indent->[-1] >= $indent2 ) {
|
|
| 6455 |
+ # Null hash entry |
|
| 6456 |
+ $hash->{$key} = undef;
|
|
| 6457 |
+ } else {
|
|
| 6458 |
+ $hash->{$key} = {};
|
|
| 6459 |
+ $self->_read_hash( $hash->{$key}, [ @$indent, length($1) ], $lines );
|
|
| 6460 |
+ } |
|
| 6461 |
+ } |
|
| 6462 |
+ } |
|
| 6463 |
+ } |
|
| 6458 | 6464 |
|
| 6459 |
- sub can_read {
|
|
| 6460 |
- @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/); |
|
| 6461 |
- my $self = shift; |
|
| 6462 |
- return $self->_do_timeout('read', @_)
|
|
| 6465 |
+ return 1; |
|
| 6463 | 6466 |
} |
| 6464 | 6467 |
|
| 6465 |
- sub can_write {
|
|
| 6466 |
- @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/); |
|
| 6467 |
- my $self = shift; |
|
| 6468 |
- return $self->_do_timeout('write', @_)
|
|
| 6469 |
- } |
|
| 6468 |
+ # Save an object to a file |
|
| 6469 |
+ sub write {
|
|
| 6470 |
+ my $self = shift; |
|
| 6471 |
+ my $file = shift or return $self->_error('No file name provided');
|
|
| 6470 | 6472 |
|
| 6471 |
- 1; |
|
| 6473 |
+ # Write it to the file |
|
| 6474 |
+ open( CFG, '>' . $file ) or return $self->_error( |
|
| 6475 |
+ "Failed to open file '$file' for writing: $!" |
|
| 6476 |
+ ); |
|
| 6477 |
+ print CFG $self->write_string; |
|
| 6478 |
+ close CFG; |
|
| 6472 | 6479 |
|
| 6480 |
+ return 1; |
|
| 6481 |
+ } |
|
| 6473 | 6482 |
|
| 6483 |
+ # Save an object to a string |
|
| 6484 |
+ sub write_string {
|
|
| 6485 |
+ my $self = shift; |
|
| 6486 |
+ return '' unless @$self; |
|
| 6474 | 6487 |
|
| 6475 |
- __END__ |
|
| 6476 |
- =pod |
|
| 6488 |
+ # Iterate over the documents |
|
| 6489 |
+ my $indent = 0; |
|
| 6490 |
+ my @lines = (); |
|
| 6491 |
+ foreach my $cursor ( @$self ) {
|
|
| 6492 |
+ push @lines, '---'; |
|
| 6477 | 6493 |
|
| 6478 |
-HTTP_TINY |
|
| 6479 |
- |
|
| 6480 |
-$fatpacked{"JSON/PP.pm"} = <<'JSON_PP';
|
|
| 6481 |
- package JSON::PP; |
|
| 6494 |
+ # An empty document |
|
| 6495 |
+ if ( ! defined $cursor ) {
|
|
| 6496 |
+ # Do nothing |
|
| 6482 | 6497 |
|
| 6483 |
- # JSON-2.0 |
|
| 6498 |
+ # A scalar document |
|
| 6499 |
+ } elsif ( ! ref $cursor ) {
|
|
| 6500 |
+ $lines[-1] .= ' ' . $self->_write_scalar( $cursor, $indent ); |
|
| 6484 | 6501 |
|
| 6485 |
- use 5.005; |
|
| 6486 |
- use strict; |
|
| 6487 |
- use base qw(Exporter); |
|
| 6488 |
- use overload (); |
|
| 6502 |
+ # A list at the root |
|
| 6503 |
+ } elsif ( ref $cursor eq 'ARRAY' ) {
|
|
| 6504 |
+ unless ( @$cursor ) {
|
|
| 6505 |
+ $lines[-1] .= ' []'; |
|
| 6506 |
+ next; |
|
| 6507 |
+ } |
|
| 6508 |
+ push @lines, $self->_write_array( $cursor, $indent, {} );
|
|
| 6489 | 6509 |
|
| 6490 |
- use Carp (); |
|
| 6491 |
- use B (); |
|
| 6492 |
- #use Devel::Peek; |
|
| 6510 |
+ # A hash at the root |
|
| 6511 |
+ } elsif ( ref $cursor eq 'HASH' ) {
|
|
| 6512 |
+ unless ( %$cursor ) {
|
|
| 6513 |
+ $lines[-1] .= ' {}';
|
|
| 6514 |
+ next; |
|
| 6515 |
+ } |
|
| 6516 |
+ push @lines, $self->_write_hash( $cursor, $indent, {} );
|
|
| 6493 | 6517 |
|
| 6494 |
- $JSON::PP::VERSION = '2.27200'; |
|
| 6518 |
+ } else {
|
|
| 6519 |
+ Carp::croak("Cannot serialize " . ref($cursor));
|
|
| 6520 |
+ } |
|
| 6521 |
+ } |
|
| 6495 | 6522 |
|
| 6496 |
- @JSON::PP::EXPORT = qw(encode_json decode_json from_json to_json); |
|
| 6523 |
+ join '', map { "$_\n" } @lines;
|
|
| 6524 |
+ } |
|
| 6497 | 6525 |
|
| 6498 |
- # instead of hash-access, i tried index-access for speed. |
|
| 6499 |
- # but this method is not faster than what i expected. so it will be changed. |
|
| 6526 |
+ sub _write_scalar {
|
|
| 6527 |
+ my $string = $_[1]; |
|
| 6528 |
+ return '~' unless defined $string; |
|
| 6529 |
+ return "''" unless length $string; |
|
| 6530 |
+ if ( $string =~ /[\x00-\x08\x0b-\x0d\x0e-\x1f\"\'\n]/ ) {
|
|
| 6531 |
+ $string =~ s/\\/\\\\/g; |
|
| 6532 |
+ $string =~ s/"/\\"/g; |
|
| 6533 |
+ $string =~ s/\n/\\n/g; |
|
| 6534 |
+ $string =~ s/([\x00-\x1f])/\\$UNPRINTABLE[ord($1)]/g; |
|
| 6535 |
+ return qq|"$string"|; |
|
| 6536 |
+ } |
|
| 6537 |
+ if ( $string =~ /(?:^\W|\s|:\z)/ or $QUOTE{$string} ) {
|
|
| 6538 |
+ return "'$string'"; |
|
| 6539 |
+ } |
|
| 6540 |
+ return $string; |
|
| 6541 |
+ } |
|
| 6500 | 6542 |
|
| 6501 |
- use constant P_ASCII => 0; |
|
| 6502 |
- use constant P_LATIN1 => 1; |
|
| 6503 |
- use constant P_UTF8 => 2; |
|
| 6504 |
- use constant P_INDENT => 3; |
|
| 6505 |
- use constant P_CANONICAL => 4; |
|
| 6506 |
- use constant P_SPACE_BEFORE => 5; |
|
| 6507 |
- use constant P_SPACE_AFTER => 6; |
|
| 6508 |
- use constant P_ALLOW_NONREF => 7; |
|
| 6509 |
- use constant P_SHRINK => 8; |
|
| 6510 |
- use constant P_ALLOW_BLESSED => 9; |
|
| 6511 |
- use constant P_CONVERT_BLESSED => 10; |
|
| 6512 |
- use constant P_RELAXED => 11; |
|
| 6543 |
+ sub _write_array {
|
|
| 6544 |
+ my ($self, $array, $indent, $seen) = @_; |
|
| 6545 |
+ if ( $seen->{refaddr($array)}++ ) {
|
|
| 6546 |
+ die "CPAN::Meta::YAML does not support circular references"; |
|
| 6547 |
+ } |
|
| 6548 |
+ my @lines = (); |
|
| 6549 |
+ foreach my $el ( @$array ) {
|
|
| 6550 |
+ my $line = (' ' x $indent) . '-';
|
|
| 6551 |
+ my $type = ref $el; |
|
| 6552 |
+ if ( ! $type ) {
|
|
| 6553 |
+ $line .= ' ' . $self->_write_scalar( $el, $indent + 1 ); |
|
| 6554 |
+ push @lines, $line; |
|
| 6513 | 6555 |
|
| 6514 |
- use constant P_LOOSE => 12; |
|
| 6515 |
- use constant P_ALLOW_BIGNUM => 13; |
|
| 6516 |
- use constant P_ALLOW_BAREKEY => 14; |
|
| 6517 |
- use constant P_ALLOW_SINGLEQUOTE => 15; |
|
| 6518 |
- use constant P_ESCAPE_SLASH => 16; |
|
| 6519 |
- use constant P_AS_NONBLESSED => 17; |
|
| 6556 |
+ } elsif ( $type eq 'ARRAY' ) {
|
|
| 6557 |
+ if ( @$el ) {
|
|
| 6558 |
+ push @lines, $line; |
|
| 6559 |
+ push @lines, $self->_write_array( $el, $indent + 1, $seen ); |
|
| 6560 |
+ } else {
|
|
| 6561 |
+ $line .= ' []'; |
|
| 6562 |
+ push @lines, $line; |
|
| 6563 |
+ } |
|
| 6520 | 6564 |
|
| 6521 |
- use constant P_ALLOW_UNKNOWN => 18; |
|
| 6565 |
+ } elsif ( $type eq 'HASH' ) {
|
|
| 6566 |
+ if ( keys %$el ) {
|
|
| 6567 |
+ push @lines, $line; |
|
| 6568 |
+ push @lines, $self->_write_hash( $el, $indent + 1, $seen ); |
|
| 6569 |
+ } else {
|
|
| 6570 |
+ $line .= ' {}';
|
|
| 6571 |
+ push @lines, $line; |
|
| 6572 |
+ } |
|
| 6522 | 6573 |
|
| 6523 |
- use constant OLD_PERL => $] < 5.008 ? 1 : 0; |
|
| 6574 |
+ } else {
|
|
| 6575 |
+ die "CPAN::Meta::YAML does not support $type references"; |
|
| 6576 |
+ } |
|
| 6577 |
+ } |
|
| 6524 | 6578 |
|
| 6525 |
- BEGIN {
|
|
| 6526 |
- my @xs_compati_bit_properties = qw( |
|
| 6527 |
- latin1 ascii utf8 indent canonical space_before space_after allow_nonref shrink |
|
| 6528 |
- allow_blessed convert_blessed relaxed allow_unknown |
|
| 6529 |
- ); |
|
| 6530 |
- my @pp_bit_properties = qw( |
|
| 6531 |
- allow_singlequote allow_bignum loose |
|
| 6532 |
- allow_barekey escape_slash as_nonblessed |
|
| 6533 |
- ); |
|
| 6579 |
+ @lines; |
|
| 6580 |
+ } |
|
| 6534 | 6581 |
|
| 6535 |
- # Perl version check, Unicode handling is enable? |
|
| 6536 |
- # Helper module sets @JSON::PP::_properties. |
|
| 6537 |
- if ($] < 5.008 ) {
|
|
| 6538 |
- my $helper = $] >= 5.006 ? 'JSON::PP::Compat5006' : 'JSON::PP::Compat5005'; |
|
| 6539 |
- eval qq| require $helper |; |
|
| 6540 |
- if ($@) { Carp::croak $@; }
|
|
| 6541 |
- } |
|
| 6582 |
+ sub _write_hash {
|
|
| 6583 |
+ my ($self, $hash, $indent, $seen) = @_; |
|
| 6584 |
+ if ( $seen->{refaddr($hash)}++ ) {
|
|
| 6585 |
+ die "CPAN::Meta::YAML does not support circular references"; |
|
| 6586 |
+ } |
|
| 6587 |
+ my @lines = (); |
|
| 6588 |
+ foreach my $name ( sort keys %$hash ) {
|
|
| 6589 |
+ my $el = $hash->{$name};
|
|
| 6590 |
+ my $line = (' ' x $indent) . "$name:";
|
|
| 6591 |
+ my $type = ref $el; |
|
| 6592 |
+ if ( ! $type ) {
|
|
| 6593 |
+ $line .= ' ' . $self->_write_scalar( $el, $indent + 1 ); |
|
| 6594 |
+ push @lines, $line; |
|
| 6542 | 6595 |
|
| 6543 |
- for my $name (@xs_compati_bit_properties, @pp_bit_properties) {
|
|
| 6544 |
- my $flag_name = 'P_' . uc($name); |
|
| 6596 |
+ } elsif ( $type eq 'ARRAY' ) {
|
|
| 6597 |
+ if ( @$el ) {
|
|
| 6598 |
+ push @lines, $line; |
|
| 6599 |
+ push @lines, $self->_write_array( $el, $indent + 1, $seen ); |
|
| 6600 |
+ } else {
|
|
| 6601 |
+ $line .= ' []'; |
|
| 6602 |
+ push @lines, $line; |
|
| 6603 |
+ } |
|
| 6545 | 6604 |
|
| 6546 |
- eval qq/ |
|
| 6547 |
- sub $name {
|
|
| 6548 |
- my \$enable = defined \$_[1] ? \$_[1] : 1; |
|
| 6549 |
- |
|
| 6550 |
- if (\$enable) {
|
|
| 6551 |
- \$_[0]->{PROPS}->[$flag_name] = 1;
|
|
| 6552 |
- } |
|
| 6553 |
- else {
|
|
| 6554 |
- \$_[0]->{PROPS}->[$flag_name] = 0;
|
|
| 6555 |
- } |
|
| 6605 |
+ } elsif ( $type eq 'HASH' ) {
|
|
| 6606 |
+ if ( keys %$el ) {
|
|
| 6607 |
+ push @lines, $line; |
|
| 6608 |
+ push @lines, $self->_write_hash( $el, $indent + 1, $seen ); |
|
| 6609 |
+ } else {
|
|
| 6610 |
+ $line .= ' {}';
|
|
| 6611 |
+ push @lines, $line; |
|
| 6612 |
+ } |
|
| 6556 | 6613 |
|
| 6557 |
- \$_[0]; |
|
| 6558 |
- } |
|
| 6614 |
+ } else {
|
|
| 6615 |
+ die "CPAN::Meta::YAML does not support $type references"; |
|
| 6616 |
+ } |
|
| 6617 |
+ } |
|
| 6559 | 6618 |
|
| 6560 |
- sub get_$name {
|
|
| 6561 |
- \$_[0]->{PROPS}->[$flag_name] ? 1 : '';
|
|
| 6562 |
- } |
|
| 6563 |
- /; |
|
| 6564 |
- } |
|
| 6619 |
+ @lines; |
|
| 6620 |
+ } |
|
| 6565 | 6621 |
|
| 6622 |
+ # Set error |
|
| 6623 |
+ sub _error {
|
|
| 6624 |
+ $CPAN::Meta::YAML::errstr = $_[1]; |
|
| 6625 |
+ undef; |
|
| 6566 | 6626 |
} |
| 6567 | 6627 |
|
| 6628 |
+ # Retrieve error |
|
| 6629 |
+ sub errstr {
|
|
| 6630 |
+ $CPAN::Meta::YAML::errstr; |
|
| 6631 |
+ } |
|
| 6568 | 6632 |
|
| 6569 | 6633 |
|
| 6570 |
- # Functions |
|
| 6571 | 6634 |
|
| 6572 |
- my %encode_allow_method |
|
| 6573 |
- = map {($_ => 1)} qw/utf8 pretty allow_nonref latin1 self_encode escape_slash
|
|
| 6574 |
- allow_blessed convert_blessed indent indent_length allow_bignum |
|
| 6575 |
- as_nonblessed |
|
| 6576 |
- /; |
|
| 6577 |
- my %decode_allow_method |
|
| 6578 |
- = map {($_ => 1)} qw/utf8 allow_nonref loose allow_singlequote allow_bignum
|
|
| 6579 |
- allow_barekey max_size relaxed/; |
|
| 6580 | 6635 |
|
| 6581 | 6636 |
|
| 6582 |
- my $JSON; # cache |
|
| 6637 |
+ ##################################################################### |
|
| 6638 |
+ # YAML Compatibility |
|
| 6583 | 6639 |
|
| 6584 |
- sub encode_json ($) { # encode
|
|
| 6585 |
- ($JSON ||= __PACKAGE__->new->utf8)->encode(@_); |
|
| 6640 |
+ sub Dump {
|
|
| 6641 |
+ CPAN::Meta::YAML->new(@_)->write_string; |
|
| 6586 | 6642 |
} |
| 6587 | 6643 |
|
| 6644 |
+ sub Load {
|
|
| 6645 |
+ my $self = CPAN::Meta::YAML->read_string(@_); |
|
| 6646 |
+ unless ( $self ) {
|
|
| 6647 |
+ Carp::croak("Failed to load YAML document from string");
|
|
| 6648 |
+ } |
|
| 6649 |
+ if ( wantarray ) {
|
|
| 6650 |
+ return @$self; |
|
| 6651 |
+ } else {
|
|
| 6652 |
+ # To match YAML.pm, return the last document |
|
| 6653 |
+ return $self->[-1]; |
|
| 6654 |
+ } |
|
| 6655 |
+ } |
|
| 6588 | 6656 |
|
| 6589 |
- sub decode_json { # decode
|
|
| 6590 |
- ($JSON ||= __PACKAGE__->new->utf8)->decode(@_); |
|
| 6657 |
+ BEGIN {
|
|
| 6658 |
+ *freeze = *Dump; |
|
| 6659 |
+ *thaw = *Load; |
|
| 6591 | 6660 |
} |
| 6592 | 6661 |
|
| 6593 |
- # Obsoleted |
|
| 6662 |
+ sub DumpFile {
|
|
| 6663 |
+ my $file = shift; |
|
| 6664 |
+ CPAN::Meta::YAML->new(@_)->write($file); |
|
| 6665 |
+ } |
|
| 6594 | 6666 |
|
| 6595 |
- sub to_json($) {
|
|
| 6596 |
- Carp::croak ("JSON::PP::to_json has been renamed to encode_json.");
|
|
| 6667 |
+ sub LoadFile {
|
|
| 6668 |
+ my $self = CPAN::Meta::YAML->read($_[0]); |
|
| 6669 |
+ unless ( $self ) {
|
|
| 6670 |
+ Carp::croak("Failed to load YAML document from '" . ($_[0] || '') . "'");
|
|
| 6671 |
+ } |
|
| 6672 |
+ if ( wantarray ) {
|
|
| 6673 |
+ return @$self; |
|
| 6674 |
+ } else {
|
|
| 6675 |
+ # Return only the last document to match YAML.pm, |
|
| 6676 |
+ return $self->[-1]; |
|
| 6677 |
+ } |
|
| 6597 | 6678 |
} |
| 6598 | 6679 |
|
| 6599 | 6680 |
|
| 6600 |
- sub from_json($) {
|
|
| 6601 |
- Carp::croak ("JSON::PP::from_json has been renamed to decode_json.");
|
|
| 6602 |
- } |
|
| 6603 | 6681 |
|
| 6604 | 6682 |
|
| 6605 |
- # Methods |
|
| 6606 | 6683 |
|
| 6607 |
- sub new {
|
|
| 6608 |
- my $class = shift; |
|
| 6609 |
- my $self = {
|
|
| 6610 |
- max_depth => 512, |
|
| 6611 |
- max_size => 0, |
|
| 6612 |
- indent => 0, |
|
| 6613 |
- FLAGS => 0, |
|
| 6614 |
- fallback => sub { encode_error('Invalid value. JSON can only reference.') },
|
|
| 6615 |
- indent_length => 3, |
|
| 6616 |
- }; |
|
| 6684 |
+ ##################################################################### |
|
| 6685 |
+ # Use Scalar::Util if possible, otherwise emulate it |
|
| 6617 | 6686 |
|
| 6618 |
- bless $self, $class; |
|
| 6687 |
+ BEGIN {
|
|
| 6688 |
+ local $@; |
|
| 6689 |
+ eval {
|
|
| 6690 |
+ require Scalar::Util; |
|
| 6691 |
+ }; |
|
| 6692 |
+ my $v = eval("$Scalar::Util::VERSION") || 0;
|
|
| 6693 |
+ if ( $@ or $v < 1.18 ) {
|
|
| 6694 |
+ eval <<'END_PERL'; |
|
| 6695 |
+ # Scalar::Util failed to load or too old |
|
| 6696 |
+ sub refaddr {
|
|
| 6697 |
+ my $pkg = ref($_[0]) or return undef; |
|
| 6698 |
+ if ( !! UNIVERSAL::can($_[0], 'can') ) {
|
|
| 6699 |
+ bless $_[0], 'Scalar::Util::Fake'; |
|
| 6700 |
+ } else {
|
|
| 6701 |
+ $pkg = undef; |
|
| 6702 |
+ } |
|
| 6703 |
+ "$_[0]" =~ /0x(\w+)/; |
|
| 6704 |
+ my $i = do { local $^W; hex $1 };
|
|
| 6705 |
+ bless $_[0], $pkg if defined $pkg; |
|
| 6706 |
+ $i; |
|
| 6707 |
+ } |
|
| 6708 |
+ END_PERL |
|
| 6709 |
+ } else {
|
|
| 6710 |
+ *refaddr = *Scalar::Util::refaddr; |
|
| 6711 |
+ } |
|
| 6619 | 6712 |
} |
| 6620 | 6713 |
|
| 6714 |
+ 1; |
|
| 6715 |
+ |
|
| 6621 | 6716 |
|
| 6622 |
- sub encode {
|
|
| 6623 |
- return $_[0]->PP_encode_json($_[1]); |
|
| 6624 |
- } |
|
| 6625 | 6717 |
|
| 6626 | 6718 |
|
| 6627 |
- sub decode {
|
|
| 6628 |
- return $_[0]->PP_decode_json($_[1], 0x00000000); |
|
| 6629 |
- } |
|
| 6719 |
+ __END__ |
|
| 6630 | 6720 |
|
| 6631 | 6721 |
|
| 6632 |
- sub decode_prefix {
|
|
| 6633 |
- return $_[0]->PP_decode_json($_[1], 0x00000001); |
|
| 6634 |
- } |
|
| 6722 |
+ # ABSTRACT: Read and write a subset of YAML for CPAN Meta files |
|
| 6635 | 6723 |
|
| 6636 | 6724 |
|
| 6637 |
- # accessor |
|
| 6725 |
+CPAN_META_YAML |
|
| 6726 |
+ |
|
| 6727 |
+$fatpacked{"File/pushd.pm"} = <<'FILE_PUSHD';
|
|
| 6728 |
+ use strict; |
|
| 6729 |
+ use warnings; |
|
| 6730 |
+ package File::pushd; |
|
| 6731 |
+ # ABSTRACT: change directory temporarily for a limited scope |
|
| 6732 |
+ our $VERSION = '1.004'; # VERSION |
|
| 6733 |
+ |
|
| 6734 |
+ our @EXPORT = qw( pushd tempd ); |
|
| 6735 |
+ our @ISA = qw( Exporter ); |
|
| 6736 |
+ |
|
| 6737 |
+ use Exporter; |
|
| 6738 |
+ use Carp; |
|
| 6739 |
+ use Cwd qw( cwd abs_path ); |
|
| 6740 |
+ use File::Path qw( rmtree ); |
|
| 6741 |
+ use File::Temp qw(); |
|
| 6742 |
+ use File::Spec; |
|
| 6638 | 6743 |
|
| 6744 |
+ use overload |
|
| 6745 |
+ q{""} => sub { File::Spec->canonpath( $_[0]->{_pushd} ) },
|
|
| 6746 |
+ fallback => 1; |
|
| 6639 | 6747 |
|
| 6640 |
- # pretty printing |
|
| 6748 |
+ #--------------------------------------------------------------------------# |
|
| 6749 |
+ # pushd() |
|
| 6750 |
+ #--------------------------------------------------------------------------# |
|
| 6641 | 6751 |
|
| 6642 |
- sub pretty {
|
|
| 6643 |
- my ($self, $v) = @_; |
|
| 6644 |
- my $enable = defined $v ? $v : 1; |
|
| 6752 |
+ sub pushd {
|
|
| 6753 |
+ my ($target_dir, $options) = @_; |
|
| 6754 |
+ $options->{untaint_pattern} ||= qr{^([-+@\w./]+)$};
|
|
| 6645 | 6755 |
|
| 6646 |
- if ($enable) { # indent_length(3) for JSON::XS compatibility
|
|
| 6647 |
- $self->indent(1)->indent_length(3)->space_before(1)->space_after(1); |
|
| 6756 |
+ my $tainted_orig = cwd; |
|
| 6757 |
+ my $orig; |
|
| 6758 |
+ if ( $tainted_orig =~ $options->{untaint_pattern} ) {
|
|
| 6759 |
+ $orig = $1; |
|
| 6648 | 6760 |
} |
| 6649 | 6761 |
else {
|
| 6650 |
- $self->indent(0)->space_before(0)->space_after(0); |
|
| 6762 |
+ $orig = $tainted_orig; |
|
| 6651 | 6763 |
} |
| 6652 | 6764 |
|
| 6653 |
- $self; |
|
| 6765 |
+ my $tainted_dest; |
|
| 6766 |
+ eval { $tainted_dest = $target_dir ? abs_path( $target_dir ) : $orig };
|
|
| 6767 |
+ croak "Can't locate directory $target_dir: $@" if $@; |
|
| 6768 |
+ |
|
| 6769 |
+ my $dest; |
|
| 6770 |
+ if ( $tainted_dest =~ $options->{untaint_pattern} ) {
|
|
| 6771 |
+ $dest = $1; |
|
| 6772 |
+ } |
|
| 6773 |
+ else {
|
|
| 6774 |
+ $dest = $tainted_dest; |
|
| 6775 |
+ } |
|
| 6776 |
+ |
|
| 6777 |
+ if ($dest ne $orig) {
|
|
| 6778 |
+ chdir $dest or croak "Can't chdir to $dest\: $!"; |
|
| 6779 |
+ } |
|
| 6780 |
+ |
|
| 6781 |
+ my $self = bless {
|
|
| 6782 |
+ _pushd => $dest, |
|
| 6783 |
+ _original => $orig |
|
| 6784 |
+ }, __PACKAGE__; |
|
| 6785 |
+ |
|
| 6786 |
+ return $self; |
|
| 6654 | 6787 |
} |
| 6655 | 6788 |
|
| 6656 |
- # etc |
|
| 6789 |
+ #--------------------------------------------------------------------------# |
|
| 6790 |
+ # tempd() |
|
| 6791 |
+ #--------------------------------------------------------------------------# |
|
| 6657 | 6792 |
|
| 6658 |
- sub max_depth {
|
|
| 6659 |
- my $max = defined $_[1] ? $_[1] : 0x80000000; |
|
| 6660 |
- $_[0]->{max_depth} = $max;
|
|
| 6661 |
- $_[0]; |
|
| 6793 |
+ sub tempd {
|
|
| 6794 |
+ my ($options) = @_; |
|
| 6795 |
+ my $dir; |
|
| 6796 |
+ eval { $dir = pushd( File::Temp::tempdir( CLEANUP => 0 ), $options ) };
|
|
| 6797 |
+ croak $@ if $@; |
|
| 6798 |
+ $dir->{_tempd} = 1;
|
|
| 6799 |
+ return $dir; |
|
| 6662 | 6800 |
} |
| 6663 | 6801 |
|
| 6802 |
+ #--------------------------------------------------------------------------# |
|
| 6803 |
+ # preserve() |
|
| 6804 |
+ #--------------------------------------------------------------------------# |
|
| 6664 | 6805 |
|
| 6665 |
- sub get_max_depth { $_[0]->{max_depth}; }
|
|
| 6806 |
+ sub preserve {
|
|
| 6807 |
+ my $self = shift; |
|
| 6808 |
+ return 1 if ! $self->{"_tempd"};
|
|
| 6809 |
+ if ( @_ == 0 ) {
|
|
| 6810 |
+ return $self->{_preserve} = 1;
|
|
| 6811 |
+ } |
|
| 6812 |
+ else {
|
|
| 6813 |
+ return $self->{_preserve} = $_[0] ? 1 : 0;
|
|
| 6814 |
+ } |
|
| 6815 |
+ } |
|
| 6666 | 6816 |
|
| 6817 |
+ #--------------------------------------------------------------------------# |
|
| 6818 |
+ # DESTROY() |
|
| 6819 |
+ # Revert to original directory as object is destroyed and cleanup |
|
| 6820 |
+ # if necessary |
|
| 6821 |
+ #--------------------------------------------------------------------------# |
|
| 6667 | 6822 |
|
| 6668 |
- sub max_size {
|
|
| 6669 |
- my $max = defined $_[1] ? $_[1] : 0; |
|
| 6670 |
- $_[0]->{max_size} = $max;
|
|
| 6671 |
- $_[0]; |
|
| 6823 |
+ sub DESTROY {
|
|
| 6824 |
+ my ($self) = @_; |
|
| 6825 |
+ my $orig = $self->{_original};
|
|
| 6826 |
+ chdir $orig if $orig; # should always be so, but just in case... |
|
| 6827 |
+ if ( $self->{_tempd} &&
|
|
| 6828 |
+ !$self->{_preserve} ) {
|
|
| 6829 |
+ # don't destroy existing $@ if there is no error. |
|
| 6830 |
+ my $err = do {
|
|
| 6831 |
+ local $@; |
|
| 6832 |
+ eval { rmtree( $self->{_pushd} ) };
|
|
| 6833 |
+ $@; |
|
| 6834 |
+ }; |
|
| 6835 |
+ carp $err if $err; |
|
| 6836 |
+ } |
|
| 6672 | 6837 |
} |
| 6673 | 6838 |
|
| 6839 |
+ 1; |
|
| 6840 |
+ |
|
| 6841 |
+ __END__ |
|
| 6674 | 6842 |
|
| 6675 |
- sub get_max_size { $_[0]->{max_size}; }
|
|
| 6843 |
+FILE_PUSHD |
|
| 6844 |
+ |
|
| 6845 |
+$fatpacked{"HTTP/Tiny.pm"} = <<'HTTP_TINY';
|
|
| 6846 |
+ # vim: ts=4 sts=4 sw=4 et: |
|
| 6847 |
+ package HTTP::Tiny; |
|
| 6848 |
+ use strict; |
|
| 6849 |
+ use warnings; |
|
| 6850 |
+ # ABSTRACT: A small, simple, correct HTTP/1.1 client |
|
| 6851 |
+ our $VERSION = '0.028'; # VERSION |
|
| 6676 | 6852 |
|
| 6853 |
+ use Carp (); |
|
| 6677 | 6854 |
|
| 6678 |
- sub filter_json_object {
|
|
| 6679 |
- $_[0]->{cb_object} = defined $_[1] ? $_[1] : 0;
|
|
| 6680 |
- $_[0]->{F_HOOK} = ($_[0]->{cb_object} or $_[0]->{cb_sk_object}) ? 1 : 0;
|
|
| 6681 |
- $_[0]; |
|
| 6682 |
- } |
|
| 6683 | 6855 |
|
| 6684 |
- sub filter_json_single_key_object {
|
|
| 6685 |
- if (@_ > 1) {
|
|
| 6686 |
- $_[0]->{cb_sk_object}->{$_[1]} = $_[2];
|
|
| 6856 |
+ my @attributes; |
|
| 6857 |
+ BEGIN {
|
|
| 6858 |
+ @attributes = qw(agent cookie_jar default_headers local_address max_redirect max_size proxy timeout SSL_options verify_SSL); |
|
| 6859 |
+ no strict 'refs'; |
|
| 6860 |
+ for my $accessor ( @attributes ) {
|
|
| 6861 |
+ *{$accessor} = sub {
|
|
| 6862 |
+ @_ > 1 ? $_[0]->{$accessor} = $_[1] : $_[0]->{$accessor};
|
|
| 6863 |
+ }; |
|
| 6687 | 6864 |
} |
| 6688 |
- $_[0]->{F_HOOK} = ($_[0]->{cb_object} or $_[0]->{cb_sk_object}) ? 1 : 0;
|
|
| 6689 |
- $_[0]; |
|
| 6690 | 6865 |
} |
| 6691 | 6866 |
|
| 6692 |
- sub indent_length {
|
|
| 6693 |
- if (!defined $_[1] or $_[1] > 15 or $_[1] < 0) {
|
|
| 6694 |
- Carp::carp "The acceptable range of indent_length() is 0 to 15."; |
|
| 6867 |
+ sub new {
|
|
| 6868 |
+ my($class, %args) = @_; |
|
| 6869 |
+ |
|
| 6870 |
+ (my $default_agent = $class) =~ s{::}{-}g;
|
|
| 6871 |
+ $default_agent .= "/" . ($class->VERSION || 0); |
|
| 6872 |
+ |
|
| 6873 |
+ my $self = {
|
|
| 6874 |
+ agent => $default_agent, |
|
| 6875 |
+ max_redirect => 5, |
|
| 6876 |
+ timeout => 60, |
|
| 6877 |
+ verify_SSL => $args{verify_SSL} || $args{verify_ssl} || 0, # no verification by default
|
|
| 6878 |
+ }; |
|
| 6879 |
+ |
|
| 6880 |
+ $args{agent} .= $default_agent
|
|
| 6881 |
+ if defined $args{agent} && $args{agent} =~ / $/;
|
|
| 6882 |
+ |
|
| 6883 |
+ $class->_validate_cookie_jar( $args{cookie_jar} ) if $args{cookie_jar};
|
|
| 6884 |
+ |
|
| 6885 |
+ for my $key ( @attributes ) {
|
|
| 6886 |
+ $self->{$key} = $args{$key} if exists $args{$key}
|
|
| 6695 | 6887 |
} |
| 6696 |
- else {
|
|
| 6697 |
- $_[0]->{indent_length} = $_[1];
|
|
| 6888 |
+ |
|
| 6889 |
+ # Never override proxy argument as this breaks backwards compat. |
|
| 6890 |
+ if (!exists $self->{proxy} && (my $http_proxy = $ENV{http_proxy})) {
|
|
| 6891 |
+ if ($http_proxy =~ m{\Ahttp://[^/?#:@]+:\d+/?\z}) {
|
|
| 6892 |
+ $self->{proxy} = $http_proxy;
|
|
| 6893 |
+ } |
|
| 6894 |
+ else {
|
|
| 6895 |
+ Carp::croak(qq{Environment 'http_proxy' must be in format http://<host>:<port>/\n});
|
|
| 6896 |
+ } |
|
| 6698 | 6897 |
} |
| 6699 |
- $_[0]; |
|
| 6700 |
- } |
|
| 6701 | 6898 |
|
| 6702 |
- sub get_indent_length {
|
|
| 6703 |
- $_[0]->{indent_length};
|
|
| 6899 |
+ return bless $self, $class; |
|
| 6704 | 6900 |
} |
| 6705 | 6901 |
|
| 6706 |
- sub sort_by {
|
|
| 6707 |
- $_[0]->{sort_by} = defined $_[1] ? $_[1] : 1;
|
|
| 6708 |
- $_[0]; |
|
| 6709 |
- } |
|
| 6710 | 6902 |
|
| 6711 |
- sub allow_bigint {
|
|
| 6712 |
- Carp::carp("allow_bigint() is obsoleted. use allow_bignum() insted.");
|
|
| 6903 |
+ for my $sub_name ( qw/get head put post delete/ ) {
|
|
| 6904 |
+ my $req_method = uc $sub_name; |
|
| 6905 |
+ no strict 'refs'; |
|
| 6906 |
+ eval <<"HERE"; ## no critic |
|
| 6907 |
+ sub $sub_name {
|
|
| 6908 |
+ my (\$self, \$url, \$args) = \@_; |
|
| 6909 |
+ \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH') |
|
| 6910 |
+ or Carp::croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n"); |
|
| 6911 |
+ return \$self->request('$req_method', \$url, \$args || {});
|
|
| 6912 |
+ } |
|
| 6913 |
+ HERE |
|
| 6713 | 6914 |
} |
| 6714 | 6915 |
|
| 6715 |
- ############################### |
|
| 6716 | 6916 |
|
| 6717 |
- ### |
|
| 6718 |
- ### Perl => JSON |
|
| 6719 |
- ### |
|
| 6917 |
+ sub post_form {
|
|
| 6918 |
+ my ($self, $url, $data, $args) = @_; |
|
| 6919 |
+ (@_ == 3 || @_ == 4 && ref $args eq 'HASH') |
|
| 6920 |
+ or Carp::croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n"); |
|
| 6720 | 6921 |
|
| 6922 |
+ my $headers = {};
|
|
| 6923 |
+ while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
|
|
| 6924 |
+ $headers->{lc $key} = $value;
|
|
| 6925 |
+ } |
|
| 6926 |
+ delete $args->{headers};
|
|
| 6721 | 6927 |
|
| 6722 |
- { # Convert
|
|
| 6928 |
+ return $self->request('POST', $url, {
|
|
| 6929 |
+ %$args, |
|
| 6930 |
+ content => $self->www_form_urlencode($data), |
|
| 6931 |
+ headers => {
|
|
| 6932 |
+ %$headers, |
|
| 6933 |
+ 'content-type' => 'application/x-www-form-urlencoded' |
|
| 6934 |
+ }, |
|
| 6935 |
+ } |
|
| 6936 |
+ ); |
|
| 6937 |
+ } |
|
| 6723 | 6938 |
|
| 6724 |
- my $max_depth; |
|
| 6725 |
- my $indent; |
|
| 6726 |
- my $ascii; |
|
| 6727 |
- my $latin1; |
|
| 6728 |
- my $utf8; |
|
| 6729 |
- my $space_before; |
|
| 6730 |
- my $space_after; |
|
| 6731 |
- my $canonical; |
|
| 6732 |
- my $allow_blessed; |
|
| 6733 |
- my $convert_blessed; |
|
| 6734 | 6939 |
|
| 6735 |
- my $indent_length; |
|
| 6736 |
- my $escape_slash; |
|
| 6737 |
- my $bignum; |
|
| 6738 |
- my $as_nonblessed; |
|
| 6940 |
+ sub mirror {
|
|
| 6941 |
+ my ($self, $url, $file, $args) = @_; |
|
| 6942 |
+ @_ == 3 || (@_ == 4 && ref $args eq 'HASH') |
|
| 6943 |
+ or Carp::croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n"); |
|
| 6944 |
+ if ( -e $file and my $mtime = (stat($file))[9] ) {
|
|
| 6945 |
+ $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
|
|
| 6946 |
+ } |
|
| 6947 |
+ my $tempfile = $file . int(rand(2**31)); |
|
| 6948 |
+ open my $fh, ">", $tempfile |
|
| 6949 |
+ or Carp::croak(qq/Error: Could not open temporary file $tempfile for downloading: $!\n/); |
|
| 6950 |
+ binmode $fh; |
|
| 6951 |
+ $args->{data_callback} = sub { print {$fh} $_[0] };
|
|
| 6952 |
+ my $response = $self->request('GET', $url, $args);
|
|
| 6953 |
+ close $fh |
|
| 6954 |
+ or Carp::croak(qq/Error: Could not close temporary file $tempfile: $!\n/); |
|
| 6955 |
+ if ( $response->{success} ) {
|
|
| 6956 |
+ rename $tempfile, $file |
|
| 6957 |
+ or Carp::croak(qq/Error replacing $file with $tempfile: $!\n/); |
|
| 6958 |
+ my $lm = $response->{headers}{'last-modified'};
|
|
| 6959 |
+ if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
|
|
| 6960 |
+ utime $mtime, $mtime, $file; |
|
| 6961 |
+ } |
|
| 6962 |
+ } |
|
| 6963 |
+ $response->{success} ||= $response->{status} eq '304';
|
|
| 6964 |
+ unlink $tempfile; |
|
| 6965 |
+ return $response; |
|
| 6966 |
+ } |
|
| 6739 | 6967 |
|
| 6740 |
- my $depth; |
|
| 6741 |
- my $indent_count; |
|
| 6742 |
- my $keysort; |
|
| 6743 | 6968 |
|
| 6969 |
+ my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
|
|
| 6744 | 6970 |
|
| 6745 |
- sub PP_encode_json {
|
|
| 6746 |
- my $self = shift; |
|
| 6747 |
- my $obj = shift; |
|
| 6971 |
+ sub request {
|
|
| 6972 |
+ my ($self, $method, $url, $args) = @_; |
|
| 6973 |
+ @_ == 3 || (@_ == 4 && ref $args eq 'HASH') |
|
| 6974 |
+ or Carp::croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n"); |
|
| 6975 |
+ $args ||= {}; # we keep some state in this during _request
|
|
| 6748 | 6976 |
|
| 6749 |
- $indent_count = 0; |
|
| 6750 |
- $depth = 0; |
|
| 6977 |
+ # RFC 2616 Section 8.1.4 mandates a single retry on broken socket |
|
| 6978 |
+ my $response; |
|
| 6979 |
+ for ( 0 .. 1 ) {
|
|
| 6980 |
+ $response = eval { $self->_request($method, $url, $args) };
|
|
| 6981 |
+ last unless $@ && $idempotent{$method}
|
|
| 6982 |
+ && $@ =~ m{^(?:Socket closed|Unexpected end)};
|
|
| 6983 |
+ } |
|
| 6751 | 6984 |
|
| 6752 |
- my $idx = $self->{PROPS};
|
|
| 6985 |
+ if (my $e = "$@") {
|
|
| 6986 |
+ $response = {
|
|
| 6987 |
+ url => $url, |
|
| 6988 |
+ success => q{},
|
|
| 6989 |
+ status => 599, |
|
| 6990 |
+ reason => 'Internal Exception', |
|
| 6991 |
+ content => $e, |
|
| 6992 |
+ headers => {
|
|
| 6993 |
+ 'content-type' => 'text/plain', |
|
| 6994 |
+ 'content-length' => length $e, |
|
| 6995 |
+ } |
|
| 6996 |
+ }; |
|
| 6997 |
+ } |
|
| 6998 |
+ return $response; |
|
| 6999 |
+ } |
|
| 6753 | 7000 |
|
| 6754 |
- ($ascii, $latin1, $utf8, $indent, $canonical, $space_before, $space_after, $allow_blessed, |
|
| 6755 |
- $convert_blessed, $escape_slash, $bignum, $as_nonblessed) |
|
| 6756 |
- = @{$idx}[P_ASCII .. P_SPACE_AFTER, P_ALLOW_BLESSED, P_CONVERT_BLESSED,
|
|
| 6757 |
- P_ESCAPE_SLASH, P_ALLOW_BIGNUM, P_AS_NONBLESSED]; |
|
| 6758 | 7001 |
|
| 6759 |
- ($max_depth, $indent_length) = @{$self}{qw/max_depth indent_length/};
|
|
| 7002 |
+ sub www_form_urlencode {
|
|
| 7003 |
+ my ($self, $data) = @_; |
|
| 7004 |
+ (@_ == 2 && ref $data) |
|
| 7005 |
+ or Carp::croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n"); |
|
| 7006 |
+ (ref $data eq 'HASH' || ref $data eq 'ARRAY') |
|
| 7007 |
+ or Carp::croak("form data must be a hash or array reference\n");
|
|
| 6760 | 7008 |
|
| 6761 |
- $keysort = $canonical ? sub { $a cmp $b } : undef;
|
|
| 7009 |
+ my @params = ref $data eq 'HASH' ? %$data : @$data; |
|
| 7010 |
+ @params % 2 == 0 |
|
| 7011 |
+ or Carp::croak("form data reference must have an even number of terms\n");
|
|
| 6762 | 7012 |
|
| 6763 |
- if ($self->{sort_by}) {
|
|
| 6764 |
- $keysort = ref($self->{sort_by}) eq 'CODE' ? $self->{sort_by}
|
|
| 6765 |
- : $self->{sort_by} =~ /\D+/ ? $self->{sort_by}
|
|
| 6766 |
- : sub { $a cmp $b };
|
|
| 7013 |
+ my @terms; |
|
| 7014 |
+ while( @params ) {
|
|
| 7015 |
+ my ($key, $value) = splice(@params, 0, 2); |
|
| 7016 |
+ if ( ref $value eq 'ARRAY' ) {
|
|
| 7017 |
+ unshift @params, map { $key => $_ } @$value;
|
|
| 7018 |
+ } |
|
| 7019 |
+ else {
|
|
| 7020 |
+ push @terms, join("=", map { $self->_uri_escape($_) } $key, $value);
|
|
| 6767 | 7021 |
} |
| 7022 |
+ } |
|
| 6768 | 7023 |
|
| 6769 |
- encode_error("hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)")
|
|
| 6770 |
- if(!ref $obj and !$idx->[ P_ALLOW_NONREF ]); |
|
| 7024 |
+ return join("&", sort @terms);
|
|
| 7025 |
+ } |
|
| 6771 | 7026 |
|
| 6772 |
- my $str = $self->object_to_json($obj); |
|
| 7027 |
+ #--------------------------------------------------------------------------# |
|
| 7028 |
+ # private methods |
|
| 7029 |
+ #--------------------------------------------------------------------------# |
|
| 6773 | 7030 |
|
| 6774 |
- $str .= "\n" if ( $indent ); # JSON::XS 2.26 compatible |
|
| 7031 |
+ my %DefaultPort = ( |
|
| 7032 |
+ http => 80, |
|
| 7033 |
+ https => 443, |
|
| 7034 |
+ ); |
|
| 6775 | 7035 |
|
| 6776 |
- unless ($ascii or $latin1 or $utf8) {
|
|
| 6777 |
- utf8::upgrade($str); |
|
| 6778 |
- } |
|
| 7036 |
+ sub _request {
|
|
| 7037 |
+ my ($self, $method, $url, $args) = @_; |
|
| 6779 | 7038 |
|
| 6780 |
- if ($idx->[ P_SHRINK ]) {
|
|
| 6781 |
- utf8::downgrade($str, 1); |
|
| 6782 |
- } |
|
| 7039 |
+ my ($scheme, $host, $port, $path_query) = $self->_split_url($url); |
|
| 6783 | 7040 |
|
| 6784 |
- return $str; |
|
| 7041 |
+ my $request = {
|
|
| 7042 |
+ method => $method, |
|
| 7043 |
+ scheme => $scheme, |
|
| 7044 |
+ host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
|
|
| 7045 |
+ uri => $path_query, |
|
| 7046 |
+ headers => {},
|
|
| 7047 |
+ }; |
|
| 7048 |
+ |
|
| 7049 |
+ my $handle = HTTP::Tiny::Handle->new( |
|
| 7050 |
+ timeout => $self->{timeout},
|
|
| 7051 |
+ SSL_options => $self->{SSL_options},
|
|
| 7052 |
+ verify_SSL => $self->{verify_SSL},
|
|
| 7053 |
+ local_address => $self->{local_address},
|
|
| 7054 |
+ ); |
|
| 7055 |
+ |
|
| 7056 |
+ if ($self->{proxy}) {
|
|
| 7057 |
+ $request->{uri} = "$scheme://$request->{host_port}$path_query";
|
|
| 7058 |
+ die(qq/HTTPS via proxy is not supported\n/) |
|
| 7059 |
+ if $request->{scheme} eq 'https';
|
|
| 7060 |
+ $handle->connect(($self->_split_url($self->{proxy}))[0..2]);
|
|
| 7061 |
+ } |
|
| 7062 |
+ else {
|
|
| 7063 |
+ $handle->connect($scheme, $host, $port); |
|
| 6785 | 7064 |
} |
| 6786 | 7065 |
|
| 7066 |
+ $self->_prepare_headers_and_cb($request, $args, $url); |
|
| 7067 |
+ $handle->write_request($request); |
|
| 6787 | 7068 |
|
| 6788 |
- sub object_to_json {
|
|
| 6789 |
- my ($self, $obj) = @_; |
|
| 6790 |
- my $type = ref($obj); |
|
| 7069 |
+ my $response; |
|
| 7070 |
+ do { $response = $handle->read_response_header }
|
|
| 7071 |
+ until (substr($response->{status},0,1) ne '1');
|
|
| 6791 | 7072 |
|
| 6792 |
- if($type eq 'HASH'){
|
|
| 6793 |
- return $self->hash_to_json($obj); |
|
| 6794 |
- } |
|
| 6795 |
- elsif($type eq 'ARRAY'){
|
|
| 6796 |
- return $self->array_to_json($obj); |
|
| 6797 |
- } |
|
| 6798 |
- elsif ($type) { # blessed object?
|
|
| 6799 |
- if (blessed($obj)) {
|
|
| 7073 |
+ $self->_update_cookie_jar( $url, $response ) if $self->{cookie_jar};
|
|
| 6800 | 7074 |
|
| 6801 |
- return $self->value_to_json($obj) if ( $obj->isa('JSON::PP::Boolean') );
|
|
| 7075 |
+ if ( my @redir_args = $self->_maybe_redirect($request, $response, $args) ) {
|
|
| 7076 |
+ $handle->close; |
|
| 7077 |
+ return $self->_request(@redir_args, $args); |
|
| 7078 |
+ } |
|
| 6802 | 7079 |
|
| 6803 |
- if ( $convert_blessed and $obj->can('TO_JSON') ) {
|
|
| 6804 |
- my $result = $obj->TO_JSON(); |
|
| 6805 |
- if ( defined $result and ref( $result ) ) {
|
|
| 6806 |
- if ( refaddr( $obj ) eq refaddr( $result ) ) {
|
|
| 6807 |
- encode_error( sprintf( |
|
| 6808 |
- "%s::TO_JSON method returned same object as was passed instead of a new one", |
|
| 6809 |
- ref $obj |
|
| 6810 |
- ) ); |
|
| 6811 |
- } |
|
| 6812 |
- } |
|
| 7080 |
+ if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
|
|
| 7081 |
+ # response has no message body |
|
| 7082 |
+ } |
|
| 7083 |
+ else {
|
|
| 7084 |
+ my $data_cb = $self->_prepare_data_cb($response, $args); |
|
| 7085 |
+ $handle->read_body($data_cb, $response); |
|
| 7086 |
+ } |
|
| 6813 | 7087 |
|
| 6814 |
- return $self->object_to_json( $result ); |
|
| 6815 |
- } |
|
| 7088 |
+ $handle->close; |
|
| 7089 |
+ $response->{success} = substr($response->{status},0,1) eq '2';
|
|
| 7090 |
+ $response->{url} = $url;
|
|
| 7091 |
+ return $response; |
|
| 7092 |
+ } |
|
| 6816 | 7093 |
|
| 6817 |
- return "$obj" if ( $bignum and _is_bignum($obj) ); |
|
| 6818 |
- return $self->blessed_to_json($obj) if ($allow_blessed and $as_nonblessed); # will be removed. |
|
| 7094 |
+ sub _prepare_headers_and_cb {
|
|
| 7095 |
+ my ($self, $request, $args, $url) = @_; |
|
| 6819 | 7096 |
|
| 6820 |
- encode_error( sprintf("encountered object '%s', but neither allow_blessed "
|
|
| 6821 |
- . "nor convert_blessed settings are enabled", $obj) |
|
| 6822 |
- ) unless ($allow_blessed); |
|
| 7097 |
+ for ($self->{default_headers}, $args->{headers}) {
|
|
| 7098 |
+ next unless defined; |
|
| 7099 |
+ while (my ($k, $v) = each %$_) {
|
|
| 7100 |
+ $request->{headers}{lc $k} = $v;
|
|
| 7101 |
+ } |
|
| 7102 |
+ } |
|
| 7103 |
+ $request->{headers}{'host'} = $request->{host_port};
|
|
| 7104 |
+ $request->{headers}{'connection'} = "close";
|
|
| 7105 |
+ $request->{headers}{'user-agent'} ||= $self->{agent};
|
|
| 6823 | 7106 |
|
| 6824 |
- return 'null'; |
|
| 6825 |
- } |
|
| 6826 |
- else {
|
|
| 6827 |
- return $self->value_to_json($obj); |
|
| 6828 |
- } |
|
| 7107 |
+ if (defined $args->{content}) {
|
|
| 7108 |
+ $request->{headers}{'content-type'} ||= "application/octet-stream";
|
|
| 7109 |
+ if (ref $args->{content} eq 'CODE') {
|
|
| 7110 |
+ $request->{headers}{'transfer-encoding'} = 'chunked'
|
|
| 7111 |
+ unless $request->{headers}{'content-length'}
|
|
| 7112 |
+ || $request->{headers}{'transfer-encoding'};
|
|
| 7113 |
+ $request->{cb} = $args->{content};
|
|
| 6829 | 7114 |
} |
| 6830 |
- else{
|
|
| 6831 |
- return $self->value_to_json($obj); |
|
| 7115 |
+ else {
|
|
| 7116 |
+ my $content = $args->{content};
|
|
| 7117 |
+ if ( $] ge '5.008' ) {
|
|
| 7118 |
+ utf8::downgrade($content, 1) |
|
| 7119 |
+ or die(qq/Wide character in request message body\n/); |
|
| 7120 |
+ } |
|
| 7121 |
+ $request->{headers}{'content-length'} = length $content
|
|
| 7122 |
+ unless $request->{headers}{'content-length'}
|
|
| 7123 |
+ || $request->{headers}{'transfer-encoding'};
|
|
| 7124 |
+ $request->{cb} = sub { substr $content, 0, length $content, '' };
|
|
| 6832 | 7125 |
} |
| 7126 |
+ $request->{trailer_cb} = $args->{trailer_callback}
|
|
| 7127 |
+ if ref $args->{trailer_callback} eq 'CODE';
|
|
| 6833 | 7128 |
} |
| 6834 | 7129 |
|
| 7130 |
+ ### If we have a cookie jar, then maybe add relevant cookies |
|
| 7131 |
+ if ( $self->{cookie_jar} ) {
|
|
| 7132 |
+ my $cookies = $self->cookie_jar->cookie_header( $url ); |
|
| 7133 |
+ $request->{headers}{cookie} = $cookies if length $cookies;
|
|
| 7134 |
+ } |
|
| 6835 | 7135 |
|
| 6836 |
- sub hash_to_json {
|
|
| 6837 |
- my ($self, $obj) = @_; |
|
| 6838 |
- my @res; |
|
| 6839 |
- |
|
| 6840 |
- encode_error("json text or perl structure exceeds maximum nesting level (max_depth set too low?)")
|
|
| 6841 |
- if (++$depth > $max_depth); |
|
| 7136 |
+ return; |
|
| 7137 |
+ } |
|
| 6842 | 7138 |
|
| 6843 |
- my ($pre, $post) = $indent ? $self->_up_indent() : ('', '');
|
|
| 6844 |
- my $del = ($space_before ? ' ' : '') . ':' . ($space_after ? ' ' : ''); |
|
| 7139 |
+ sub _prepare_data_cb {
|
|
| 7140 |
+ my ($self, $response, $args) = @_; |
|
| 7141 |
+ my $data_cb = $args->{data_callback};
|
|
| 7142 |
+ $response->{content} = '';
|
|
| 6845 | 7143 |
|
| 6846 |
- for my $k ( _sort( $obj ) ) {
|
|
| 6847 |
- if ( OLD_PERL ) { utf8::decode($k) } # key for Perl 5.6 / be optimized
|
|
| 6848 |
- push @res, string_to_json( $self, $k ) |
|
| 6849 |
- . $del |
|
| 6850 |
- . ( $self->object_to_json( $obj->{$k} ) || $self->value_to_json( $obj->{$k} ) );
|
|
| 7144 |
+ if (!$data_cb || $response->{status} !~ /^2/) {
|
|
| 7145 |
+ if (defined $self->{max_size}) {
|
|
| 7146 |
+ $data_cb = sub {
|
|
| 7147 |
+ $_[1]->{content} .= $_[0];
|
|
| 7148 |
+ die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
|
|
| 7149 |
+ if length $_[1]->{content} > $self->{max_size};
|
|
| 7150 |
+ }; |
|
| 7151 |
+ } |
|
| 7152 |
+ else {
|
|
| 7153 |
+ $data_cb = sub { $_[1]->{content} .= $_[0] };
|
|
| 6851 | 7154 |
} |
| 6852 |
- |
|
| 6853 |
- --$depth; |
|
| 6854 |
- $self->_down_indent() if ($indent); |
|
| 6855 |
- |
|
| 6856 |
- return '{' . ( @res ? $pre : '' ) . ( @res ? join( ",$pre", @res ) . $post : '' ) . '}';
|
|
| 6857 | 7155 |
} |
| 7156 |
+ return $data_cb; |
|
| 7157 |
+ } |
|
| 6858 | 7158 |
|
| 7159 |
+ sub _update_cookie_jar {
|
|
| 7160 |
+ my ($self, $url, $response) = @_; |
|
| 6859 | 7161 |
|
| 6860 |
- sub array_to_json {
|
|
| 6861 |
- my ($self, $obj) = @_; |
|
| 6862 |
- my @res; |
|
| 7162 |
+ my $cookies = $response->{headers}->{'set-cookie'};
|
|
| 7163 |
+ return unless defined $cookies; |
|
| 6863 | 7164 |
|
| 6864 |
- encode_error("json text or perl structure exceeds maximum nesting level (max_depth set too low?)")
|
|
| 6865 |
- if (++$depth > $max_depth); |
|
| 7165 |
+ my @cookies = ref $cookies ? @$cookies : $cookies; |
|
| 6866 | 7166 |
|
| 6867 |
- my ($pre, $post) = $indent ? $self->_up_indent() : ('', '');
|
|
| 7167 |
+ $self->cookie_jar->add( $url, $_ ) for @cookies; |
|
| 6868 | 7168 |
|
| 6869 |
- for my $v (@$obj){
|
|
| 6870 |
- push @res, $self->object_to_json($v) || $self->value_to_json($v); |
|
| 6871 |
- } |
|
| 7169 |
+ return; |
|
| 7170 |
+ } |
|
| 6872 | 7171 |
|
| 6873 |
- --$depth; |
|
| 6874 |
- $self->_down_indent() if ($indent); |
|
| 7172 |
+ sub _validate_cookie_jar {
|
|
| 7173 |
+ my ($class, $jar) = @_; |
|
| 6875 | 7174 |
|
| 6876 |
- return '[' . ( @res ? $pre : '' ) . ( @res ? join( ",$pre", @res ) . $post : '' ) . ']'; |
|
| 7175 |
+ # duck typing |
|
| 7176 |
+ for my $method ( qw/add cookie_header/ ) {
|
|
| 7177 |
+ Carp::croak(qq/Cookie jar must provide the '$method' method\n/) |
|
| 7178 |
+ unless ref($jar) && ref($jar)->can($method); |
|
| 6877 | 7179 |
} |
| 6878 | 7180 |
|
| 7181 |
+ return; |
|
| 7182 |
+ } |
|
| 6879 | 7183 |
|
| 6880 |
- sub value_to_json {
|
|
| 6881 |
- my ($self, $value) = @_; |
|
| 7184 |
+ sub _maybe_redirect {
|
|
| 7185 |
+ my ($self, $request, $response, $args) = @_; |
|
| 7186 |
+ my $headers = $response->{headers};
|
|
| 7187 |
+ my ($status, $method) = ($response->{status}, $request->{method});
|
|
| 7188 |
+ if (($status eq '303' or ($status =~ /^30[127]/ && $method =~ /^GET|HEAD$/)) |
|
| 7189 |
+ and $headers->{location}
|
|
| 7190 |
+ and ++$args->{redirects} <= $self->{max_redirect}
|
|
| 7191 |
+ ) {
|
|
| 7192 |
+ my $location = ($headers->{location} =~ /^\//)
|
|
| 7193 |
+ ? "$request->{scheme}://$request->{host_port}$headers->{location}"
|
|
| 7194 |
+ : $headers->{location} ;
|
|
| 7195 |
+ return (($status eq '303' ? 'GET' : $method), $location); |
|
| 7196 |
+ } |
|
| 7197 |
+ return; |
|
| 7198 |
+ } |
|
| 6882 | 7199 |
|
| 6883 |
- return 'null' if(!defined $value); |
|
| 7200 |
+ sub _split_url {
|
|
| 7201 |
+ my $url = pop; |
|
| 6884 | 7202 |
|
| 6885 |
- my $b_obj = B::svref_2object(\$value); # for round trip problem |
|
| 6886 |
- my $flags = $b_obj->FLAGS; |
|
| 7203 |
+ # URI regex adapted from the URI module |
|
| 7204 |
+ my ($scheme, $authority, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)> |
|
| 7205 |
+ or die(qq/Cannot parse URL: '$url'\n/); |
|
| 6887 | 7206 |
|
| 6888 |
- return $value # as is |
|
| 6889 |
- if $flags & ( B::SVp_IOK | B::SVp_NOK ) and !( $flags & B::SVp_POK ); # SvTYPE is IV or NV? |
|
| 7207 |
+ $scheme = lc $scheme; |
|
| 7208 |
+ $path_query = "/$path_query" unless $path_query =~ m<\A/>; |
|
| 6890 | 7209 |
|
| 6891 |
- my $type = ref($value); |
|
| 7210 |
+ my $host = (length($authority)) ? lc $authority : 'localhost'; |
|
| 7211 |
+ $host =~ s/\A[^@]*@//; # userinfo |
|
| 7212 |
+ my $port = do {
|
|
| 7213 |
+ $host =~ s/:([0-9]*)\z// && length $1 |
|
| 7214 |
+ ? $1 |
|
| 7215 |
+ : ($scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef); |
|
| 7216 |
+ }; |
|
| 6892 | 7217 |
|
| 6893 |
- if(!$type){
|
|
| 6894 |
- return string_to_json($self, $value); |
|
| 6895 |
- } |
|
| 6896 |
- elsif( blessed($value) and $value->isa('JSON::PP::Boolean') ){
|
|
| 6897 |
- return $$value == 1 ? 'true' : 'false'; |
|
| 6898 |
- } |
|
| 6899 |
- elsif ($type) {
|
|
| 6900 |
- if ((overload::StrVal($value) =~ /=(\w+)/)[0]) {
|
|
| 6901 |
- return $self->value_to_json("$value");
|
|
| 6902 |
- } |
|
| 7218 |
+ return ($scheme, $host, $port, $path_query); |
|
| 7219 |
+ } |
|
| 6903 | 7220 |
|
| 6904 |
- if ($type eq 'SCALAR' and defined $$value) {
|
|
| 6905 |
- return $$value eq '1' ? 'true' |
|
| 6906 |
- : $$value eq '0' ? 'false' |
|
| 6907 |
- : $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ? 'null'
|
|
| 6908 |
- : encode_error("cannot encode reference to scalar");
|
|
| 6909 |
- } |
|
| 7221 |
+ # Date conversions adapted from HTTP::Date |
|
| 7222 |
+ my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat"; |
|
| 7223 |
+ my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"; |
|
| 7224 |
+ sub _http_date {
|
|
| 7225 |
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]); |
|
| 7226 |
+ return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
|
|
| 7227 |
+ substr($DoW,$wday*4,3), |
|
| 7228 |
+ $mday, substr($MoY,$mon*4,3), $year+1900, |
|
| 7229 |
+ $hour, $min, $sec |
|
| 7230 |
+ ); |
|
| 7231 |
+ } |
|
| 6910 | 7232 |
|
| 6911 |
- if ( $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ) {
|
|
| 6912 |
- return 'null'; |
|
| 6913 |
- } |
|
| 6914 |
- else {
|
|
| 6915 |
- if ( $type eq 'SCALAR' or $type eq 'REF' ) {
|
|
| 6916 |
- encode_error("cannot encode reference to scalar");
|
|
| 6917 |
- } |
|
| 6918 |
- else {
|
|
| 6919 |
- encode_error("encountered $value, but JSON can only represent references to arrays or hashes");
|
|
| 6920 |
- } |
|
| 6921 |
- } |
|
| 7233 |
+ sub _parse_http_date {
|
|
| 7234 |
+ my ($self, $str) = @_; |
|
| 7235 |
+ require Time::Local; |
|
| 7236 |
+ my @tl_parts; |
|
| 7237 |
+ if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
|
|
| 7238 |
+ @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); |
|
| 7239 |
+ } |
|
| 7240 |
+ elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
|
|
| 7241 |
+ @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); |
|
| 7242 |
+ } |
|
| 7243 |
+ elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
|
|
| 7244 |
+ @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6); |
|
| 7245 |
+ } |
|
| 7246 |
+ return eval {
|
|
| 7247 |
+ my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1; |
|
| 7248 |
+ $t < 0 ? undef : $t; |
|
| 7249 |
+ }; |
|
| 7250 |
+ } |
|
| 6922 | 7251 |
|
| 6923 |
- } |
|
| 6924 |
- else {
|
|
| 6925 |
- return $self->{fallback}->($value)
|
|
| 6926 |
- if ($self->{fallback} and ref($self->{fallback}) eq 'CODE');
|
|
| 6927 |
- return 'null'; |
|
| 6928 |
- } |
|
| 7252 |
+ # URI escaping adapted from URI::Escape |
|
| 7253 |
+ # c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 |
|
| 7254 |
+ # perl 5.6 ready UTF-8 encoding adapted from JSON::PP |
|
| 7255 |
+ my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255;
|
|
| 7256 |
+ $escapes{' '}="+";
|
|
| 7257 |
+ my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/; |
|
| 6929 | 7258 |
|
| 7259 |
+ sub _uri_escape {
|
|
| 7260 |
+ my ($self, $str) = @_; |
|
| 7261 |
+ if ( $] ge '5.008' ) {
|
|
| 7262 |
+ utf8::encode($str); |
|
| 6930 | 7263 |
} |
| 7264 |
+ else {
|
|
| 7265 |
+ $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string
|
|
| 7266 |
+ if ( length $str == do { use bytes; length $str } );
|
|
| 7267 |
+ $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag
|
|
| 7268 |
+ } |
|
| 7269 |
+ $str =~ s/($unsafe_char)/$escapes{$1}/ge;
|
|
| 7270 |
+ return $str; |
|
| 7271 |
+ } |
|
| 6931 | 7272 |
|
| 7273 |
+ package |
|
| 7274 |
+ HTTP::Tiny::Handle; # hide from PAUSE/indexers |
|
| 7275 |
+ use strict; |
|
| 7276 |
+ use warnings; |
|
| 6932 | 7277 |
|
| 6933 |
- my %esc = ( |
|
| 6934 |
- "\n" => '\n', |
|
| 6935 |
- "\r" => '\r', |
|
| 6936 |
- "\t" => '\t', |
|
| 6937 |
- "\f" => '\f', |
|
| 6938 |
- "\b" => '\b', |
|
| 6939 |
- "\"" => '\"', |
|
| 6940 |
- "\\" => '\\\\', |
|
| 6941 |
- "\'" => '\\\'', |
|
| 6942 |
- ); |
|
| 6943 |
- |
|
| 7278 |
+ use Errno qw[EINTR EPIPE]; |
|
| 7279 |
+ use IO::Socket qw[SOCK_STREAM]; |
|
| 6944 | 7280 |
|
| 6945 |
- sub string_to_json {
|
|
| 6946 |
- my ($self, $arg) = @_; |
|
| 7281 |
+ sub BUFSIZE () { 32768 } ## no critic
|
|
| 6947 | 7282 |
|
| 6948 |
- $arg =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/g;
|
|
| 6949 |
- $arg =~ s/\//\\\//g if ($escape_slash); |
|
| 6950 |
- $arg =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg;
|
|
| 7283 |
+ my $Printable = sub {
|
|
| 7284 |
+ local $_ = shift; |
|
| 7285 |
+ s/\r/\\r/g; |
|
| 7286 |
+ s/\n/\\n/g; |
|
| 7287 |
+ s/\t/\\t/g; |
|
| 7288 |
+ s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
|
|
| 7289 |
+ $_; |
|
| 7290 |
+ }; |
|
| 6951 | 7291 |
|
| 6952 |
- if ($ascii) {
|
|
| 6953 |
- $arg = JSON_PP_encode_ascii($arg); |
|
| 6954 |
- } |
|
| 7292 |
+ my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/; |
|
| 6955 | 7293 |
|
| 6956 |
- if ($latin1) {
|
|
| 6957 |
- $arg = JSON_PP_encode_latin1($arg); |
|
| 6958 |
- } |
|
| 7294 |
+ sub new {
|
|
| 7295 |
+ my ($class, %args) = @_; |
|
| 7296 |
+ return bless {
|
|
| 7297 |
+ rbuf => '', |
|
| 7298 |
+ timeout => 60, |
|
| 7299 |
+ max_line_size => 16384, |
|
| 7300 |
+ max_header_lines => 64, |
|
| 7301 |
+ verify_SSL => 0, |
|
| 7302 |
+ SSL_options => {},
|
|
| 7303 |
+ %args |
|
| 7304 |
+ }, $class; |
|
| 7305 |
+ } |
|
| 6959 | 7306 |
|
| 6960 |
- if ($utf8) {
|
|
| 6961 |
- utf8::encode($arg); |
|
| 6962 |
- } |
|
| 7307 |
+ sub connect {
|
|
| 7308 |
+ @_ == 4 || die(q/Usage: $handle->connect(scheme, host, port)/ . "\n"); |
|
| 7309 |
+ my ($self, $scheme, $host, $port) = @_; |
|
| 6963 | 7310 |
|
| 6964 |
- return '"' . $arg . '"'; |
|
| 7311 |
+ if ( $scheme eq 'https' ) {
|
|
| 7312 |
+ die(qq/IO::Socket::SSL 1.56 must be installed for https support\n/) |
|
| 7313 |
+ unless eval {require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.56)};
|
|
| 7314 |
+ die(qq/Net::SSLeay 1.49 must be installed for https support\n/) |
|
| 7315 |
+ unless eval {require Net::SSLeay; Net::SSLeay->VERSION(1.49)};
|
|
| 6965 | 7316 |
} |
| 6966 |
- |
|
| 6967 |
- |
|
| 6968 |
- sub blessed_to_json {
|
|
| 6969 |
- my $reftype = reftype($_[1]) || ''; |
|
| 6970 |
- if ($reftype eq 'HASH') {
|
|
| 6971 |
- return $_[0]->hash_to_json($_[1]); |
|
| 6972 |
- } |
|
| 6973 |
- elsif ($reftype eq 'ARRAY') {
|
|
| 6974 |
- return $_[0]->array_to_json($_[1]); |
|
| 6975 |
- } |
|
| 6976 |
- else {
|
|
| 6977 |
- return 'null'; |
|
| 6978 |
- } |
|
| 7317 |
+ elsif ( $scheme ne 'http' ) {
|
|
| 7318 |
+ die(qq/Unsupported URL scheme '$scheme'\n/); |
|
| 6979 | 7319 |
} |
| 7320 |
+ $self->{fh} = 'IO::Socket::INET'->new(
|
|
| 7321 |
+ PeerHost => $host, |
|
| 7322 |
+ PeerPort => $port, |
|
| 7323 |
+ $self->{local_address} ?
|
|
| 7324 |
+ ( LocalAddr => $self->{local_address} ) : (),
|
|
| 7325 |
+ Proto => 'tcp', |
|
| 7326 |
+ Type => SOCK_STREAM, |
|
| 7327 |
+ Timeout => $self->{timeout}
|
|
| 7328 |
+ ) or die(qq/Could not connect to '$host:$port': $@\n/); |
|
| 6980 | 7329 |
|
| 7330 |
+ binmode($self->{fh})
|
|
| 7331 |
+ or die(qq/Could not binmode() socket: '$!'\n/); |
|
| 6981 | 7332 |
|
| 6982 |
- sub encode_error {
|
|
| 6983 |
- my $error = shift; |
|
| 6984 |
- Carp::croak "$error"; |
|
| 6985 |
- } |
|
| 6986 |
- |
|
| 7333 |
+ if ( $scheme eq 'https') {
|
|
| 7334 |
+ my $ssl_args = $self->_ssl_args($host); |
|
| 7335 |
+ IO::Socket::SSL->start_SSL( |
|
| 7336 |
+ $self->{fh},
|
|
| 7337 |
+ %$ssl_args, |
|
| 7338 |
+ SSL_create_ctx_callback => sub {
|
|
| 7339 |
+ my $ctx = shift; |
|
| 7340 |
+ Net::SSLeay::CTX_set_mode($ctx, Net::SSLeay::MODE_AUTO_RETRY()); |
|
| 7341 |
+ }, |
|
| 7342 |
+ ); |
|
| 6987 | 7343 |
|
| 6988 |
- sub _sort {
|
|
| 6989 |
- defined $keysort ? (sort $keysort (keys %{$_[0]})) : keys %{$_[0]};
|
|
| 7344 |
+ unless ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
|
|
| 7345 |
+ my $ssl_err = IO::Socket::SSL->errstr; |
|
| 7346 |
+ die(qq/SSL connection failed for $host: $ssl_err\n/); |
|
| 7347 |
+ } |
|
| 6990 | 7348 |
} |
| 6991 | 7349 |
|
| 7350 |
+ $self->{host} = $host;
|
|
| 7351 |
+ $self->{port} = $port;
|
|
| 6992 | 7352 |
|
| 6993 |
- sub _up_indent {
|
|
| 6994 |
- my $self = shift; |
|
| 6995 |
- my $space = ' ' x $indent_length; |
|
| 6996 |
- |
|
| 6997 |
- my ($pre,$post) = ('','');
|
|
| 6998 |
- |
|
| 6999 |
- $post = "\n" . $space x $indent_count; |
|
| 7353 |
+ return $self; |
|
| 7354 |
+ } |
|
| 7000 | 7355 |
|
| 7001 |
- $indent_count++; |
|
| 7356 |
+ sub close {
|
|
| 7357 |
+ @_ == 1 || die(q/Usage: $handle->close()/ . "\n"); |
|
| 7358 |
+ my ($self) = @_; |
|
| 7359 |
+ CORE::close($self->{fh})
|
|
| 7360 |
+ or die(qq/Could not close socket: '$!'\n/); |
|
| 7361 |
+ } |
|
| 7002 | 7362 |
|
| 7003 |
- $pre = "\n" . $space x $indent_count; |
|
| 7363 |
+ sub write {
|
|
| 7364 |
+ @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n"); |
|
| 7365 |
+ my ($self, $buf) = @_; |
|
| 7004 | 7366 |
|
| 7005 |
- return ($pre,$post); |
|
| 7367 |
+ if ( $] ge '5.008' ) {
|
|
| 7368 |
+ utf8::downgrade($buf, 1) |
|
| 7369 |
+ or die(qq/Wide character in write()\n/); |
|
| 7006 | 7370 |
} |
| 7007 | 7371 |
|
| 7372 |
+ my $len = length $buf; |
|
| 7373 |
+ my $off = 0; |
|
| 7008 | 7374 |
|
| 7009 |
- sub _down_indent { $indent_count--; }
|
|
| 7375 |
+ local $SIG{PIPE} = 'IGNORE';
|
|
| 7010 | 7376 |
|
| 7377 |
+ while () {
|
|
| 7378 |
+ $self->can_write |
|
| 7379 |
+ or die(qq/Timed out while waiting for socket to become ready for writing\n/); |
|
| 7380 |
+ my $r = syswrite($self->{fh}, $buf, $len, $off);
|
|
| 7381 |
+ if (defined $r) {
|
|
| 7382 |
+ $len -= $r; |
|
| 7383 |
+ $off += $r; |
|
| 7384 |
+ last unless $len > 0; |
|
| 7385 |
+ } |
|
| 7386 |
+ elsif ($! == EPIPE) {
|
|
| 7387 |
+ die(qq/Socket closed by remote server: $!\n/); |
|
| 7388 |
+ } |
|
| 7389 |
+ elsif ($! != EINTR) {
|
|
| 7390 |
+ if ($self->{fh}->can('errstr')){
|
|
| 7391 |
+ my $err = $self->{fh}->errstr();
|
|
| 7392 |
+ die (qq/Could not write to SSL socket: '$err'\n /); |
|
| 7393 |
+ } |
|
| 7394 |
+ else {
|
|
| 7395 |
+ die(qq/Could not write to socket: '$!'\n/); |
|
| 7396 |
+ } |
|
| 7011 | 7397 |
|
| 7012 |
- sub PP_encode_box {
|
|
| 7013 |
- {
|
|
| 7014 |
- depth => $depth, |
|
| 7015 |
- indent_count => $indent_count, |
|
| 7016 |
- }; |
|
| 7398 |
+ } |
|
| 7017 | 7399 |
} |
| 7400 |
+ return $off; |
|
| 7401 |
+ } |
|
| 7018 | 7402 |
|
| 7019 |
- } # Convert |
|
| 7403 |
+ sub read {
|
|
| 7404 |
+ @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n"); |
|
| 7405 |
+ my ($self, $len, $allow_partial) = @_; |
|
| 7020 | 7406 |
|
| 7407 |
+ my $buf = ''; |
|
| 7408 |
+ my $got = length $self->{rbuf};
|
|
| 7021 | 7409 |
|
| 7022 |
- sub _encode_ascii {
|
|
| 7023 |
- join('',
|
|
| 7024 |
- map {
|
|
| 7025 |
- $_ <= 127 ? |
|
| 7026 |
- chr($_) : |
|
| 7027 |
- $_ <= 65535 ? |
|
| 7028 |
- sprintf('\u%04x', $_) : sprintf('\u%x\u%x', _encode_surrogates($_));
|
|
| 7029 |
- } unpack('U*', $_[0])
|
|
| 7030 |
- ); |
|
| 7031 |
- } |
|
| 7032 |
- |
|
| 7410 |
+ if ($got) {
|
|
| 7411 |
+ my $take = ($got < $len) ? $got : $len; |
|
| 7412 |
+ $buf = substr($self->{rbuf}, 0, $take, '');
|
|
| 7413 |
+ $len -= $take; |
|
| 7414 |
+ } |
|
| 7033 | 7415 |
|
| 7034 |
- sub _encode_latin1 {
|
|
| 7035 |
- join('',
|
|
| 7036 |
- map {
|
|
| 7037 |
- $_ <= 255 ? |
|
| 7038 |
- chr($_) : |
|
| 7039 |
- $_ <= 65535 ? |
|
| 7040 |
- sprintf('\u%04x', $_) : sprintf('\u%x\u%x', _encode_surrogates($_));
|
|
| 7041 |
- } unpack('U*', $_[0])
|
|
| 7042 |
- ); |
|
| 7416 |
+ while ($len > 0) {
|
|
| 7417 |
+ $self->can_read |
|
| 7418 |
+ or die(q/Timed out while waiting for socket to become ready for reading/ . "\n"); |
|
| 7419 |
+ my $r = sysread($self->{fh}, $buf, $len, length $buf);
|
|
| 7420 |
+ if (defined $r) {
|
|
| 7421 |
+ last unless $r; |
|
| 7422 |
+ $len -= $r; |
|
| 7423 |
+ } |
|
| 7424 |
+ elsif ($! != EINTR) {
|
|
| 7425 |
+ if ($self->{fh}->can('errstr')){
|
|
| 7426 |
+ my $err = $self->{fh}->errstr();
|
|
| 7427 |
+ die (qq/Could not read from SSL socket: '$err'\n /); |
|
| 7428 |
+ } |
|
| 7429 |
+ else {
|
|
| 7430 |
+ die(qq/Could not read from socket: '$!'\n/); |
|
| 7431 |
+ } |
|
| 7432 |
+ } |
|
| 7433 |
+ } |
|
| 7434 |
+ if ($len && !$allow_partial) {
|
|
| 7435 |
+ die(qq/Unexpected end of stream\n/); |
|
| 7436 |
+ } |
|
| 7437 |
+ return $buf; |
|
| 7043 | 7438 |
} |
| 7044 | 7439 |
|
| 7440 |
+ sub readline {
|
|
| 7441 |
+ @_ == 1 || die(q/Usage: $handle->readline()/ . "\n"); |
|
| 7442 |
+ my ($self) = @_; |
|
| 7045 | 7443 |
|
| 7046 |
- sub _encode_surrogates { # from perlunicode
|
|
| 7047 |
- my $uni = $_[0] - 0x10000; |
|
| 7048 |
- return ($uni / 0x400 + 0xD800, $uni % 0x400 + 0xDC00); |
|
| 7444 |
+ while () {
|
|
| 7445 |
+ if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
|
|
| 7446 |
+ return $1; |
|
| 7447 |
+ } |
|
| 7448 |
+ if (length $self->{rbuf} >= $self->{max_line_size}) {
|
|
| 7449 |
+ die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/);
|
|
| 7450 |
+ } |
|
| 7451 |
+ $self->can_read |
|
| 7452 |
+ or die(qq/Timed out while waiting for socket to become ready for reading\n/); |
|
| 7453 |
+ my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
|
|
| 7454 |
+ if (defined $r) {
|
|
| 7455 |
+ last unless $r; |
|
| 7456 |
+ } |
|
| 7457 |
+ elsif ($! != EINTR) {
|
|
| 7458 |
+ if ($self->{fh}->can('errstr')){
|
|
| 7459 |
+ my $err = $self->{fh}->errstr();
|
|
| 7460 |
+ die (qq/Could not read from SSL socket: '$err'\n /); |
|
| 7461 |
+ } |
|
| 7462 |
+ else {
|
|
| 7463 |
+ die(qq/Could not read from socket: '$!'\n/); |
|
| 7464 |
+ } |
|
| 7465 |
+ } |
|
| 7466 |
+ } |
|
| 7467 |
+ die(qq/Unexpected end of stream while looking for line\n/); |
|
| 7049 | 7468 |
} |
| 7050 | 7469 |
|
| 7470 |
+ sub read_header_lines {
|
|
| 7471 |
+ @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n"); |
|
| 7472 |
+ my ($self, $headers) = @_; |
|
| 7473 |
+ $headers ||= {};
|
|
| 7474 |
+ my $lines = 0; |
|
| 7475 |
+ my $val; |
|
| 7476 |
+ |
|
| 7477 |
+ while () {
|
|
| 7478 |
+ my $line = $self->readline; |
|
| 7051 | 7479 |
|
| 7052 |
- sub _is_bignum {
|
|
| 7053 |
- $_[0]->isa('Math::BigInt') or $_[0]->isa('Math::BigFloat');
|
|
| 7480 |
+ if (++$lines >= $self->{max_header_lines}) {
|
|
| 7481 |
+ die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/);
|
|
| 7482 |
+ } |
|
| 7483 |
+ elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
|
|
| 7484 |
+ my ($field_name) = lc $1; |
|
| 7485 |
+ if (exists $headers->{$field_name}) {
|
|
| 7486 |
+ for ($headers->{$field_name}) {
|
|
| 7487 |
+ $_ = [$_] unless ref $_ eq "ARRAY"; |
|
| 7488 |
+ push @$_, $2; |
|
| 7489 |
+ $val = \$_->[-1]; |
|
| 7490 |
+ } |
|
| 7491 |
+ } |
|
| 7492 |
+ else {
|
|
| 7493 |
+ $val = \($headers->{$field_name} = $2);
|
|
| 7494 |
+ } |
|
| 7495 |
+ } |
|
| 7496 |
+ elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
|
|
| 7497 |
+ $val |
|
| 7498 |
+ or die(qq/Unexpected header continuation line\n/); |
|
| 7499 |
+ next unless length $1; |
|
| 7500 |
+ $$val .= ' ' if length $$val; |
|
| 7501 |
+ $$val .= $1; |
|
| 7502 |
+ } |
|
| 7503 |
+ elsif ($line =~ /\A \x0D?\x0A \z/x) {
|
|
| 7504 |
+ last; |
|
| 7505 |
+ } |
|
| 7506 |
+ else {
|
|
| 7507 |
+ die(q/Malformed header line: / . $Printable->($line) . "\n"); |
|
| 7508 |
+ } |
|
| 7509 |
+ } |
|
| 7510 |
+ return $headers; |
|
| 7054 | 7511 |
} |
| 7055 | 7512 |
|
| 7513 |
+ sub write_request {
|
|
| 7514 |
+ @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n"); |
|
| 7515 |
+ my($self, $request) = @_; |
|
| 7516 |
+ $self->write_request_header(@{$request}{qw/method uri headers/});
|
|
| 7517 |
+ $self->write_body($request) if $request->{cb};
|
|
| 7518 |
+ return; |
|
| 7519 |
+ } |
|
| 7056 | 7520 |
|
| 7521 |
+ my %HeaderCase = ( |
|
| 7522 |
+ 'content-md5' => 'Content-MD5', |
|
| 7523 |
+ 'etag' => 'ETag', |
|
| 7524 |
+ 'te' => 'TE', |
|
| 7525 |
+ 'www-authenticate' => 'WWW-Authenticate', |
|
| 7526 |
+ 'x-xss-protection' => 'X-XSS-Protection', |
|
| 7527 |
+ ); |
|
| 7057 | 7528 |
|
| 7058 |
- # |
|
| 7059 |
- # JSON => Perl |
|
| 7060 |
- # |
|
| 7061 |
- |
|
| 7062 |
- my $max_intsize; |
|
| 7529 |
+ sub write_header_lines {
|
|
| 7530 |
+ (@_ == 2 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers)/ . "\n"); |
|
| 7531 |
+ my($self, $headers) = @_; |
|
| 7063 | 7532 |
|
| 7064 |
- BEGIN {
|
|
| 7065 |
- my $checkint = 1111; |
|
| 7066 |
- for my $d (5..64) {
|
|
| 7067 |
- $checkint .= 1; |
|
| 7068 |
- my $int = eval qq| $checkint |; |
|
| 7069 |
- if ($int =~ /[eE]/) {
|
|
| 7070 |
- $max_intsize = $d - 1; |
|
| 7071 |
- last; |
|
| 7533 |
+ my $buf = ''; |
|
| 7534 |
+ while (my ($k, $v) = each %$headers) {
|
|
| 7535 |
+ my $field_name = lc $k; |
|
| 7536 |
+ if (exists $HeaderCase{$field_name}) {
|
|
| 7537 |
+ $field_name = $HeaderCase{$field_name};
|
|
| 7538 |
+ } |
|
| 7539 |
+ else {
|
|
| 7540 |
+ $field_name =~ /\A $Token+ \z/xo |
|
| 7541 |
+ or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n"); |
|
| 7542 |
+ $field_name =~ s/\b(\w)/\u$1/g; |
|
| 7543 |
+ $HeaderCase{lc $field_name} = $field_name;
|
|
| 7544 |
+ } |
|
| 7545 |
+ for (ref $v eq 'ARRAY' ? @$v : $v) {
|
|
| 7546 |
+ /[^\x0D\x0A]/ |
|
| 7547 |
+ or die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n"); |
|
| 7548 |
+ $buf .= "$field_name: $_\x0D\x0A"; |
|
| 7072 | 7549 |
} |
| 7073 | 7550 |
} |
| 7551 |
+ $buf .= "\x0D\x0A"; |
|
| 7552 |
+ return $self->write($buf); |
|
| 7074 | 7553 |
} |
| 7075 | 7554 |
|
| 7076 |
- { # PARSE
|
|
| 7077 |
- |
|
| 7078 |
- my %escapes = ( # by Jeremy Muhlich <jmuhlich [at] bitflood.org> |
|
| 7079 |
- b => "\x8", |
|
| 7080 |
- t => "\x9", |
|
| 7081 |
- n => "\xA", |
|
| 7082 |
- f => "\xC", |
|
| 7083 |
- r => "\xD", |
|
| 7084 |
- '\\' => '\\', |
|
| 7085 |
- '"' => '"', |
|
| 7086 |
- '/' => '/', |
|
| 7087 |
- ); |
|
| 7555 |
+ sub read_body {
|
|
| 7556 |
+ @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n"); |
|
| 7557 |
+ my ($self, $cb, $response) = @_; |
|
| 7558 |
+ my $te = $response->{headers}{'transfer-encoding'} || '';
|
|
| 7559 |
+ if ( grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ) {
|
|
| 7560 |
+ $self->read_chunked_body($cb, $response); |
|
| 7561 |
+ } |
|
| 7562 |
+ else {
|
|
| 7563 |
+ $self->read_content_body($cb, $response); |
|
| 7564 |
+ } |
|
| 7565 |
+ return; |
|
| 7566 |
+ } |
|
| 7088 | 7567 |
|
| 7089 |
- my $text; # json data |
|
| 7090 |
- my $at; # offset |
|
| 7091 |
- my $ch; # 1chracter |
|
| 7092 |
- my $len; # text length (changed according to UTF8 or NON UTF8) |
|
| 7093 |
- # INTERNAL |
|
| 7094 |
- my $depth; # nest counter |
|
| 7095 |
- my $encoding; # json text encoding |
|
| 7096 |
- my $is_valid_utf8; # temp variable |
|
| 7097 |
- my $utf8_len; # utf8 byte length |
|
| 7098 |
- # FLAGS |
|
| 7099 |
- my $utf8; # must be utf8 |
|
| 7100 |
- my $max_depth; # max nest nubmer of objects and arrays |
|
| 7101 |
- my $max_size; |
|
| 7102 |
- my $relaxed; |
|
| 7103 |
- my $cb_object; |
|
| 7104 |
- my $cb_sk_object; |
|
| 7568 |
+ sub write_body {
|
|
| 7569 |
+ @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n"); |
|
| 7570 |
+ my ($self, $request) = @_; |
|
| 7571 |
+ if ($request->{headers}{'content-length'}) {
|
|
| 7572 |
+ return $self->write_content_body($request); |
|
| 7573 |
+ } |
|
| 7574 |
+ else {
|
|
| 7575 |
+ return $self->write_chunked_body($request); |
|
| 7576 |
+ } |
|
| 7577 |
+ } |
|
| 7105 | 7578 |
|
| 7106 |
- my $F_HOOK; |
|
| 7579 |
+ sub read_content_body {
|
|
| 7580 |
+ @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n"); |
|
| 7581 |
+ my ($self, $cb, $response, $content_length) = @_; |
|
| 7582 |
+ $content_length ||= $response->{headers}{'content-length'};
|
|
| 7107 | 7583 |
|
| 7108 |
- my $allow_bigint; # using Math::BigInt |
|
| 7109 |
- my $singlequote; # loosely quoting |
|
| 7110 |
- my $loose; # |
|
| 7111 |
- my $allow_barekey; # bareKey |
|
| 7584 |
+ if ( $content_length ) {
|
|
| 7585 |
+ my $len = $content_length; |
|
| 7586 |
+ while ($len > 0) {
|
|
| 7587 |
+ my $read = ($len > BUFSIZE) ? BUFSIZE : $len; |
|
| 7588 |
+ $cb->($self->read($read, 0), $response); |
|
| 7589 |
+ $len -= $read; |
|
| 7590 |
+ } |
|
| 7591 |
+ } |
|
| 7592 |
+ else {
|
|
| 7593 |
+ my $chunk; |
|
| 7594 |
+ $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) ); |
|
| 7595 |
+ } |
|
| 7112 | 7596 |
|
| 7113 |
- # $opt flag |
|
| 7114 |
- # 0x00000001 .... decode_prefix |
|
| 7115 |
- # 0x10000000 .... incr_parse |
|
| 7597 |
+ return; |
|
| 7598 |
+ } |
|
| 7116 | 7599 |
|
| 7117 |
- sub PP_decode_json {
|
|
| 7118 |
- my ($self, $opt); # $opt is an effective flag during this decode_json. |
|
| 7600 |
+ sub write_content_body {
|
|
| 7601 |
+ @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n"); |
|
| 7602 |
+ my ($self, $request) = @_; |
|
| 7119 | 7603 |
|
| 7120 |
- ($self, $text, $opt) = @_; |
|
| 7604 |
+ my ($len, $content_length) = (0, $request->{headers}{'content-length'});
|
|
| 7605 |
+ while () {
|
|
| 7606 |
+ my $data = $request->{cb}->();
|
|
| 7121 | 7607 |
|
| 7122 |
- ($at, $ch, $depth) = (0, '', 0); |
|
| 7608 |
+ defined $data && length $data |
|
| 7609 |
+ or last; |
|
| 7123 | 7610 |
|
| 7124 |
- if ( !defined $text or ref $text ) {
|
|
| 7125 |
- decode_error("malformed JSON string, neither array, object, number, string or atom");
|
|
| 7611 |
+ if ( $] ge '5.008' ) {
|
|
| 7612 |
+ utf8::downgrade($data, 1) |
|
| 7613 |
+ or die(qq/Wide character in write_content()\n/); |
|
| 7126 | 7614 |
} |
| 7127 | 7615 |
|
| 7128 |
- my $idx = $self->{PROPS};
|
|
| 7616 |
+ $len += $self->write($data); |
|
| 7617 |
+ } |
|
| 7129 | 7618 |
|
| 7130 |
- ($utf8, $relaxed, $loose, $allow_bigint, $allow_barekey, $singlequote) |
|
| 7131 |
- = @{$idx}[P_UTF8, P_RELAXED, P_LOOSE .. P_ALLOW_SINGLEQUOTE];
|
|
| 7619 |
+ $len == $content_length |
|
| 7620 |
+ or die(qq/Content-Length missmatch (got: $len expected: $content_length)\n/); |
|
| 7132 | 7621 |
|
| 7133 |
- if ( $utf8 ) {
|
|
| 7134 |
- utf8::downgrade( $text, 1 ) or Carp::croak("Wide character in subroutine entry");
|
|
| 7135 |
- } |
|
| 7136 |
- else {
|
|
| 7137 |
- utf8::upgrade( $text ); |
|
| 7138 |
- } |
|
| 7622 |
+ return $len; |
|
| 7623 |
+ } |
|
| 7139 | 7624 |
|
| 7140 |
- $len = length $text; |
|
| 7625 |
+ sub read_chunked_body {
|
|
| 7626 |
+ @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n"); |
|
| 7627 |
+ my ($self, $cb, $response) = @_; |
|
| 7141 | 7628 |
|
| 7142 |
- ($max_depth, $max_size, $cb_object, $cb_sk_object, $F_HOOK) |
|
| 7143 |
- = @{$self}{qw/max_depth max_size cb_object cb_sk_object F_HOOK/};
|
|
| 7629 |
+ while () {
|
|
| 7630 |
+ my $head = $self->readline; |
|
| 7144 | 7631 |
|
| 7145 |
- if ($max_size > 1) {
|
|
| 7146 |
- use bytes; |
|
| 7147 |
- my $bytes = length $text; |
|
| 7148 |
- decode_error( |
|
| 7149 |
- sprintf("attempted decode of JSON text of %s bytes size, but max_size is set to %s"
|
|
| 7150 |
- , $bytes, $max_size), 1 |
|
| 7151 |
- ) if ($bytes > $max_size); |
|
| 7152 |
- } |
|
| 7632 |
+ $head =~ /\A ([A-Fa-f0-9]+)/x |
|
| 7633 |
+ or die(q/Malformed chunk head: / . $Printable->($head) . "\n"); |
|
| 7153 | 7634 |
|
| 7154 |
- # Currently no effect |
|
| 7155 |
- # should use regexp |
|
| 7156 |
- my @octets = unpack('C4', $text);
|
|
| 7157 |
- $encoding = ( $octets[0] and $octets[1]) ? 'UTF-8' |
|
| 7158 |
- : (!$octets[0] and $octets[1]) ? 'UTF-16BE' |
|
| 7159 |
- : (!$octets[0] and !$octets[1]) ? 'UTF-32BE' |
|
| 7160 |
- : ( $octets[2] ) ? 'UTF-16LE' |
|
| 7161 |
- : (!$octets[2] ) ? 'UTF-32LE' |
|
| 7162 |
- : 'unknown'; |
|
| 7635 |
+ my $len = hex($1) |
|
| 7636 |
+ or last; |
|
| 7163 | 7637 |
|
| 7164 |
- white(); # remove head white space |
|
| 7638 |
+ $self->read_content_body($cb, $response, $len); |
|
| 7165 | 7639 |
|
| 7166 |
- my $valid_start = defined $ch; # Is there a first character for JSON structure? |
|
| 7640 |
+ $self->read(2) eq "\x0D\x0A" |
|
| 7641 |
+ or die(qq/Malformed chunk: missing CRLF after chunk data\n/); |
|
| 7642 |
+ } |
|
| 7643 |
+ $self->read_header_lines($response->{headers});
|
|
| 7644 |
+ return; |
|
| 7645 |
+ } |
|
| 7167 | 7646 |
|
| 7168 |
- my $result = value(); |
|
| 7647 |
+ sub write_chunked_body {
|
|
| 7648 |
+ @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n"); |
|
| 7649 |
+ my ($self, $request) = @_; |
|
| 7169 | 7650 |
|
| 7170 |
- return undef if ( !$result && ( $opt & 0x10000000 ) ); # for incr_parse |
|
| 7651 |
+ my $len = 0; |
|
| 7652 |
+ while () {
|
|
| 7653 |
+ my $data = $request->{cb}->();
|
|
| 7171 | 7654 |
|
| 7172 |
- decode_error("malformed JSON string, neither array, object, number, string or atom") unless $valid_start;
|
|
| 7655 |
+ defined $data && length $data |
|
| 7656 |
+ or last; |
|
| 7173 | 7657 |
|
| 7174 |
- if ( !$idx->[ P_ALLOW_NONREF ] and !ref $result ) {
|
|
| 7175 |
- decode_error( |
|
| 7176 |
- 'JSON text must be an object or array (but found number, string, true, false or null,' |
|
| 7177 |
- . ' use allow_nonref to allow this)', 1); |
|
| 7658 |
+ if ( $] ge '5.008' ) {
|
|
| 7659 |
+ utf8::downgrade($data, 1) |
|
| 7660 |
+ or die(qq/Wide character in write_chunked_body()\n/); |
|
| 7178 | 7661 |
} |
| 7179 | 7662 |
|
| 7180 |
- Carp::croak('something wrong.') if $len < $at; # we won't arrive here.
|
|
| 7663 |
+ $len += length $data; |
|
| 7181 | 7664 |
|
| 7182 |
- my $consumed = defined $ch ? $at - 1 : $at; # consumed JSON text length |
|
| 7665 |
+ my $chunk = sprintf '%X', length $data; |
|
| 7666 |
+ $chunk .= "\x0D\x0A"; |
|
| 7667 |
+ $chunk .= $data; |
|
| 7668 |
+ $chunk .= "\x0D\x0A"; |
|
| 7183 | 7669 |
|
| 7184 |
- white(); # remove tail white space |
|
| 7670 |
+ $self->write($chunk); |
|
| 7671 |
+ } |
|
| 7672 |
+ $self->write("0\x0D\x0A");
|
|
| 7673 |
+ $self->write_header_lines($request->{trailer_cb}->())
|
|
| 7674 |
+ if ref $request->{trailer_cb} eq 'CODE';
|
|
| 7675 |
+ return $len; |
|
| 7676 |
+ } |
|
| 7185 | 7677 |
|
| 7186 |
- if ( $ch ) {
|
|
| 7187 |
- return ( $result, $consumed ) if ($opt & 0x00000001); # all right if decode_prefix |
|
| 7188 |
- decode_error("garbage after JSON object");
|
|
| 7189 |
- } |
|
| 7678 |
+ sub read_response_header {
|
|
| 7679 |
+ @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n"); |
|
| 7680 |
+ my ($self) = @_; |
|
| 7190 | 7681 |
|
| 7191 |
- ( $opt & 0x00000001 ) ? ( $result, $consumed ) : $result; |
|
| 7192 |
- } |
|
| 7682 |
+ my $line = $self->readline; |
|
| 7193 | 7683 |
|
| 7684 |
+ $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
|
|
| 7685 |
+ or die(q/Malformed Status-Line: / . $Printable->($line). "\n"); |
|
| 7194 | 7686 |
|
| 7195 |
- sub next_chr {
|
|
| 7196 |
- return $ch = undef if($at >= $len); |
|
| 7197 |
- $ch = substr($text, $at++, 1); |
|
| 7198 |
- } |
|
| 7687 |
+ my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4); |
|
| 7199 | 7688 |
|
| 7689 |
+ die (qq/Unsupported HTTP protocol: $protocol\n/) |
|
| 7690 |
+ unless $version =~ /0*1\.0*[01]/; |
|
| 7200 | 7691 |
|
| 7201 |
- sub value {
|
|
| 7202 |
- white(); |
|
| 7203 |
- return if(!defined $ch); |
|
| 7204 |
- return object() if($ch eq '{');
|
|
| 7205 |
- return array() if($ch eq '['); |
|
| 7206 |
- return string() if($ch eq '"' or ($singlequote and $ch eq "'")); |
|
| 7207 |
- return number() if($ch =~ /[0-9]/ or $ch eq '-'); |
|
| 7208 |
- return word(); |
|
| 7209 |
- } |
|
| 7692 |
+ return {
|
|
| 7693 |
+ status => $status, |
|
| 7694 |
+ reason => $reason, |
|
| 7695 |
+ headers => $self->read_header_lines, |
|
| 7696 |
+ protocol => $protocol, |
|
| 7697 |
+ }; |
|
| 7698 |
+ } |
|
| 7210 | 7699 |
|
| 7211 |
- sub string {
|
|
| 7212 |
- my ($i, $s, $t, $u); |
|
| 7213 |
- my $utf16; |
|
| 7214 |
- my $is_utf8; |
|
| 7700 |
+ sub write_request_header {
|
|
| 7701 |
+ @_ == 4 || die(q/Usage: $handle->write_request_header(method, request_uri, headers)/ . "\n"); |
|
| 7702 |
+ my ($self, $method, $request_uri, $headers) = @_; |
|
| 7215 | 7703 |
|
| 7216 |
- ($is_valid_utf8, $utf8_len) = ('', 0);
|
|
| 7704 |
+ return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
|
|
| 7705 |
+ + $self->write_header_lines($headers); |
|
| 7706 |
+ } |
|
| 7217 | 7707 |
|
| 7218 |
- $s = ''; # basically UTF8 flag on |
|
| 7708 |
+ sub _do_timeout {
|
|
| 7709 |
+ my ($self, $type, $timeout) = @_; |
|
| 7710 |
+ $timeout = $self->{timeout}
|
|
| 7711 |
+ unless defined $timeout && $timeout >= 0; |
|
| 7219 | 7712 |
|
| 7220 |
- if($ch eq '"' or ($singlequote and $ch eq "'")){
|
|
| 7221 |
- my $boundChar = $ch; |
|
| 7713 |
+ my $fd = fileno $self->{fh};
|
|
| 7714 |
+ defined $fd && $fd >= 0 |
|
| 7715 |
+ or die(qq/select(2): 'Bad file descriptor'\n/); |
|
| 7222 | 7716 |
|
| 7223 |
- OUTER: while( defined(next_chr()) ){
|
|
| 7717 |
+ my $initial = time; |
|
| 7718 |
+ my $pending = $timeout; |
|
| 7719 |
+ my $nfound; |
|
| 7224 | 7720 |
|
| 7225 |
- if($ch eq $boundChar){
|
|
| 7226 |
- next_chr(); |
|
| 7721 |
+ vec(my $fdset = '', $fd, 1) = 1; |
|
| 7227 | 7722 |
|
| 7228 |
- if ($utf16) {
|
|
| 7229 |
- decode_error("missing low surrogate character in surrogate pair");
|
|
| 7230 |
- } |
|
| 7723 |
+ while () {
|
|
| 7724 |
+ $nfound = ($type eq 'read') |
|
| 7725 |
+ ? select($fdset, undef, undef, $pending) |
|
| 7726 |
+ : select(undef, $fdset, undef, $pending) ; |
|
| 7727 |
+ if ($nfound == -1) {
|
|
| 7728 |
+ $! == EINTR |
|
| 7729 |
+ or die(qq/select(2): '$!'\n/); |
|
| 7730 |
+ redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; |
|
| 7731 |
+ $nfound = 0; |
|
| 7732 |
+ } |
|
| 7733 |
+ last; |
|
| 7734 |
+ } |
|
| 7735 |
+ $! = 0; |
|
| 7736 |
+ return $nfound; |
|
| 7737 |
+ } |
|
| 7231 | 7738 |
|
| 7232 |
- utf8::decode($s) if($is_utf8); |
|
| 7739 |
+ sub can_read {
|
|
| 7740 |
+ @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n"); |
|
| 7741 |
+ my $self = shift; |
|
| 7742 |
+ return $self->_do_timeout('read', @_)
|
|
| 7743 |
+ } |
|
| 7233 | 7744 |
|
| 7234 |
- return $s; |
|
| 7235 |
- } |
|
| 7236 |
- elsif($ch eq '\\'){
|
|
| 7237 |
- next_chr(); |
|
| 7238 |
- if(exists $escapes{$ch}){
|
|
| 7239 |
- $s .= $escapes{$ch};
|
|
| 7240 |
- } |
|
| 7241 |
- elsif($ch eq 'u'){ # UNICODE handling
|
|
| 7242 |
- my $u = ''; |
|
| 7745 |
+ sub can_write {
|
|
| 7746 |
+ @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n"); |
|
| 7747 |
+ my $self = shift; |
|
| 7748 |
+ return $self->_do_timeout('write', @_)
|
|
| 7749 |
+ } |
|
| 7243 | 7750 |
|
| 7244 |
- for(1..4){
|
|
| 7245 |
- $ch = next_chr(); |
|
| 7246 |
- last OUTER if($ch !~ /[0-9a-fA-F]/); |
|
| 7247 |
- $u .= $ch; |
|
| 7248 |
- } |
|
| 7751 |
+ # Try to find a CA bundle to validate the SSL cert, |
|
| 7752 |
+ # prefer Mozilla::CA or fallback to a system file |
|
| 7753 |
+ sub _find_CA_file {
|
|
| 7754 |
+ my $self = shift(); |
|
| 7249 | 7755 |
|
| 7250 |
- # U+D800 - U+DBFF |
|
| 7251 |
- if ($u =~ /^[dD][89abAB][0-9a-fA-F]{2}/) { # UTF-16 high surrogate?
|
|
| 7252 |
- $utf16 = $u; |
|
| 7253 |
- } |
|
| 7254 |
- # U+DC00 - U+DFFF |
|
| 7255 |
- elsif ($u =~ /^[dD][c-fC-F][0-9a-fA-F]{2}/) { # UTF-16 low surrogate?
|
|
| 7256 |
- unless (defined $utf16) {
|
|
| 7257 |
- decode_error("missing high surrogate character in surrogate pair");
|
|
| 7258 |
- } |
|
| 7259 |
- $is_utf8 = 1; |
|
| 7260 |
- $s .= JSON_PP_decode_surrogates($utf16, $u) || next; |
|
| 7261 |
- $utf16 = undef; |
|
| 7262 |
- } |
|
| 7263 |
- else {
|
|
| 7264 |
- if (defined $utf16) {
|
|
| 7265 |
- decode_error("surrogate pair expected");
|
|
| 7266 |
- } |
|
| 7756 |
+ return $self->{SSL_options}->{SSL_ca_file}
|
|
| 7757 |
+ if $self->{SSL_options}->{SSL_ca_file} and -e $self->{SSL_options}->{SSL_ca_file};
|
|
| 7267 | 7758 |
|
| 7268 |
- if ( ( my $hex = hex( $u ) ) > 127 ) {
|
|
| 7269 |
- $is_utf8 = 1; |
|
| 7270 |
- $s .= JSON_PP_decode_unicode($u) || next; |
|
| 7271 |
- } |
|
| 7272 |
- else {
|
|
| 7273 |
- $s .= chr $hex; |
|
| 7274 |
- } |
|
| 7275 |
- } |
|
| 7759 |
+ return Mozilla::CA::SSL_ca_file() |
|
| 7760 |
+ if eval { require Mozilla::CA };
|
|
| 7276 | 7761 |
|
| 7277 |
- } |
|
| 7278 |
- else{
|
|
| 7279 |
- unless ($loose) {
|
|
| 7280 |
- $at -= 2; |
|
| 7281 |
- decode_error('illegal backslash escape sequence in string');
|
|
| 7282 |
- } |
|
| 7283 |
- $s .= $ch; |
|
| 7284 |
- } |
|
| 7285 |
- } |
|
| 7286 |
- else{
|
|
| 7762 |
+ foreach my $ca_bundle (qw{
|
|
| 7763 |
+ /etc/ssl/certs/ca-certificates.crt |
|
| 7764 |
+ /etc/pki/tls/certs/ca-bundle.crt |
|
| 7765 |
+ /etc/ssl/ca-bundle.pem |
|
| 7766 |
+ } |
|
| 7767 |
+ ) {
|
|
| 7768 |
+ return $ca_bundle if -e $ca_bundle; |
|
| 7769 |
+ } |
|
| 7287 | 7770 |
|
| 7288 |
- if ( ord $ch > 127 ) {
|
|
| 7289 |
- if ( $utf8 ) {
|
|
| 7290 |
- unless( $ch = is_valid_utf8($ch) ) {
|
|
| 7291 |
- $at -= 1; |
|
| 7292 |
- decode_error("malformed UTF-8 character in JSON string");
|
|
| 7293 |
- } |
|
| 7294 |
- else {
|
|
| 7295 |
- $at += $utf8_len - 1; |
|
| 7296 |
- } |
|
| 7297 |
- } |
|
| 7298 |
- else {
|
|
| 7299 |
- utf8::encode( $ch ); |
|
| 7300 |
- } |
|
| 7771 |
+ die qq/Couldn't find a CA bundle with which to verify the SSL certificate.\n/ |
|
| 7772 |
+ . qq/Try installing Mozilla::CA from CPAN\n/; |
|
| 7773 |
+ } |
|
| 7301 | 7774 |
|
| 7302 |
- $is_utf8 = 1; |
|
| 7303 |
- } |
|
| 7775 |
+ sub _ssl_args {
|
|
| 7776 |
+ my ($self, $host) = @_; |
|
| 7304 | 7777 |
|
| 7305 |
- if (!$loose) {
|
|
| 7306 |
- if ($ch =~ /[\x00-\x1f\x22\x5c]/) { # '/' ok
|
|
| 7307 |
- $at--; |
|
| 7308 |
- decode_error('invalid character encountered while parsing JSON string');
|
|
| 7309 |
- } |
|
| 7310 |
- } |
|
| 7778 |
+ my %ssl_args = ( |
|
| 7779 |
+ SSL_hostname => $host, # SNI |
|
| 7780 |
+ ); |
|
| 7311 | 7781 |
|
| 7312 |
- $s .= $ch; |
|
| 7313 |
- } |
|
| 7314 |
- } |
|
| 7315 |
- } |
|
| 7782 |
+ if ($self->{verify_SSL}) {
|
|
| 7783 |
+ $ssl_args{SSL_verifycn_scheme} = 'http'; # enable CN validation
|
|
| 7784 |
+ $ssl_args{SSL_verifycn_name} = $host; # set validation hostname
|
|
| 7785 |
+ $ssl_args{SSL_verify_mode} = 0x01; # enable cert validation
|
|
| 7786 |
+ $ssl_args{SSL_ca_file} = $self->_find_CA_file;
|
|
| 7787 |
+ } |
|
| 7788 |
+ else {
|
|
| 7789 |
+ $ssl_args{SSL_verifycn_scheme} = 'none'; # disable CN validation
|
|
| 7790 |
+ $ssl_args{SSL_verify_mode} = 0x00; # disable cert validation
|
|
| 7791 |
+ } |
|
| 7316 | 7792 |
|
| 7317 |
- decode_error("unexpected end of string while parsing JSON string");
|
|
| 7793 |
+ # user options override settings from verify_SSL |
|
| 7794 |
+ for my $k ( keys %{$self->{SSL_options}} ) {
|
|
| 7795 |
+ $ssl_args{$k} = $self->{SSL_options}{$k} if $k =~ m/^SSL_/;
|
|
| 7318 | 7796 |
} |
| 7319 | 7797 |
|
| 7798 |
+ return \%ssl_args; |
|
| 7799 |
+ } |
|
| 7320 | 7800 |
|
| 7321 |
- sub white {
|
|
| 7322 |
- while( defined $ch ){
|
|
| 7323 |
- if($ch le ' '){
|
|
| 7324 |
- next_chr(); |
|
| 7325 |
- } |
|
| 7326 |
- elsif($ch eq '/'){
|
|
| 7327 |
- next_chr(); |
|
| 7328 |
- if(defined $ch and $ch eq '/'){
|
|
| 7329 |
- 1 while(defined(next_chr()) and $ch ne "\n" and $ch ne "\r"); |
|
| 7330 |
- } |
|
| 7331 |
- elsif(defined $ch and $ch eq '*'){
|
|
| 7332 |
- next_chr(); |
|
| 7333 |
- while(1){
|
|
| 7334 |
- if(defined $ch){
|
|
| 7335 |
- if($ch eq '*'){
|
|
| 7336 |
- if(defined(next_chr()) and $ch eq '/'){
|
|
| 7337 |
- next_chr(); |
|
| 7338 |
- last; |
|
| 7339 |
- } |
|
| 7340 |
- } |
|
| 7341 |
- else{
|
|
| 7342 |
- next_chr(); |
|
| 7343 |
- } |
|
| 7344 |
- } |
|
| 7345 |
- else{
|
|
| 7346 |
- decode_error("Unterminated comment");
|
|
| 7347 |
- } |
|
| 7348 |
- } |
|
| 7349 |
- next; |
|
| 7350 |
- } |
|
| 7351 |
- else{
|
|
| 7352 |
- $at--; |
|
| 7353 |
- decode_error("malformed JSON string, neither array, object, number, string or atom");
|
|
| 7354 |
- } |
|
| 7355 |
- } |
|
| 7356 |
- else{
|
|
| 7357 |
- if ($relaxed and $ch eq '#') { # correctly?
|
|
| 7358 |
- pos($text) = $at; |
|
| 7359 |
- $text =~ /\G([^\n]*(?:\r\n|\r|\n|$))/g; |
|
| 7360 |
- $at = pos($text); |
|
| 7361 |
- next_chr; |
|
| 7362 |
- next; |
|
| 7363 |
- } |
|
| 7801 |
+ 1; |
|
| 7364 | 7802 |
|
| 7365 |
- last; |
|
| 7366 |
- } |
|
| 7367 |
- } |
|
| 7368 |
- } |
|
| 7803 |
+ __END__ |
|
| 7369 | 7804 |
|
| 7805 |
+HTTP_TINY |
|
| 7806 |
+ |
|
| 7807 |
+$fatpacked{"JSON/PP.pm"} = <<'JSON_PP';
|
|
| 7808 |
+ package JSON::PP; |
|
| 7370 | 7809 |
|
| 7371 |
- sub array {
|
|
| 7372 |
- my $a = $_[0] || []; # you can use this code to use another array ref object. |
|
| 7810 |
+ # JSON-2.0 |
|
| 7373 | 7811 |
|
| 7374 |
- decode_error('json text or perl structure exceeds maximum nesting level (max_depth set too low?)')
|
|
| 7375 |
- if (++$depth > $max_depth); |
|
| 7812 |
+ use 5.005; |
|
| 7813 |
+ use strict; |
|
| 7814 |
+ use base qw(Exporter); |
|
| 7815 |
+ use overload (); |
|
| 7376 | 7816 |
|
| 7377 |
- next_chr(); |
|
| 7378 |
- white(); |
|
| 7817 |
+ use Carp (); |
|
| 7818 |
+ use B (); |
|
| 7819 |
+ #use Devel::Peek; |
|
| 7379 | 7820 |
|
| 7380 |
- if(defined $ch and $ch eq ']'){
|
|
| 7381 |
- --$depth; |
|
| 7382 |
- next_chr(); |
|
| 7383 |
- return $a; |
|
| 7384 |
- } |
|
| 7385 |
- else {
|
|
| 7386 |
- while(defined($ch)){
|
|
| 7387 |
- push @$a, value(); |
|
| 7821 |
+ $JSON::PP::VERSION = '2.27200'; |
|
| 7388 | 7822 |
|
| 7389 |
- white(); |
|
| 7823 |
+ @JSON::PP::EXPORT = qw(encode_json decode_json from_json to_json); |
|
| 7390 | 7824 |
|
| 7391 |
- if (!defined $ch) {
|
|
| 7392 |
- last; |
|
| 7393 |
- } |
|
| 7825 |
+ # instead of hash-access, i tried index-access for speed. |
|
| 7826 |
+ # but this method is not faster than what i expected. so it will be changed. |
|
| 7394 | 7827 |
|
| 7395 |
- if($ch eq ']'){
|
|
| 7396 |
- --$depth; |
|
| 7397 |
- next_chr(); |
|
| 7398 |
- return $a; |
|
| 7399 |
- } |
|
| 7828 |
+ use constant P_ASCII => 0; |
|
| 7829 |
+ use constant P_LATIN1 => 1; |
|
| 7830 |
+ use constant P_UTF8 => 2; |
|
| 7831 |
+ use constant P_INDENT => 3; |
|
| 7832 |
+ use constant P_CANONICAL => 4; |
|
| 7833 |
+ use constant P_SPACE_BEFORE => 5; |
|
| 7834 |
+ use constant P_SPACE_AFTER => 6; |
|
| 7835 |
+ use constant P_ALLOW_NONREF => 7; |
|
| 7836 |
+ use constant P_SHRINK => 8; |
|
| 7837 |
+ use constant P_ALLOW_BLESSED => 9; |
|
| 7838 |
+ use constant P_CONVERT_BLESSED => 10; |
|
| 7839 |
+ use constant P_RELAXED => 11; |
|
| 7400 | 7840 |
|
| 7401 |
- if($ch ne ','){
|
|
| 7402 |
- last; |
|
| 7403 |
- } |
|
| 7841 |
+ use constant P_LOOSE => 12; |
|
| 7842 |
+ use constant P_ALLOW_BIGNUM => 13; |
|
| 7843 |
+ use constant P_ALLOW_BAREKEY => 14; |
|
| 7844 |
+ use constant P_ALLOW_SINGLEQUOTE => 15; |
|
| 7845 |
+ use constant P_ESCAPE_SLASH => 16; |
|
| 7846 |
+ use constant P_AS_NONBLESSED => 17; |
|
| 7404 | 7847 |
|
| 7405 |
- next_chr(); |
|
| 7406 |
- white(); |
|
| 7848 |
+ use constant P_ALLOW_UNKNOWN => 18; |
|
| 7407 | 7849 |
|
| 7408 |
- if ($relaxed and $ch eq ']') {
|
|
| 7409 |
- --$depth; |
|
| 7410 |
- next_chr(); |
|
| 7411 |
- return $a; |
|
| 7412 |
- } |
|
| 7850 |
+ use constant OLD_PERL => $] < 5.008 ? 1 : 0; |
|
| 7413 | 7851 |
|
| 7414 |
- } |
|
| 7415 |
- } |
|
| 7852 |
+ BEGIN {
|
|
| 7853 |
+ my @xs_compati_bit_properties = qw( |
|
| 7854 |
+ latin1 ascii utf8 indent canonical space_before space_after allow_nonref shrink |
|
| 7855 |
+ allow_blessed convert_blessed relaxed allow_unknown |
|
| 7856 |
+ ); |
|
| 7857 |
+ my @pp_bit_properties = qw( |
|
| 7858 |
+ allow_singlequote allow_bignum loose |
|
| 7859 |
+ allow_barekey escape_slash as_nonblessed |
|
| 7860 |
+ ); |
|
| 7416 | 7861 |
|
| 7417 |
- decode_error(", or ] expected while parsing array");
|
|
| 7862 |
+ # Perl version check, Unicode handling is enable? |
|
| 7863 |
+ # Helper module sets @JSON::PP::_properties. |
|
| 7864 |
+ if ($] < 5.008 ) {
|
|
| 7865 |
+ my $helper = $] >= 5.006 ? 'JSON::PP::Compat5006' : 'JSON::PP::Compat5005'; |
|
| 7866 |
+ eval qq| require $helper |; |
|
| 7867 |
+ if ($@) { Carp::croak $@; }
|
|
| 7418 | 7868 |
} |
| 7419 | 7869 |
|
| 7870 |
+ for my $name (@xs_compati_bit_properties, @pp_bit_properties) {
|
|
| 7871 |
+ my $flag_name = 'P_' . uc($name); |
|
| 7420 | 7872 |
|
| 7421 |
- sub object {
|
|
| 7422 |
- my $o = $_[0] || {}; # you can use this code to use another hash ref object.
|
|
| 7423 |
- my $k; |
|
| 7873 |
+ eval qq/ |
|
| 7874 |
+ sub $name {
|
|
| 7875 |
+ my \$enable = defined \$_[1] ? \$_[1] : 1; |
|
| 7424 | 7876 |
|
| 7425 |
- decode_error('json text or perl structure exceeds maximum nesting level (max_depth set too low?)')
|
|
| 7426 |
- if (++$depth > $max_depth); |
|
| 7427 |
- next_chr(); |
|
| 7428 |
- white(); |
|
| 7877 |
+ if (\$enable) {
|
|
| 7878 |
+ \$_[0]->{PROPS}->[$flag_name] = 1;
|
|
| 7879 |
+ } |
|
| 7880 |
+ else {
|
|
| 7881 |
+ \$_[0]->{PROPS}->[$flag_name] = 0;
|
|
| 7882 |
+ } |
|
| 7429 | 7883 |
|
| 7430 |
- if(defined $ch and $ch eq '}'){
|
|
| 7431 |
- --$depth; |
|
| 7432 |
- next_chr(); |
|
| 7433 |
- if ($F_HOOK) {
|
|
| 7434 |
- return _json_object_hook($o); |
|
| 7884 |
+ \$_[0]; |
|
| 7435 | 7885 |
} |
| 7436 |
- return $o; |
|
| 7437 |
- } |
|
| 7438 |
- else {
|
|
| 7439 |
- while (defined $ch) {
|
|
| 7440 |
- $k = ($allow_barekey and $ch ne '"' and $ch ne "'") ? bareKey() : string(); |
|
| 7441 |
- white(); |
|
| 7442 | 7886 |
|
| 7443 |
- if(!defined $ch or $ch ne ':'){
|
|
| 7444 |
- $at--; |
|
| 7445 |
- decode_error("':' expected");
|
|
| 7446 |
- } |
|
| 7887 |
+ sub get_$name {
|
|
| 7888 |
+ \$_[0]->{PROPS}->[$flag_name] ? 1 : '';
|
|
| 7889 |
+ } |
|
| 7890 |
+ /; |
|
| 7891 |
+ } |
|
| 7447 | 7892 |
|
| 7448 |
- next_chr(); |
|
| 7449 |
- $o->{$k} = value();
|
|
| 7450 |
- white(); |
|
| 7893 |
+ } |
|
| 7451 | 7894 |
|
| 7452 |
- last if (!defined $ch); |
|
| 7453 | 7895 |
|
| 7454 |
- if($ch eq '}'){
|
|
| 7455 |
- --$depth; |
|
| 7456 |
- next_chr(); |
|
| 7457 |
- if ($F_HOOK) {
|
|
| 7458 |
- return _json_object_hook($o); |
|
| 7459 |
- } |
|
| 7460 |
- return $o; |
|
| 7461 |
- } |
|
| 7462 | 7896 |
|
| 7463 |
- if($ch ne ','){
|
|
| 7464 |
- last; |
|
| 7465 |
- } |
|
| 7897 |
+ # Functions |
|
| 7466 | 7898 |
|
| 7467 |
- next_chr(); |
|
| 7468 |
- white(); |
|
| 7899 |
+ my %encode_allow_method |
|
| 7900 |
+ = map {($_ => 1)} qw/utf8 pretty allow_nonref latin1 self_encode escape_slash
|
|
| 7901 |
+ allow_blessed convert_blessed indent indent_length allow_bignum |
|
| 7902 |
+ as_nonblessed |
|
| 7903 |
+ /; |
|
| 7904 |
+ my %decode_allow_method |
|
| 7905 |
+ = map {($_ => 1)} qw/utf8 allow_nonref loose allow_singlequote allow_bignum
|
|
| 7906 |
+ allow_barekey max_size relaxed/; |
|
| 7469 | 7907 |
|
| 7470 |
- if ($relaxed and $ch eq '}') {
|
|
| 7471 |
- --$depth; |
|
| 7472 |
- next_chr(); |
|
| 7473 |
- if ($F_HOOK) {
|
|
| 7474 |
- return _json_object_hook($o); |
|
| 7475 |
- } |
|
| 7476 |
- return $o; |
|
| 7477 |
- } |
|
| 7478 | 7908 |
|
| 7479 |
- } |
|
| 7909 |
+ my $JSON; # cache |
|
| 7480 | 7910 |
|
| 7481 |
- } |
|
| 7911 |
+ sub encode_json ($) { # encode
|
|
| 7912 |
+ ($JSON ||= __PACKAGE__->new->utf8)->encode(@_); |
|
| 7913 |
+ } |
|
| 7482 | 7914 |
|
| 7483 |
- $at--; |
|
| 7484 |
- decode_error(", or } expected while parsing object/hash");
|
|
| 7485 |
- } |
|
| 7486 | 7915 |
|
| 7916 |
+ sub decode_json { # decode
|
|
| 7917 |
+ ($JSON ||= __PACKAGE__->new->utf8)->decode(@_); |
|
| 7918 |
+ } |
|
| 7487 | 7919 |
|
| 7488 |
- sub bareKey { # doesn't strictly follow Standard ECMA-262 3rd Edition
|
|
| 7489 |
- my $key; |
|
| 7490 |
- while($ch =~ /[^\x00-\x23\x25-\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F]/){
|
|
| 7491 |
- $key .= $ch; |
|
| 7492 |
- next_chr(); |
|
| 7493 |
- } |
|
| 7494 |
- return $key; |
|
| 7495 |
- } |
|
| 7920 |
+ # Obsoleted |
|
| 7496 | 7921 |
|
| 7922 |
+ sub to_json($) {
|
|
| 7923 |
+ Carp::croak ("JSON::PP::to_json has been renamed to encode_json.");
|
|
| 7924 |
+ } |
|
| 7497 | 7925 |
|
| 7498 |
- sub word {
|
|
| 7499 |
- my $word = substr($text,$at-1,4); |
|
| 7500 | 7926 |
|
| 7501 |
- if($word eq 'true'){
|
|
| 7502 |
- $at += 3; |
|
| 7503 |
- next_chr; |
|
| 7504 |
- return $JSON::PP::true; |
|
| 7505 |
- } |
|
| 7506 |
- elsif($word eq 'null'){
|
|
| 7507 |
- $at += 3; |
|
| 7508 |
- next_chr; |
|
| 7509 |
- return undef; |
|
| 7510 |
- } |
|
| 7511 |
- elsif($word eq 'fals'){
|
|
| 7512 |
- $at += 3; |
|
| 7513 |
- if(substr($text,$at,1) eq 'e'){
|
|
| 7514 |
- $at++; |
|
| 7515 |
- next_chr; |
|
| 7516 |
- return $JSON::PP::false; |
|
| 7517 |
- } |
|
| 7518 |
- } |
|
| 7927 |
+ sub from_json($) {
|
|
| 7928 |
+ Carp::croak ("JSON::PP::from_json has been renamed to decode_json.");
|
|
| 7929 |
+ } |
|
| 7519 | 7930 |
|
| 7520 |
- $at--; # for decode_error report |
|
| 7521 | 7931 |
|
| 7522 |
- decode_error("'null' expected") if ($word =~ /^n/);
|
|
| 7523 |
- decode_error("'true' expected") if ($word =~ /^t/);
|
|
| 7524 |
- decode_error("'false' expected") if ($word =~ /^f/);
|
|
| 7525 |
- decode_error("malformed JSON string, neither array, object, number, string or atom");
|
|
| 7526 |
- } |
|
| 7932 |
+ # Methods |
|
| 7527 | 7933 |
|
| 7934 |
+ sub new {
|
|
| 7935 |
+ my $class = shift; |
|
| 7936 |
+ my $self = {
|
|
| 7937 |
+ max_depth => 512, |
|
| 7938 |
+ max_size => 0, |
|
| 7939 |
+ indent => 0, |
|
| 7940 |
+ FLAGS => 0, |
|
| 7941 |
+ fallback => sub { encode_error('Invalid value. JSON can only reference.') },
|
|
| 7942 |
+ indent_length => 3, |
|
| 7943 |
+ }; |
|
| 7528 | 7944 |
|
| 7529 |
- sub number {
|
|
| 7530 |
- my $n = ''; |
|
| 7531 |
- my $v; |
|
| 7945 |
+ bless $self, $class; |
|
| 7946 |
+ } |
|
| 7532 | 7947 |
|
| 7533 |
- # According to RFC4627, hex or oct digts are invalid. |
|
| 7534 |
- if($ch eq '0'){
|
|
| 7535 |
- my $peek = substr($text,$at,1); |
|
| 7536 |
- my $hex = $peek =~ /[xX]/; # 0 or 1 |
|
| 7537 | 7948 |
|
| 7538 |
- if($hex){
|
|
| 7539 |
- decode_error("malformed number (leading zero must not be followed by another digit)");
|
|
| 7540 |
- ($n) = ( substr($text, $at+1) =~ /^([0-9a-fA-F]+)/); |
|
| 7541 |
- } |
|
| 7542 |
- else{ # oct
|
|
| 7543 |
- ($n) = ( substr($text, $at) =~ /^([0-7]+)/); |
|
| 7544 |
- if (defined $n and length $n > 1) {
|
|
| 7545 |
- decode_error("malformed number (leading zero must not be followed by another digit)");
|
|
| 7546 |
- } |
|
| 7547 |
- } |
|
| 7548 |
- |
|
| 7549 |
- if(defined $n and length($n)){
|
|
| 7550 |
- if (!$hex and length($n) == 1) {
|
|
| 7551 |
- decode_error("malformed number (leading zero must not be followed by another digit)");
|
|
| 7552 |
- } |
|
| 7553 |
- $at += length($n) + $hex; |
|
| 7554 |
- next_chr; |
|
| 7555 |
- return $hex ? hex($n) : oct($n); |
|
| 7556 |
- } |
|
| 7557 |
- } |
|
| 7558 |
- |
|
| 7559 |
- if($ch eq '-'){
|
|
| 7560 |
- $n = '-'; |
|
| 7561 |
- next_chr; |
|
| 7562 |
- if (!defined $ch or $ch !~ /\d/) {
|
|
| 7563 |
- decode_error("malformed number (no digits after initial minus)");
|
|
| 7564 |
- } |
|
| 7565 |
- } |
|
| 7566 |
- |
|
| 7567 |
- while(defined $ch and $ch =~ /\d/){
|
|
| 7568 |
- $n .= $ch; |
|
| 7569 |
- next_chr; |
|
| 7570 |
- } |
|
| 7949 |
+ sub encode {
|
|
| 7950 |
+ return $_[0]->PP_encode_json($_[1]); |
|
| 7951 |
+ } |
|
| 7571 | 7952 |
|
| 7572 |
- if(defined $ch and $ch eq '.'){
|
|
| 7573 |
- $n .= '.'; |
|
| 7574 | 7953 |
|
| 7575 |
- next_chr; |
|
| 7576 |
- if (!defined $ch or $ch !~ /\d/) {
|
|
| 7577 |
- decode_error("malformed number (no digits after decimal point)");
|
|
| 7578 |
- } |
|
| 7579 |
- else {
|
|
| 7580 |
- $n .= $ch; |
|
| 7581 |
- } |
|
| 7954 |
+ sub decode {
|
|
| 7955 |
+ return $_[0]->PP_decode_json($_[1], 0x00000000); |
|
| 7956 |
+ } |
|
| 7582 | 7957 |
|
| 7583 |
- while(defined(next_chr) and $ch =~ /\d/){
|
|
| 7584 |
- $n .= $ch; |
|
| 7585 |
- } |
|
| 7586 |
- } |
|
| 7587 | 7958 |
|
| 7588 |
- if(defined $ch and ($ch eq 'e' or $ch eq 'E')){
|
|
| 7589 |
- $n .= $ch; |
|
| 7590 |
- next_chr; |
|
| 7959 |
+ sub decode_prefix {
|
|
| 7960 |
+ return $_[0]->PP_decode_json($_[1], 0x00000001); |
|
| 7961 |
+ } |
|
| 7591 | 7962 |
|
| 7592 |
- if(defined($ch) and ($ch eq '+' or $ch eq '-')){
|
|
| 7593 |
- $n .= $ch; |
|
| 7594 |
- next_chr; |
|
| 7595 |
- if (!defined $ch or $ch =~ /\D/) {
|
|
| 7596 |
- decode_error("malformed number (no digits after exp sign)");
|
|
| 7597 |
- } |
|
| 7598 |
- $n .= $ch; |
|
| 7599 |
- } |
|
| 7600 |
- elsif(defined($ch) and $ch =~ /\d/){
|
|
| 7601 |
- $n .= $ch; |
|
| 7602 |
- } |
|
| 7603 |
- else {
|
|
| 7604 |
- decode_error("malformed number (no digits after exp sign)");
|
|
| 7605 |
- } |
|
| 7606 | 7963 |
|
| 7607 |
- while(defined(next_chr) and $ch =~ /\d/){
|
|
| 7608 |
- $n .= $ch; |
|
| 7609 |
- } |
|
| 7964 |
+ # accessor |
|
| 7610 | 7965 |
|
| 7611 |
- } |
|
| 7612 | 7966 |
|
| 7613 |
- $v .= $n; |
|
| 7967 |
+ # pretty printing |
|
| 7614 | 7968 |
|
| 7615 |
- if ($v !~ /[.eE]/ and length $v > $max_intsize) {
|
|
| 7616 |
- if ($allow_bigint) { # from Adam Sussman
|
|
| 7617 |
- require Math::BigInt; |
|
| 7618 |
- return Math::BigInt->new($v); |
|
| 7619 |
- } |
|
| 7620 |
- else {
|
|
| 7621 |
- return "$v"; |
|
| 7622 |
- } |
|
| 7623 |
- } |
|
| 7624 |
- elsif ($allow_bigint) {
|
|
| 7625 |
- require Math::BigFloat; |
|
| 7626 |
- return Math::BigFloat->new($v); |
|
| 7627 |
- } |
|
| 7969 |
+ sub pretty {
|
|
| 7970 |
+ my ($self, $v) = @_; |
|
| 7971 |
+ my $enable = defined $v ? $v : 1; |
|
| 7628 | 7972 |
|
| 7629 |
- return 0+$v; |
|
| 7973 |
+ if ($enable) { # indent_length(3) for JSON::XS compatibility
|
|
| 7974 |
+ $self->indent(1)->indent_length(3)->space_before(1)->space_after(1); |
|
| 7975 |
+ } |
|
| 7976 |
+ else {
|
|
| 7977 |
+ $self->indent(0)->space_before(0)->space_after(0); |
|
| 7630 | 7978 |
} |
| 7631 | 7979 |
|
| 7980 |
+ $self; |
|
| 7981 |
+ } |
|
| 7632 | 7982 |
|
| 7633 |
- sub is_valid_utf8 {
|
|
| 7983 |
+ # etc |
|
| 7634 | 7984 |
|
| 7635 |
- $utf8_len = $_[0] =~ /[\x00-\x7F]/ ? 1 |
|
| 7636 |
- : $_[0] =~ /[\xC2-\xDF]/ ? 2 |
|
| 7637 |
- : $_[0] =~ /[\xE0-\xEF]/ ? 3 |
|
| 7638 |
- : $_[0] =~ /[\xF0-\xF4]/ ? 4 |
|
| 7639 |
- : 0 |
|
| 7640 |
- ; |
|
| 7985 |
+ sub max_depth {
|
|
| 7986 |
+ my $max = defined $_[1] ? $_[1] : 0x80000000; |
|
| 7987 |
+ $_[0]->{max_depth} = $max;
|
|
| 7988 |
+ $_[0]; |
|
| 7989 |
+ } |
|
| 7641 | 7990 |
|
| 7642 |
- return unless $utf8_len; |
|
| 7643 | 7991 |
|
| 7644 |
- my $is_valid_utf8 = substr($text, $at - 1, $utf8_len); |
|
| 7992 |
+ sub get_max_depth { $_[0]->{max_depth}; }
|
|
| 7645 | 7993 |
|
| 7646 |
- return ( $is_valid_utf8 =~ /^(?: |
|
| 7647 |
- [\x00-\x7F] |
|
| 7648 |
- |[\xC2-\xDF][\x80-\xBF] |
|
| 7649 |
- |[\xE0][\xA0-\xBF][\x80-\xBF] |
|
| 7650 |
- |[\xE1-\xEC][\x80-\xBF][\x80-\xBF] |
|
| 7651 |
- |[\xED][\x80-\x9F][\x80-\xBF] |
|
| 7652 |
- |[\xEE-\xEF][\x80-\xBF][\x80-\xBF] |
|
| 7653 |
- |[\xF0][\x90-\xBF][\x80-\xBF][\x80-\xBF] |
|
| 7654 |
- |[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF] |
|
| 7655 |
- |[\xF4][\x80-\x8F][\x80-\xBF][\x80-\xBF] |
|
| 7656 |
- )$/x ) ? $is_valid_utf8 : ''; |
|
| 7657 |
- } |
|
| 7658 | 7994 |
|
| 7995 |
+ sub max_size {
|
|
| 7996 |
+ my $max = defined $_[1] ? $_[1] : 0; |
|
| 7997 |
+ $_[0]->{max_size} = $max;
|
|
| 7998 |
+ $_[0]; |
|
| 7999 |
+ } |
|
| 7659 | 8000 |
|
| 7660 |
- sub decode_error {
|
|
| 7661 |
- my $error = shift; |
|
| 7662 |
- my $no_rep = shift; |
|
| 7663 |
- my $str = defined $text ? substr($text, $at) : ''; |
|
| 7664 |
- my $mess = ''; |
|
| 7665 |
- my $type = $] >= 5.008 ? 'U*' |
|
| 7666 |
- : $] < 5.006 ? 'C*' |
|
| 7667 |
- : utf8::is_utf8( $str ) ? 'U*' # 5.6 |
|
| 7668 |
- : 'C*' |
|
| 7669 |
- ; |
|
| 7670 | 8001 |
|
| 7671 |
- for my $c ( unpack( $type, $str ) ) { # emulate pv_uni_display() ?
|
|
| 7672 |
- $mess .= $c == 0x07 ? '\a' |
|
| 7673 |
- : $c == 0x09 ? '\t' |
|
| 7674 |
- : $c == 0x0a ? '\n' |
|
| 7675 |
- : $c == 0x0d ? '\r' |
|
| 7676 |
- : $c == 0x0c ? '\f' |
|
| 7677 |
- : $c < 0x20 ? sprintf('\x{%x}', $c)
|
|
| 7678 |
- : $c == 0x5c ? '\\\\' |
|
| 7679 |
- : $c < 0x80 ? chr($c) |
|
| 7680 |
- : sprintf('\x{%x}', $c)
|
|
| 7681 |
- ; |
|
| 7682 |
- if ( length $mess >= 20 ) {
|
|
| 7683 |
- $mess .= '...'; |
|
| 7684 |
- last; |
|
| 7685 |
- } |
|
| 7686 |
- } |
|
| 8002 |
+ sub get_max_size { $_[0]->{max_size}; }
|
|
| 7687 | 8003 |
|
| 7688 |
- unless ( length $mess ) {
|
|
| 7689 |
- $mess = '(end of string)'; |
|
| 7690 |
- } |
|
| 7691 | 8004 |
|
| 7692 |
- Carp::croak ( |
|
| 7693 |
- $no_rep ? "$error" : "$error, at character offset $at (before \"$mess\")" |
|
| 7694 |
- ); |
|
| 8005 |
+ sub filter_json_object {
|
|
| 8006 |
+ $_[0]->{cb_object} = defined $_[1] ? $_[1] : 0;
|
|
| 8007 |
+ $_[0]->{F_HOOK} = ($_[0]->{cb_object} or $_[0]->{cb_sk_object}) ? 1 : 0;
|
|
| 8008 |
+ $_[0]; |
|
| 8009 |
+ } |
|
| 7695 | 8010 |
|
| 8011 |
+ sub filter_json_single_key_object {
|
|
| 8012 |
+ if (@_ > 1) {
|
|
| 8013 |
+ $_[0]->{cb_sk_object}->{$_[1]} = $_[2];
|
|
| 7696 | 8014 |
} |
| 8015 |
+ $_[0]->{F_HOOK} = ($_[0]->{cb_object} or $_[0]->{cb_sk_object}) ? 1 : 0;
|
|
| 8016 |
+ $_[0]; |
|
| 8017 |
+ } |
|
| 7697 | 8018 |
|
| 7698 |
- |
|
| 7699 |
- sub _json_object_hook {
|
|
| 7700 |
- my $o = $_[0]; |
|
| 7701 |
- my @ks = keys %{$o};
|
|
| 7702 |
- |
|
| 7703 |
- if ( $cb_sk_object and @ks == 1 and exists $cb_sk_object->{ $ks[0] } and ref $cb_sk_object->{ $ks[0] } ) {
|
|
| 7704 |
- my @val = $cb_sk_object->{ $ks[0] }->( $o->{$ks[0]} );
|
|
| 7705 |
- if (@val == 1) {
|
|
| 7706 |
- return $val[0]; |
|
| 7707 |
- } |
|
| 7708 |
- } |
|
| 7709 |
- |
|
| 7710 |
- my @val = $cb_object->($o) if ($cb_object); |
|
| 7711 |
- if (@val == 0 or @val > 1) {
|
|
| 7712 |
- return $o; |
|
| 7713 |
- } |
|
| 7714 |
- else {
|
|
| 7715 |
- return $val[0]; |
|
| 7716 |
- } |
|
| 8019 |
+ sub indent_length {
|
|
| 8020 |
+ if (!defined $_[1] or $_[1] > 15 or $_[1] < 0) {
|
|
| 8021 |
+ Carp::carp "The acceptable range of indent_length() is 0 to 15."; |
|
| 7717 | 8022 |
} |
| 7718 |
- |
|
| 7719 |
- |
|
| 7720 |
- sub PP_decode_box {
|
|
| 7721 |
- {
|
|
| 7722 |
- text => $text, |
|
| 7723 |
- at => $at, |
|
| 7724 |
- ch => $ch, |
|
| 7725 |
- len => $len, |
|
| 7726 |
- depth => $depth, |
|
| 7727 |
- encoding => $encoding, |
|
| 7728 |
- is_valid_utf8 => $is_valid_utf8, |
|
| 7729 |
- }; |
|
| 8023 |
+ else {
|
|
| 8024 |
+ $_[0]->{indent_length} = $_[1];
|
|
| 7730 | 8025 |
} |
| 8026 |
+ $_[0]; |
|
| 8027 |
+ } |
|
| 7731 | 8028 |
|
| 7732 |
- } # PARSE |
|
| 7733 |
- |
|
| 7734 |
- |
|
| 7735 |
- sub _decode_surrogates { # from perlunicode
|
|
| 7736 |
- my $uni = 0x10000 + (hex($_[0]) - 0xD800) * 0x400 + (hex($_[1]) - 0xDC00); |
|
| 7737 |
- my $un = pack('U*', $uni);
|
|
| 7738 |
- utf8::encode( $un ); |
|
| 7739 |
- return $un; |
|
| 8029 |
+ sub get_indent_length {
|
|
| 8030 |
+ $_[0]->{indent_length};
|
|
| 7740 | 8031 |
} |
| 7741 | 8032 |
|
| 8033 |
+ sub sort_by {
|
|
| 8034 |
+ $_[0]->{sort_by} = defined $_[1] ? $_[1] : 1;
|
|
| 8035 |
+ $_[0]; |
|
| 8036 |
+ } |
|
| 7742 | 8037 |
|
| 7743 |
- sub _decode_unicode {
|
|
| 7744 |
- my $un = pack('U', hex shift);
|
|
| 7745 |
- utf8::encode( $un ); |
|
| 7746 |
- return $un; |
|
| 8038 |
+ sub allow_bigint {
|
|
| 8039 |
+ Carp::carp("allow_bigint() is obsoleted. use allow_bignum() insted.");
|
|
| 7747 | 8040 |
} |
| 7748 | 8041 |
|
| 7749 |
- # |
|
| 7750 |
- # Setup for various Perl versions (the code from JSON::PP58) |
|
| 7751 |
- # |
|
| 8042 |
+ ############################### |
|
| 7752 | 8043 |
|
| 7753 |
- BEGIN {
|
|
| 8044 |
+ ### |
|
| 8045 |
+ ### Perl => JSON |
|
| 8046 |
+ ### |
|
| 7754 | 8047 |
|
| 7755 |
- unless ( defined &utf8::is_utf8 ) {
|
|
| 7756 |
- require Encode; |
|
| 7757 |
- *utf8::is_utf8 = *Encode::is_utf8; |
|
| 7758 |
- } |
|
| 7759 | 8048 |
|
| 7760 |
- if ( $] >= 5.008 ) {
|
|
| 7761 |
- *JSON::PP::JSON_PP_encode_ascii = \&_encode_ascii; |
|
| 7762 |
- *JSON::PP::JSON_PP_encode_latin1 = \&_encode_latin1; |
|
| 7763 |
- *JSON::PP::JSON_PP_decode_surrogates = \&_decode_surrogates; |
|
| 7764 |
- *JSON::PP::JSON_PP_decode_unicode = \&_decode_unicode; |
|
| 7765 |
- } |
|
| 8049 |
+ { # Convert
|
|
| 7766 | 8050 |
|
| 7767 |
- if ($] >= 5.008 and $] < 5.008003) { # join() in 5.8.0 - 5.8.2 is broken.
|
|
| 7768 |
- package JSON::PP; |
|
| 7769 |
- require subs; |
|
| 7770 |
- subs->import('join');
|
|
| 7771 |
- eval q| |
|
| 7772 |
- sub join {
|
|
| 7773 |
- return '' if (@_ < 2); |
|
| 7774 |
- my $j = shift; |
|
| 7775 |
- my $str = shift; |
|
| 7776 |
- for (@_) { $str .= $j . $_; }
|
|
| 7777 |
- return $str; |
|
| 7778 |
- } |
|
| 7779 |
- |; |
|
| 7780 |
- } |
|
| 7781 |
- |
|
| 7782 |
- |
|
| 7783 |
- sub JSON::PP::incr_parse {
|
|
| 7784 |
- local $Carp::CarpLevel = 1; |
|
| 7785 |
- ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_parse( @_ );
|
|
| 7786 |
- } |
|
| 8051 |
+ my $max_depth; |
|
| 8052 |
+ my $indent; |
|
| 8053 |
+ my $ascii; |
|
| 8054 |
+ my $latin1; |
|
| 8055 |
+ my $utf8; |
|
| 8056 |
+ my $space_before; |
|
| 8057 |
+ my $space_after; |
|
| 8058 |
+ my $canonical; |
|
| 8059 |
+ my $allow_blessed; |
|
| 8060 |
+ my $convert_blessed; |
|
| 7787 | 8061 |
|
| 8062 |
+ my $indent_length; |
|
| 8063 |
+ my $escape_slash; |
|
| 8064 |
+ my $bignum; |
|
| 8065 |
+ my $as_nonblessed; |
|
| 7788 | 8066 |
|
| 7789 |
- sub JSON::PP::incr_skip {
|
|
| 7790 |
- ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_skip;
|
|
| 7791 |
- } |
|
| 8067 |
+ my $depth; |
|
| 8068 |
+ my $indent_count; |
|
| 8069 |
+ my $keysort; |
|
| 7792 | 8070 |
|
| 7793 | 8071 |
|
| 7794 |
- sub JSON::PP::incr_reset {
|
|
| 7795 |
- ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_reset;
|
|
| 7796 |
- } |
|
| 8072 |
+ sub PP_encode_json {
|
|
| 8073 |
+ my $self = shift; |
|
| 8074 |
+ my $obj = shift; |
|
| 7797 | 8075 |
|
| 7798 |
- eval q{
|
|
| 7799 |
- sub JSON::PP::incr_text : lvalue {
|
|
| 7800 |
- $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new;
|
|
| 8076 |
+ $indent_count = 0; |
|
| 8077 |
+ $depth = 0; |
|
| 7801 | 8078 |
|
| 7802 |
- if ( $_[0]->{_incr_parser}->{incr_parsing} ) {
|
|
| 7803 |
- Carp::croak("incr_text can not be called when the incremental parser already started parsing");
|
|
| 7804 |
- } |
|
| 7805 |
- $_[0]->{_incr_parser}->{incr_text};
|
|
| 7806 |
- } |
|
| 7807 |
- } if ( $] >= 5.006 ); |
|
| 8079 |
+ my $idx = $self->{PROPS};
|
|
| 7808 | 8080 |
|
| 7809 |
- } # Setup for various Perl versions (the code from JSON::PP58) |
|
| 8081 |
+ ($ascii, $latin1, $utf8, $indent, $canonical, $space_before, $space_after, $allow_blessed, |
|
| 8082 |
+ $convert_blessed, $escape_slash, $bignum, $as_nonblessed) |
|
| 8083 |
+ = @{$idx}[P_ASCII .. P_SPACE_AFTER, P_ALLOW_BLESSED, P_CONVERT_BLESSED,
|
|
| 8084 |
+ P_ESCAPE_SLASH, P_ALLOW_BIGNUM, P_AS_NONBLESSED]; |
|
| 7810 | 8085 |
|
| 8086 |
+ ($max_depth, $indent_length) = @{$self}{qw/max_depth indent_length/};
|
|
| 7811 | 8087 |
|
| 7812 |
- ############################### |
|
| 7813 |
- # Utilities |
|
| 7814 |
- # |
|
| 8088 |
+ $keysort = $canonical ? sub { $a cmp $b } : undef;
|
|
| 7815 | 8089 |
|
| 7816 |
- BEGIN {
|
|
| 7817 |
- eval 'require Scalar::Util'; |
|
| 7818 |
- unless($@){
|
|
| 7819 |
- *JSON::PP::blessed = \&Scalar::Util::blessed; |
|
| 7820 |
- *JSON::PP::reftype = \&Scalar::Util::reftype; |
|
| 7821 |
- *JSON::PP::refaddr = \&Scalar::Util::refaddr; |
|
| 7822 |
- } |
|
| 7823 |
- else{ # This code is from Sclar::Util.
|
|
| 7824 |
- # warn $@; |
|
| 7825 |
- eval 'sub UNIVERSAL::a_sub_not_likely_to_be_here { ref($_[0]) }';
|
|
| 7826 |
- *JSON::PP::blessed = sub {
|
|
| 7827 |
- local($@, $SIG{__DIE__}, $SIG{__WARN__});
|
|
| 7828 |
- ref($_[0]) ? eval { $_[0]->a_sub_not_likely_to_be_here } : undef;
|
|
| 7829 |
- }; |
|
| 7830 |
- my %tmap = qw( |
|
| 7831 |
- B::NULL SCALAR |
|
| 7832 |
- B::HV HASH |
|
| 7833 |
- B::AV ARRAY |
|
| 7834 |
- B::CV CODE |
|
| 7835 |
- B::IO IO |
|
| 7836 |
- B::GV GLOB |
|
| 7837 |
- B::REGEXP REGEXP |
|
| 7838 |
- ); |
|
| 7839 |
- *JSON::PP::reftype = sub {
|
|
| 7840 |
- my $r = shift; |
|
| 8090 |
+ if ($self->{sort_by}) {
|
|
| 8091 |
+ $keysort = ref($self->{sort_by}) eq 'CODE' ? $self->{sort_by}
|
|
| 8092 |
+ : $self->{sort_by} =~ /\D+/ ? $self->{sort_by}
|
|
| 8093 |
+ : sub { $a cmp $b };
|
|
| 8094 |
+ } |
|
| 7841 | 8095 |
|
| 7842 |
- return undef unless length(ref($r)); |
|
| 8096 |
+ encode_error("hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)")
|
|
| 8097 |
+ if(!ref $obj and !$idx->[ P_ALLOW_NONREF ]); |
|
| 7843 | 8098 |
|
| 7844 |
- my $t = ref(B::svref_2object($r)); |
|
| 8099 |
+ my $str = $self->object_to_json($obj); |
|
| 7845 | 8100 |
|
| 7846 |
- return |
|
| 7847 |
- exists $tmap{$t} ? $tmap{$t}
|
|
| 7848 |
- : length(ref($$r)) ? 'REF' |
|
| 7849 |
- : 'SCALAR'; |
|
| 7850 |
- }; |
|
| 7851 |
- *JSON::PP::refaddr = sub {
|
|
| 7852 |
- return undef unless length(ref($_[0])); |
|
| 8101 |
+ $str .= "\n" if ( $indent ); # JSON::XS 2.26 compatible |
|
| 7853 | 8102 |
|
| 7854 |
- my $addr; |
|
| 7855 |
- if(defined(my $pkg = blessed($_[0]))) {
|
|
| 7856 |
- $addr .= bless $_[0], 'Scalar::Util::Fake'; |
|
| 7857 |
- bless $_[0], $pkg; |
|
| 7858 |
- } |
|
| 7859 |
- else {
|
|
| 7860 |
- $addr .= $_[0] |
|
| 7861 |
- } |
|
| 8103 |
+ unless ($ascii or $latin1 or $utf8) {
|
|
| 8104 |
+ utf8::upgrade($str); |
|
| 8105 |
+ } |
|
| 7862 | 8106 |
|
| 7863 |
- $addr =~ /0x(\w+)/; |
|
| 7864 |
- local $^W; |
|
| 7865 |
- #no warnings 'portable'; |
|
| 7866 |
- hex($1); |
|
| 8107 |
+ if ($idx->[ P_SHRINK ]) {
|
|
| 8108 |
+ utf8::downgrade($str, 1); |
|
| 7867 | 8109 |
} |
| 8110 |
+ |
|
| 8111 |
+ return $str; |
|
| 7868 | 8112 |
} |
| 7869 |
- } |
|
| 7870 | 8113 |
|
| 7871 | 8114 |
|
| 7872 |
- # shamely copied and modified from JSON::XS code. |
|
| 8115 |
+ sub object_to_json {
|
|
| 8116 |
+ my ($self, $obj) = @_; |
|
| 8117 |
+ my $type = ref($obj); |
|
| 7873 | 8118 |
|
| 7874 |
- $JSON::PP::true = do { bless \(my $dummy = 1), "JSON::PP::Boolean" };
|
|
| 7875 |
- $JSON::PP::false = do { bless \(my $dummy = 0), "JSON::PP::Boolean" };
|
|
| 8119 |
+ if($type eq 'HASH'){
|
|
| 8120 |
+ return $self->hash_to_json($obj); |
|
| 8121 |
+ } |
|
| 8122 |
+ elsif($type eq 'ARRAY'){
|
|
| 8123 |
+ return $self->array_to_json($obj); |
|
| 8124 |
+ } |
|
| 8125 |
+ elsif ($type) { # blessed object?
|
|
| 8126 |
+ if (blessed($obj)) {
|
|
| 7876 | 8127 |
|
| 7877 |
- sub is_bool { defined $_[0] and UNIVERSAL::isa($_[0], "JSON::PP::Boolean"); }
|
|
| 8128 |
+ return $self->value_to_json($obj) if ( $obj->isa('JSON::PP::Boolean') );
|
|
| 7878 | 8129 |
|
| 7879 |
- sub true { $JSON::PP::true }
|
|
| 7880 |
- sub false { $JSON::PP::false }
|
|
| 7881 |
- sub null { undef; }
|
|
| 8130 |
+ if ( $convert_blessed and $obj->can('TO_JSON') ) {
|
|
| 8131 |
+ my $result = $obj->TO_JSON(); |
|
| 8132 |
+ if ( defined $result and ref( $result ) ) {
|
|
| 8133 |
+ if ( refaddr( $obj ) eq refaddr( $result ) ) {
|
|
| 8134 |
+ encode_error( sprintf( |
|
| 8135 |
+ "%s::TO_JSON method returned same object as was passed instead of a new one", |
|
| 8136 |
+ ref $obj |
|
| 8137 |
+ ) ); |
|
| 8138 |
+ } |
|
| 8139 |
+ } |
|
| 7882 | 8140 |
|
| 7883 |
- ############################### |
|
| 8141 |
+ return $self->object_to_json( $result ); |
|
| 8142 |
+ } |
|
| 7884 | 8143 |
|
| 7885 |
- package JSON::PP::Boolean; |
|
| 8144 |
+ return "$obj" if ( $bignum and _is_bignum($obj) ); |
|
| 8145 |
+ return $self->blessed_to_json($obj) if ($allow_blessed and $as_nonblessed); # will be removed. |
|
| 7886 | 8146 |
|
| 7887 |
- use overload ( |
|
| 7888 |
- "0+" => sub { ${$_[0]} },
|
|
| 7889 |
- "++" => sub { $_[0] = ${$_[0]} + 1 },
|
|
| 7890 |
- "--" => sub { $_[0] = ${$_[0]} - 1 },
|
|
| 7891 |
- fallback => 1, |
|
| 7892 |
- ); |
|
| 8147 |
+ encode_error( sprintf("encountered object '%s', but neither allow_blessed "
|
|
| 8148 |
+ . "nor convert_blessed settings are enabled", $obj) |
|
| 8149 |
+ ) unless ($allow_blessed); |
|
| 7893 | 8150 |
|
| 8151 |
+ return 'null'; |
|
| 8152 |
+ } |
|
| 8153 |
+ else {
|
|
| 8154 |
+ return $self->value_to_json($obj); |
|
| 8155 |
+ } |
|
| 8156 |
+ } |
|
| 8157 |
+ else{
|
|
| 8158 |
+ return $self->value_to_json($obj); |
|
| 8159 |
+ } |
|
| 8160 |
+ } |
|
| 7894 | 8161 |
|
| 7895 |
- ############################### |
|
| 7896 | 8162 |
|
| 7897 |
- package JSON::PP::IncrParser; |
|
| 8163 |
+ sub hash_to_json {
|
|
| 8164 |
+ my ($self, $obj) = @_; |
|
| 8165 |
+ my @res; |
|
| 7898 | 8166 |
|
| 7899 |
- use strict; |
|
| 8167 |
+ encode_error("json text or perl structure exceeds maximum nesting level (max_depth set too low?)")
|
|
| 8168 |
+ if (++$depth > $max_depth); |
|
| 7900 | 8169 |
|
| 7901 |
- use constant INCR_M_WS => 0; # initial whitespace skipping |
|
| 7902 |
- use constant INCR_M_STR => 1; # inside string |
|
| 7903 |
- use constant INCR_M_BS => 2; # inside backslash |
|
| 7904 |
- use constant INCR_M_JSON => 3; # outside anything, count nesting |
|
| 7905 |
- use constant INCR_M_C0 => 4; |
|
| 7906 |
- use constant INCR_M_C1 => 5; |
|
| 8170 |
+ my ($pre, $post) = $indent ? $self->_up_indent() : ('', '');
|
|
| 8171 |
+ my $del = ($space_before ? ' ' : '') . ':' . ($space_after ? ' ' : ''); |
|
| 7907 | 8172 |
|
| 7908 |
- $JSON::PP::IncrParser::VERSION = '1.01'; |
|
| 8173 |
+ for my $k ( _sort( $obj ) ) {
|
|
| 8174 |
+ if ( OLD_PERL ) { utf8::decode($k) } # key for Perl 5.6 / be optimized
|
|
| 8175 |
+ push @res, string_to_json( $self, $k ) |
|
| 8176 |
+ . $del |
|
| 8177 |
+ . ( $self->object_to_json( $obj->{$k} ) || $self->value_to_json( $obj->{$k} ) );
|
|
| 8178 |
+ } |
|
| 7909 | 8179 |
|
| 7910 |
- my $unpack_format = $] < 5.006 ? 'C*' : 'U*'; |
|
| 8180 |
+ --$depth; |
|
| 8181 |
+ $self->_down_indent() if ($indent); |
|
| 7911 | 8182 |
|
| 7912 |
- sub new {
|
|
| 7913 |
- my ( $class ) = @_; |
|
| 8183 |
+ return '{' . ( @res ? $pre : '' ) . ( @res ? join( ",$pre", @res ) . $post : '' ) . '}';
|
|
| 8184 |
+ } |
|
| 7914 | 8185 |
|
| 7915 |
- bless {
|
|
| 7916 |
- incr_nest => 0, |
|
| 7917 |
- incr_text => undef, |
|
| 7918 |
- incr_parsing => 0, |
|
| 7919 |
- incr_p => 0, |
|
| 7920 |
- }, $class; |
|
| 7921 |
- } |
|
| 7922 | 8186 |
|
| 8187 |
+ sub array_to_json {
|
|
| 8188 |
+ my ($self, $obj) = @_; |
|
| 8189 |
+ my @res; |
|
| 7923 | 8190 |
|
| 7924 |
- sub incr_parse {
|
|
| 7925 |
- my ( $self, $coder, $text ) = @_; |
|
| 8191 |
+ encode_error("json text or perl structure exceeds maximum nesting level (max_depth set too low?)")
|
|
| 8192 |
+ if (++$depth > $max_depth); |
|
| 7926 | 8193 |
|
| 7927 |
- $self->{incr_text} = '' unless ( defined $self->{incr_text} );
|
|
| 8194 |
+ my ($pre, $post) = $indent ? $self->_up_indent() : ('', '');
|
|
| 7928 | 8195 |
|
| 7929 |
- if ( defined $text ) {
|
|
| 7930 |
- if ( utf8::is_utf8( $text ) and !utf8::is_utf8( $self->{incr_text} ) ) {
|
|
| 7931 |
- utf8::upgrade( $self->{incr_text} ) ;
|
|
| 7932 |
- utf8::decode( $self->{incr_text} ) ;
|
|
| 8196 |
+ for my $v (@$obj){
|
|
| 8197 |
+ push @res, $self->object_to_json($v) || $self->value_to_json($v); |
|
| 7933 | 8198 |
} |
| 7934 |
- $self->{incr_text} .= $text;
|
|
| 7935 |
- } |
|
| 7936 | 8199 |
|
| 8200 |
+ --$depth; |
|
| 8201 |
+ $self->_down_indent() if ($indent); |
|
| 7937 | 8202 |
|
| 7938 |
- my $max_size = $coder->get_max_size; |
|
| 8203 |
+ return '[' . ( @res ? $pre : '' ) . ( @res ? join( ",$pre", @res ) . $post : '' ) . ']'; |
|
| 8204 |
+ } |
|
| 7939 | 8205 |
|
| 7940 |
- if ( defined wantarray ) {
|
|
| 7941 | 8206 |
|
| 7942 |
- $self->{incr_mode} = INCR_M_WS unless defined $self->{incr_mode};
|
|
| 8207 |
+ sub value_to_json {
|
|
| 8208 |
+ my ($self, $value) = @_; |
|
| 7943 | 8209 |
|
| 7944 |
- if ( wantarray ) {
|
|
| 7945 |
- my @ret; |
|
| 8210 |
+ return 'null' if(!defined $value); |
|
| 7946 | 8211 |
|
| 7947 |
- $self->{incr_parsing} = 1;
|
|
| 8212 |
+ my $b_obj = B::svref_2object(\$value); # for round trip problem |
|
| 8213 |
+ my $flags = $b_obj->FLAGS; |
|
| 7948 | 8214 |
|
| 7949 |
- do {
|
|
| 7950 |
- push @ret, $self->_incr_parse( $coder, $self->{incr_text} );
|
|
| 8215 |
+ return $value # as is |
|
| 8216 |
+ if $flags & ( B::SVp_IOK | B::SVp_NOK ) and !( $flags & B::SVp_POK ); # SvTYPE is IV or NV? |
|
| 7951 | 8217 |
|
| 7952 |
- unless ( !$self->{incr_nest} and $self->{incr_mode} == INCR_M_JSON ) {
|
|
| 7953 |
- $self->{incr_mode} = INCR_M_WS if $self->{incr_mode} != INCR_M_STR;
|
|
| 7954 |
- } |
|
| 8218 |
+ my $type = ref($value); |
|
| 7955 | 8219 |
|
| 7956 |
- } until ( length $self->{incr_text} >= $self->{incr_p} );
|
|
| 8220 |
+ if(!$type){
|
|
| 8221 |
+ return string_to_json($self, $value); |
|
| 8222 |
+ } |
|
| 8223 |
+ elsif( blessed($value) and $value->isa('JSON::PP::Boolean') ){
|
|
| 8224 |
+ return $$value == 1 ? 'true' : 'false'; |
|
| 8225 |
+ } |
|
| 8226 |
+ elsif ($type) {
|
|
| 8227 |
+ if ((overload::StrVal($value) =~ /=(\w+)/)[0]) {
|
|
| 8228 |
+ return $self->value_to_json("$value");
|
|
| 8229 |
+ } |
|
| 7957 | 8230 |
|
| 7958 |
- $self->{incr_parsing} = 0;
|
|
| 8231 |
+ if ($type eq 'SCALAR' and defined $$value) {
|
|
| 8232 |
+ return $$value eq '1' ? 'true' |
|
| 8233 |
+ : $$value eq '0' ? 'false' |
|
| 8234 |
+ : $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ? 'null'
|
|
| 8235 |
+ : encode_error("cannot encode reference to scalar");
|
|
| 8236 |
+ } |
|
| 8237 |
+ |
|
| 8238 |
+ if ( $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ) {
|
|
| 8239 |
+ return 'null'; |
|
| 8240 |
+ } |
|
| 8241 |
+ else {
|
|
| 8242 |
+ if ( $type eq 'SCALAR' or $type eq 'REF' ) {
|
|
| 8243 |
+ encode_error("cannot encode reference to scalar");
|
|
| 8244 |
+ } |
|
| 8245 |
+ else {
|
|
| 8246 |
+ encode_error("encountered $value, but JSON can only represent references to arrays or hashes");
|
|
| 8247 |
+ } |
|
| 8248 |
+ } |
|
| 7959 | 8249 |
|
| 7960 |
- return @ret; |
|
| 7961 | 8250 |
} |
| 7962 |
- else { # in scalar context
|
|
| 7963 |
- $self->{incr_parsing} = 1;
|
|
| 7964 |
- my $obj = $self->_incr_parse( $coder, $self->{incr_text} );
|
|
| 7965 |
- $self->{incr_parsing} = 0 if defined $obj; # pointed by Martin J. Evans
|
|
| 7966 |
- return $obj ? $obj : undef; # $obj is an empty string, parsing was completed. |
|
| 8251 |
+ else {
|
|
| 8252 |
+ return $self->{fallback}->($value)
|
|
| 8253 |
+ if ($self->{fallback} and ref($self->{fallback}) eq 'CODE');
|
|
| 8254 |
+ return 'null'; |
|
| 7967 | 8255 |
} |
| 7968 | 8256 |
|
| 7969 | 8257 |
} |
| 7970 | 8258 |
|
| 7971 |
- } |
|
| 7972 | 8259 |
|
| 8260 |
+ my %esc = ( |
|
| 8261 |
+ "\n" => '\n', |
|
| 8262 |
+ "\r" => '\r', |
|
| 8263 |
+ "\t" => '\t', |
|
| 8264 |
+ "\f" => '\f', |
|
| 8265 |
+ "\b" => '\b', |
|
| 8266 |
+ "\"" => '\"', |
|
| 8267 |
+ "\\" => '\\\\', |
|
| 8268 |
+ "\'" => '\\\'', |
|
| 8269 |
+ ); |
|
| 7973 | 8270 |
|
| 7974 |
- sub _incr_parse {
|
|
| 7975 |
- my ( $self, $coder, $text, $skip ) = @_; |
|
| 7976 |
- my $p = $self->{incr_p};
|
|
| 7977 |
- my $restore = $p; |
|
| 7978 | 8271 |
|
| 7979 |
- my @obj; |
|
| 7980 |
- my $len = length $text; |
|
| 8272 |
+ sub string_to_json {
|
|
| 8273 |
+ my ($self, $arg) = @_; |
|
| 7981 | 8274 |
|
| 7982 |
- if ( $self->{incr_mode} == INCR_M_WS ) {
|
|
| 7983 |
- while ( $len > $p ) {
|
|
| 7984 |
- my $s = substr( $text, $p, 1 ); |
|
| 7985 |
- $p++ and next if ( 0x20 >= unpack($unpack_format, $s) ); |
|
| 7986 |
- $self->{incr_mode} = INCR_M_JSON;
|
|
| 7987 |
- last; |
|
| 7988 |
- } |
|
| 7989 |
- } |
|
| 8275 |
+ $arg =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/g;
|
|
| 8276 |
+ $arg =~ s/\//\\\//g if ($escape_slash); |
|
| 8277 |
+ $arg =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg;
|
|
| 7990 | 8278 |
|
| 7991 |
- while ( $len > $p ) {
|
|
| 7992 |
- my $s = substr( $text, $p++, 1 ); |
|
| 8279 |
+ if ($ascii) {
|
|
| 8280 |
+ $arg = JSON_PP_encode_ascii($arg); |
|
| 8281 |
+ } |
|
| 7993 | 8282 |
|
| 7994 |
- if ( $s eq '"' ) {
|
|
| 7995 |
- if (substr( $text, $p - 2, 1 ) eq '\\' ) {
|
|
| 7996 |
- next; |
|
| 7997 |
- } |
|
| 8283 |
+ if ($latin1) {
|
|
| 8284 |
+ $arg = JSON_PP_encode_latin1($arg); |
|
| 8285 |
+ } |
|
| 7998 | 8286 |
|
| 7999 |
- if ( $self->{incr_mode} != INCR_M_STR ) {
|
|
| 8000 |
- $self->{incr_mode} = INCR_M_STR;
|
|
| 8001 |
- } |
|
| 8002 |
- else {
|
|
| 8003 |
- $self->{incr_mode} = INCR_M_JSON;
|
|
| 8004 |
- unless ( $self->{incr_nest} ) {
|
|
| 8005 |
- last; |
|
| 8006 |
- } |
|
| 8007 |
- } |
|
| 8287 |
+ if ($utf8) {
|
|
| 8288 |
+ utf8::encode($arg); |
|
| 8008 | 8289 |
} |
| 8009 | 8290 |
|
| 8010 |
- if ( $self->{incr_mode} == INCR_M_JSON ) {
|
|
| 8291 |
+ return '"' . $arg . '"'; |
|
| 8292 |
+ } |
|
| 8011 | 8293 |
|
| 8012 |
- if ( $s eq '[' or $s eq '{' ) {
|
|
| 8013 |
- if ( ++$self->{incr_nest} > $coder->get_max_depth ) {
|
|
| 8014 |
- Carp::croak('json text or perl structure exceeds maximum nesting level (max_depth set too low?)');
|
|
| 8015 |
- } |
|
| 8016 |
- } |
|
| 8017 |
- elsif ( $s eq ']' or $s eq '}' ) {
|
|
| 8018 |
- last if ( --$self->{incr_nest} <= 0 );
|
|
| 8019 |
- } |
|
| 8020 |
- elsif ( $s eq '#' ) {
|
|
| 8021 |
- while ( $len > $p ) {
|
|
| 8022 |
- last if substr( $text, $p++, 1 ) eq "\n"; |
|
| 8023 |
- } |
|
| 8024 |
- } |
|
| 8025 | 8294 |
|
| 8295 |
+ sub blessed_to_json {
|
|
| 8296 |
+ my $reftype = reftype($_[1]) || ''; |
|
| 8297 |
+ if ($reftype eq 'HASH') {
|
|
| 8298 |
+ return $_[0]->hash_to_json($_[1]); |
|
| 8299 |
+ } |
|
| 8300 |
+ elsif ($reftype eq 'ARRAY') {
|
|
| 8301 |
+ return $_[0]->array_to_json($_[1]); |
|
| 8302 |
+ } |
|
| 8303 |
+ else {
|
|
| 8304 |
+ return 'null'; |
|
| 8026 | 8305 |
} |
| 8306 |
+ } |
|
| 8307 |
+ |
|
| 8027 | 8308 |
|
| 8309 |
+ sub encode_error {
|
|
| 8310 |
+ my $error = shift; |
|
| 8311 |
+ Carp::croak "$error"; |
|
| 8028 | 8312 |
} |
| 8029 | 8313 |
|
| 8030 |
- $self->{incr_p} = $p;
|
|
| 8031 | 8314 |
|
| 8032 |
- return if ( $self->{incr_mode} == INCR_M_STR and not $self->{incr_nest} );
|
|
| 8033 |
- return if ( $self->{incr_mode} == INCR_M_JSON and $self->{incr_nest} > 0 );
|
|
| 8315 |
+ sub _sort {
|
|
| 8316 |
+ defined $keysort ? (sort $keysort (keys %{$_[0]})) : keys %{$_[0]};
|
|
| 8317 |
+ } |
|
| 8034 | 8318 |
|
| 8035 |
- return '' unless ( length substr( $self->{incr_text}, 0, $p ) );
|
|
| 8036 | 8319 |
|
| 8037 |
- local $Carp::CarpLevel = 2; |
|
| 8320 |
+ sub _up_indent {
|
|
| 8321 |
+ my $self = shift; |
|
| 8322 |
+ my $space = ' ' x $indent_length; |
|
| 8038 | 8323 |
|
| 8039 |
- $self->{incr_p} = $restore;
|
|
| 8040 |
- $self->{incr_c} = $p;
|
|
| 8324 |
+ my ($pre,$post) = ('','');
|
|
| 8041 | 8325 |
|
| 8042 |
- my ( $obj, $tail ) = $coder->PP_decode_json( substr( $self->{incr_text}, 0, $p ), 0x10000001 );
|
|
| 8326 |
+ $post = "\n" . $space x $indent_count; |
|
| 8043 | 8327 |
|
| 8044 |
- $self->{incr_text} = substr( $self->{incr_text}, $p );
|
|
| 8045 |
- $self->{incr_p} = 0;
|
|
| 8328 |
+ $indent_count++; |
|
| 8046 | 8329 |
|
| 8047 |
- return $obj or ''; |
|
| 8048 |
- } |
|
| 8330 |
+ $pre = "\n" . $space x $indent_count; |
|
| 8049 | 8331 |
|
| 8332 |
+ return ($pre,$post); |
|
| 8333 |
+ } |
|
| 8050 | 8334 |
|
| 8051 |
- sub incr_text {
|
|
| 8052 |
- if ( $_[0]->{incr_parsing} ) {
|
|
| 8053 |
- Carp::croak("incr_text can not be called when the incremental parser already started parsing");
|
|
| 8335 |
+ |
|
| 8336 |
+ sub _down_indent { $indent_count--; }
|
|
| 8337 |
+ |
|
| 8338 |
+ |
|
| 8339 |
+ sub PP_encode_box {
|
|
| 8340 |
+ {
|
|
| 8341 |
+ depth => $depth, |
|
| 8342 |
+ indent_count => $indent_count, |
|
| 8343 |
+ }; |
|
| 8054 | 8344 |
} |
| 8055 |
- $_[0]->{incr_text};
|
|
| 8056 |
- } |
|
| 8057 | 8345 |
|
| 8346 |
+ } # Convert |
|
| 8058 | 8347 |
|
| 8059 |
- sub incr_skip {
|
|
| 8060 |
- my $self = shift; |
|
| 8061 |
- $self->{incr_text} = substr( $self->{incr_text}, $self->{incr_c} );
|
|
| 8062 |
- $self->{incr_p} = 0;
|
|
| 8348 |
+ |
|
| 8349 |
+ sub _encode_ascii {
|
|
| 8350 |
+ join('',
|
|
| 8351 |
+ map {
|
|
| 8352 |
+ $_ <= 127 ? |
|
| 8353 |
+ chr($_) : |
|
| 8354 |
+ $_ <= 65535 ? |
|
| 8355 |
+ sprintf('\u%04x', $_) : sprintf('\u%x\u%x', _encode_surrogates($_));
|
|
| 8356 |
+ } unpack('U*', $_[0])
|
|
| 8357 |
+ ); |
|
| 8063 | 8358 |
} |
| 8064 | 8359 |
|
| 8065 | 8360 |
|
| 8066 |
- sub incr_reset {
|
|
| 8067 |
- my $self = shift; |
|
| 8068 |
- $self->{incr_text} = undef;
|
|
| 8069 |
- $self->{incr_p} = 0;
|
|
| 8070 |
- $self->{incr_mode} = 0;
|
|
| 8071 |
- $self->{incr_nest} = 0;
|
|
| 8072 |
- $self->{incr_parsing} = 0;
|
|
| 8361 |
+ sub _encode_latin1 {
|
|
| 8362 |
+ join('',
|
|
| 8363 |
+ map {
|
|
| 8364 |
+ $_ <= 255 ? |
|
| 8365 |
+ chr($_) : |
|
| 8366 |
+ $_ <= 65535 ? |
|
| 8367 |
+ sprintf('\u%04x', $_) : sprintf('\u%x\u%x', _encode_surrogates($_));
|
|
| 8368 |
+ } unpack('U*', $_[0])
|
|
| 8369 |
+ ); |
|
| 8073 | 8370 |
} |
| 8074 | 8371 |
|
| 8075 |
- ############################### |
|
| 8076 |
- |
|
| 8077 | 8372 |
|
| 8078 |
- 1; |
|
| 8079 |
- __END__ |
|
| 8080 |
- =pod |
|
| 8373 |
+ sub _encode_surrogates { # from perlunicode
|
|
| 8374 |
+ my $uni = $_[0] - 0x10000; |
|
| 8375 |
+ return ($uni / 0x400 + 0xD800, $uni % 0x400 + 0xDC00); |
|
| 8376 |
+ } |
|
| 8081 | 8377 |
|
| 8082 |
-JSON_PP |
|
| 8083 |
- |
|
| 8084 |
-$fatpacked{"JSON/PP/Boolean.pm"} = <<'JSON_PP_BOOLEAN';
|
|
| 8085 |
- use JSON::PP (); |
|
| 8086 |
- use strict; |
|
| 8087 | 8378 |
|
| 8088 |
- 1; |
|
| 8379 |
+ sub _is_bignum {
|
|
| 8380 |
+ $_[0]->isa('Math::BigInt') or $_[0]->isa('Math::BigFloat');
|
|
| 8381 |
+ } |
|
| 8089 | 8382 |
|
| 8090 |
-JSON_PP_BOOLEAN |
|
| 8091 |
- |
|
| 8092 |
-$fatpacked{"Module/Metadata.pm"} = <<'MODULE_METADATA';
|
|
| 8093 |
- # -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*- |
|
| 8094 |
- # vim:ts=8:sw=2:et:sta:sts=2 |
|
| 8095 |
- package Module::Metadata; |
|
| 8096 | 8383 |
|
| 8097 |
- # Adapted from Perl-licensed code originally distributed with |
|
| 8098 |
- # Module-Build by Ken Williams |
|
| 8099 | 8384 |
|
| 8100 |
- # This module provides routines to gather information about |
|
| 8101 |
- # perl modules (assuming this may be expanded in the distant |
|
| 8102 |
- # parrot future to look at other types of modules). |
|
| 8385 |
+ # |
|
| 8386 |
+ # JSON => Perl |
|
| 8387 |
+ # |
|
| 8103 | 8388 |
|
| 8104 |
- use strict; |
|
| 8105 |
- use vars qw($VERSION); |
|
| 8106 |
- $VERSION = '1.000007'; |
|
| 8107 |
- $VERSION = eval $VERSION; |
|
| 8389 |
+ my $max_intsize; |
|
| 8108 | 8390 |
|
| 8109 |
- use File::Spec; |
|
| 8110 |
- use IO::File; |
|
| 8111 |
- use version 0.87; |
|
| 8112 | 8391 |
BEGIN {
|
| 8113 |
- if ($INC{'Log/Contextual.pm'}) {
|
|
| 8114 |
- Log::Contextual->import('log_info');
|
|
| 8115 |
- } else {
|
|
| 8116 |
- *log_info = sub (&) { warn $_[0]->() };
|
|
| 8117 |
- } |
|
| 8392 |
+ my $checkint = 1111; |
|
| 8393 |
+ for my $d (5..64) {
|
|
| 8394 |
+ $checkint .= 1; |
|
| 8395 |
+ my $int = eval qq| $checkint |; |
|
| 8396 |
+ if ($int =~ /[eE]/) {
|
|
| 8397 |
+ $max_intsize = $d - 1; |
|
| 8398 |
+ last; |
|
| 8399 |
+ } |
|
| 8400 |
+ } |
|
| 8118 | 8401 |
} |
| 8119 |
- use File::Find qw(find); |
|
| 8120 | 8402 |
|
| 8121 |
- my $V_NUM_REGEXP = qr{v?[0-9._]+}; # crudely, a v-string or decimal
|
|
| 8403 |
+ { # PARSE
|
|
| 8122 | 8404 |
|
| 8123 |
- my $PKG_REGEXP = qr{ # match a package declaration
|
|
| 8124 |
- ^[\s\{;]* # intro chars on a line
|
|
| 8125 |
- package # the word 'package' |
|
| 8126 |
- \s+ # whitespace |
|
| 8127 |
- ([\w:]+) # a package name |
|
| 8128 |
- \s* # optional whitespace |
|
| 8129 |
- ($V_NUM_REGEXP)? # optional version number |
|
| 8130 |
- \s* # optional whitesapce |
|
| 8131 |
- [;\{] # semicolon line terminator or block start (since 5.16)
|
|
| 8132 |
- }x; |
|
| 8405 |
+ my %escapes = ( # by Jeremy Muhlich <jmuhlich [at] bitflood.org> |
|
| 8406 |
+ b => "\x8", |
|
| 8407 |
+ t => "\x9", |
|
| 8408 |
+ n => "\xA", |
|
| 8409 |
+ f => "\xC", |
|
| 8410 |
+ r => "\xD", |
|
| 8411 |
+ '\\' => '\\', |
|
| 8412 |
+ '"' => '"', |
|
| 8413 |
+ '/' => '/', |
|
| 8414 |
+ ); |
|
| 8133 | 8415 |
|
| 8134 |
- my $VARNAME_REGEXP = qr{ # match fully-qualified VERSION name
|
|
| 8135 |
- ([\$*]) # sigil - $ or * |
|
| 8136 |
- ( |
|
| 8137 |
- ( # optional leading package name |
|
| 8138 |
- (?:::|\')? # possibly starting like just :: (� la $::VERSION) |
|
| 8139 |
- (?:\w+(?:::|\'))* # Foo::Bar:: ... |
|
| 8140 |
- )? |
|
| 8141 |
- VERSION |
|
| 8142 |
- )\b |
|
| 8143 |
- }x; |
|
| 8416 |
+ my $text; # json data |
|
| 8417 |
+ my $at; # offset |
|
| 8418 |
+ my $ch; # 1chracter |
|
| 8419 |
+ my $len; # text length (changed according to UTF8 or NON UTF8) |
|
| 8420 |
+ # INTERNAL |
|
| 8421 |
+ my $depth; # nest counter |
|
| 8422 |
+ my $encoding; # json text encoding |
|
| 8423 |
+ my $is_valid_utf8; # temp variable |
|
| 8424 |
+ my $utf8_len; # utf8 byte length |
|
| 8425 |
+ # FLAGS |
|
| 8426 |
+ my $utf8; # must be utf8 |
|
| 8427 |
+ my $max_depth; # max nest nubmer of objects and arrays |
|
| 8428 |
+ my $max_size; |
|
| 8429 |
+ my $relaxed; |
|
| 8430 |
+ my $cb_object; |
|
| 8431 |
+ my $cb_sk_object; |
|
| 8144 | 8432 |
|
| 8145 |
- my $VERS_REGEXP = qr{ # match a VERSION definition
|
|
| 8146 |
- (?: |
|
| 8147 |
- \(\s*$VARNAME_REGEXP\s*\) # with parens |
|
| 8148 |
- | |
|
| 8149 |
- $VARNAME_REGEXP # without parens |
|
| 8150 |
- ) |
|
| 8151 |
- \s* |
|
| 8152 |
- =[^=~] # = but not ==, nor =~ |
|
| 8153 |
- }x; |
|
| 8433 |
+ my $F_HOOK; |
|
| 8154 | 8434 |
|
| 8435 |
+ my $allow_bigint; # using Math::BigInt |
|
| 8436 |
+ my $singlequote; # loosely quoting |
|
| 8437 |
+ my $loose; # |
|
| 8438 |
+ my $allow_barekey; # bareKey |
|
| 8155 | 8439 |
|
| 8156 |
- sub new_from_file {
|
|
| 8157 |
- my $class = shift; |
|
| 8158 |
- my $filename = File::Spec->rel2abs( shift ); |
|
| 8440 |
+ # $opt flag |
|
| 8441 |
+ # 0x00000001 .... decode_prefix |
|
| 8442 |
+ # 0x10000000 .... incr_parse |
|
| 8159 | 8443 |
|
| 8160 |
- return undef unless defined( $filename ) && -f $filename; |
|
| 8161 |
- return $class->_init(undef, $filename, @_); |
|
| 8162 |
- } |
|
| 8444 |
+ sub PP_decode_json {
|
|
| 8445 |
+ my ($self, $opt); # $opt is an effective flag during this decode_json. |
|
| 8163 | 8446 |
|
| 8164 |
- sub new_from_handle {
|
|
| 8165 |
- my $class = shift; |
|
| 8166 |
- my $handle = shift; |
|
| 8167 |
- my $filename = shift; |
|
| 8168 |
- return undef unless defined($handle) && defined($filename); |
|
| 8169 |
- $filename = File::Spec->rel2abs( $filename ); |
|
| 8447 |
+ ($self, $text, $opt) = @_; |
|
| 8170 | 8448 |
|
| 8171 |
- return $class->_init(undef, $filename, @_, handle => $handle); |
|
| 8449 |
+ ($at, $ch, $depth) = (0, '', 0); |
|
| 8172 | 8450 |
|
| 8173 |
- } |
|
| 8451 |
+ if ( !defined $text or ref $text ) {
|
|
| 8452 |
+ decode_error("malformed JSON string, neither array, object, number, string or atom");
|
|
| 8453 |
+ } |
|
| 8174 | 8454 |
|
| 8455 |
+ my $idx = $self->{PROPS};
|
|
| 8175 | 8456 |
|
| 8176 |
- sub new_from_module {
|
|
| 8177 |
- my $class = shift; |
|
| 8178 |
- my $module = shift; |
|
| 8179 |
- my %props = @_; |
|
| 8457 |
+ ($utf8, $relaxed, $loose, $allow_bigint, $allow_barekey, $singlequote) |
|
| 8458 |
+ = @{$idx}[P_UTF8, P_RELAXED, P_LOOSE .. P_ALLOW_SINGLEQUOTE];
|
|
| 8180 | 8459 |
|
| 8181 |
- $props{inc} ||= \@INC;
|
|
| 8182 |
- my $filename = $class->find_module_by_name( $module, $props{inc} );
|
|
| 8183 |
- return undef unless defined( $filename ) && -f $filename; |
|
| 8184 |
- return $class->_init($module, $filename, %props); |
|
| 8185 |
- } |
|
| 8460 |
+ if ( $utf8 ) {
|
|
| 8461 |
+ utf8::downgrade( $text, 1 ) or Carp::croak("Wide character in subroutine entry");
|
|
| 8462 |
+ } |
|
| 8463 |
+ else {
|
|
| 8464 |
+ utf8::upgrade( $text ); |
|
| 8465 |
+ } |
|
| 8186 | 8466 |
|
| 8187 |
- {
|
|
| 8188 |
- |
|
| 8189 |
- my $compare_versions = sub {
|
|
| 8190 |
- my ($v1, $op, $v2) = @_; |
|
| 8191 |
- $v1 = version->new($v1) |
|
| 8192 |
- unless UNIVERSAL::isa($v1,'version'); |
|
| 8193 |
- |
|
| 8194 |
- my $eval_str = "\$v1 $op \$v2"; |
|
| 8195 |
- my $result = eval $eval_str; |
|
| 8196 |
- log_info { "error comparing versions: '$eval_str' $@" } if $@;
|
|
| 8197 |
- |
|
| 8198 |
- return $result; |
|
| 8199 |
- }; |
|
| 8467 |
+ $len = length $text; |
|
| 8200 | 8468 |
|
| 8201 |
- my $normalize_version = sub {
|
|
| 8202 |
- my ($version) = @_; |
|
| 8203 |
- if ( $version =~ /[=<>!,]/ ) { # logic, not just version
|
|
| 8204 |
- # take as is without modification |
|
| 8205 |
- } |
|
| 8206 |
- elsif ( ref $version eq 'version' ) { # version objects
|
|
| 8207 |
- $version = $version->is_qv ? $version->normal : $version->stringify; |
|
| 8208 |
- } |
|
| 8209 |
- elsif ( $version =~ /^[^v][^.]*\.[^.]+\./ ) { # no leading v, multiple dots
|
|
| 8210 |
- # normalize string tuples without "v": "1.2.3" -> "v1.2.3" |
|
| 8211 |
- $version = "v$version"; |
|
| 8212 |
- } |
|
| 8213 |
- else {
|
|
| 8214 |
- # leave alone |
|
| 8215 |
- } |
|
| 8216 |
- return $version; |
|
| 8217 |
- }; |
|
| 8469 |
+ ($max_depth, $max_size, $cb_object, $cb_sk_object, $F_HOOK) |
|
| 8470 |
+ = @{$self}{qw/max_depth max_size cb_object cb_sk_object F_HOOK/};
|
|
| 8218 | 8471 |
|
| 8219 |
- # separate out some of the conflict resolution logic |
|
| 8472 |
+ if ($max_size > 1) {
|
|
| 8473 |
+ use bytes; |
|
| 8474 |
+ my $bytes = length $text; |
|
| 8475 |
+ decode_error( |
|
| 8476 |
+ sprintf("attempted decode of JSON text of %s bytes size, but max_size is set to %s"
|
|
| 8477 |
+ , $bytes, $max_size), 1 |
|
| 8478 |
+ ) if ($bytes > $max_size); |
|
| 8479 |
+ } |
|
| 8220 | 8480 |
|
| 8221 |
- my $resolve_module_versions = sub {
|
|
| 8222 |
- my $packages = shift; |
|
| 8223 |
- |
|
| 8224 |
- my( $file, $version ); |
|
| 8225 |
- my $err = ''; |
|
| 8226 |
- foreach my $p ( @$packages ) {
|
|
| 8227 |
- if ( defined( $p->{version} ) ) {
|
|
| 8228 |
- if ( defined( $version ) ) {
|
|
| 8229 |
- if ( $compare_versions->( $version, '!=', $p->{version} ) ) {
|
|
| 8230 |
- $err .= " $p->{file} ($p->{version})\n";
|
|
| 8231 |
- } else {
|
|
| 8232 |
- # same version declared multiple times, ignore |
|
| 8233 |
- } |
|
| 8234 |
- } else {
|
|
| 8235 |
- $file = $p->{file};
|
|
| 8236 |
- $version = $p->{version};
|
|
| 8237 |
- } |
|
| 8481 |
+ # Currently no effect |
|
| 8482 |
+ # should use regexp |
|
| 8483 |
+ my @octets = unpack('C4', $text);
|
|
| 8484 |
+ $encoding = ( $octets[0] and $octets[1]) ? 'UTF-8' |
|
| 8485 |
+ : (!$octets[0] and $octets[1]) ? 'UTF-16BE' |
|
| 8486 |
+ : (!$octets[0] and !$octets[1]) ? 'UTF-32BE' |
|
| 8487 |
+ : ( $octets[2] ) ? 'UTF-16LE' |
|
| 8488 |
+ : (!$octets[2] ) ? 'UTF-32LE' |
|
| 8489 |
+ : 'unknown'; |
|
| 8490 |
+ |
|
| 8491 |
+ white(); # remove head white space |
|
| 8492 |
+ |
|
| 8493 |
+ my $valid_start = defined $ch; # Is there a first character for JSON structure? |
|
| 8494 |
+ |
|
| 8495 |
+ my $result = value(); |
|
| 8496 |
+ |
|
| 8497 |
+ return undef if ( !$result && ( $opt & 0x10000000 ) ); # for incr_parse |
|
| 8498 |
+ |
|
| 8499 |
+ decode_error("malformed JSON string, neither array, object, number, string or atom") unless $valid_start;
|
|
| 8500 |
+ |
|
| 8501 |
+ if ( !$idx->[ P_ALLOW_NONREF ] and !ref $result ) {
|
|
| 8502 |
+ decode_error( |
|
| 8503 |
+ 'JSON text must be an object or array (but found number, string, true, false or null,' |
|
| 8504 |
+ . ' use allow_nonref to allow this)', 1); |
|
| 8238 | 8505 |
} |
| 8239 |
- $file ||= $p->{file} if defined( $p->{file} );
|
|
| 8240 |
- } |
|
| 8241 |
- |
|
| 8242 |
- if ( $err ) {
|
|
| 8243 |
- $err = " $file ($version)\n" . $err; |
|
| 8244 |
- } |
|
| 8245 |
- |
|
| 8246 |
- my %result = ( |
|
| 8247 |
- file => $file, |
|
| 8248 |
- version => $version, |
|
| 8249 |
- err => $err |
|
| 8250 |
- ); |
|
| 8251 |
- |
|
| 8252 |
- return \%result; |
|
| 8253 |
- }; |
|
| 8254 | 8506 |
|
| 8255 |
- sub package_versions_from_directory {
|
|
| 8256 |
- my ( $class, $dir, $files ) = @_; |
|
| 8507 |
+ Carp::croak('something wrong.') if $len < $at; # we won't arrive here.
|
|
| 8257 | 8508 |
|
| 8258 |
- my @files; |
|
| 8509 |
+ my $consumed = defined $ch ? $at - 1 : $at; # consumed JSON text length |
|
| 8259 | 8510 |
|
| 8260 |
- if ( $files ) {
|
|
| 8261 |
- @files = @$files; |
|
| 8262 |
- } else {
|
|
| 8263 |
- find( {
|
|
| 8264 |
- wanted => sub {
|
|
| 8265 |
- push @files, $_ if -f $_ && /\.pm$/; |
|
| 8266 |
- }, |
|
| 8267 |
- no_chdir => 1, |
|
| 8268 |
- }, $dir ); |
|
| 8269 |
- } |
|
| 8511 |
+ white(); # remove tail white space |
|
| 8270 | 8512 |
|
| 8271 |
- # First, we enumerate all packages & versions, |
|
| 8272 |
- # separating into primary & alternative candidates |
|
| 8273 |
- my( %prime, %alt ); |
|
| 8274 |
- foreach my $file (@files) {
|
|
| 8275 |
- my $mapped_filename = File::Spec->abs2rel( $file, $dir ); |
|
| 8276 |
- my @path = split( /\//, $mapped_filename ); |
|
| 8277 |
- (my $prime_package = join( '::', @path )) =~ s/\.pm$//; |
|
| 8278 |
- |
|
| 8279 |
- my $pm_info = $class->new_from_file( $file ); |
|
| 8280 |
- |
|
| 8281 |
- foreach my $package ( $pm_info->packages_inside ) {
|
|
| 8282 |
- next if $package eq 'main'; # main can appear numerous times, ignore |
|
| 8283 |
- next if $package eq 'DB'; # special debugging package, ignore |
|
| 8284 |
- next if grep /^_/, split( /::/, $package ); # private package, ignore |
|
| 8285 |
- |
|
| 8286 |
- my $version = $pm_info->version( $package ); |
|
| 8287 |
- |
|
| 8288 |
- if ( $package eq $prime_package ) {
|
|
| 8289 |
- if ( exists( $prime{$package} ) ) {
|
|
| 8290 |
- die "Unexpected conflict in '$package'; multiple versions found.\n"; |
|
| 8291 |
- } else {
|
|
| 8292 |
- $prime{$package}{file} = $mapped_filename;
|
|
| 8293 |
- $prime{$package}{version} = $version if defined( $version );
|
|
| 8294 |
- } |
|
| 8295 |
- } else {
|
|
| 8296 |
- push( @{$alt{$package}}, {
|
|
| 8297 |
- file => $mapped_filename, |
|
| 8298 |
- version => $version, |
|
| 8299 |
- } ); |
|
| 8513 |
+ if ( $ch ) {
|
|
| 8514 |
+ return ( $result, $consumed ) if ($opt & 0x00000001); # all right if decode_prefix |
|
| 8515 |
+ decode_error("garbage after JSON object");
|
|
| 8300 | 8516 |
} |
| 8301 |
- } |
|
| 8517 |
+ |
|
| 8518 |
+ ( $opt & 0x00000001 ) ? ( $result, $consumed ) : $result; |
|
| 8302 | 8519 |
} |
| 8303 |
- |
|
| 8304 |
- # Then we iterate over all the packages found above, identifying conflicts |
|
| 8305 |
- # and selecting the "best" candidate for recording the file & version |
|
| 8306 |
- # for each package. |
|
| 8307 |
- foreach my $package ( keys( %alt ) ) {
|
|
| 8308 |
- my $result = $resolve_module_versions->( $alt{$package} );
|
|
| 8309 |
- |
|
| 8310 |
- if ( exists( $prime{$package} ) ) { # primary package selected
|
|
| 8311 |
- |
|
| 8312 |
- if ( $result->{err} ) {
|
|
| 8313 |
- # Use the selected primary package, but there are conflicting |
|
| 8314 |
- # errors among multiple alternative packages that need to be |
|
| 8315 |
- # reported |
|
| 8316 |
- log_info {
|
|
| 8317 |
- "Found conflicting versions for package '$package'\n" . |
|
| 8318 |
- " $prime{$package}{file} ($prime{$package}{version})\n" .
|
|
| 8319 |
- $result->{err}
|
|
| 8320 |
- }; |
|
| 8321 |
- |
|
| 8322 |
- } elsif ( defined( $result->{version} ) ) {
|
|
| 8323 |
- # There is a primary package selected, and exactly one |
|
| 8324 |
- # alternative package |
|
| 8325 |
- |
|
| 8326 |
- if ( exists( $prime{$package}{version} ) &&
|
|
| 8327 |
- defined( $prime{$package}{version} ) ) {
|
|
| 8328 |
- # Unless the version of the primary package agrees with the |
|
| 8329 |
- # version of the alternative package, report a conflict |
|
| 8330 |
- if ( $compare_versions->( |
|
| 8331 |
- $prime{$package}{version}, '!=', $result->{version}
|
|
| 8332 |
- ) |
|
| 8333 |
- ) {
|
|
| 8334 | 8520 |
|
| 8335 |
- log_info {
|
|
| 8336 |
- "Found conflicting versions for package '$package'\n" . |
|
| 8337 |
- " $prime{$package}{file} ($prime{$package}{version})\n" .
|
|
| 8338 |
- " $result->{file} ($result->{version})\n"
|
|
| 8339 |
- }; |
|
| 8340 |
- } |
|
| 8341 |
- |
|
| 8342 |
- } else {
|
|
| 8343 |
- # The prime package selected has no version so, we choose to |
|
| 8344 |
- # use any alternative package that does have a version |
|
| 8345 |
- $prime{$package}{file} = $result->{file};
|
|
| 8346 |
- $prime{$package}{version} = $result->{version};
|
|
| 8347 |
- } |
|
| 8348 |
- |
|
| 8349 |
- } else {
|
|
| 8350 |
- # no alt package found with a version, but we have a prime |
|
| 8351 |
- # package so we use it whether it has a version or not |
|
| 8352 |
- } |
|
| 8353 |
- |
|
| 8354 |
- } else { # No primary package was selected, use the best alternative
|
|
| 8355 |
- |
|
| 8356 |
- if ( $result->{err} ) {
|
|
| 8357 |
- log_info {
|
|
| 8358 |
- "Found conflicting versions for package '$package'\n" . |
|
| 8359 |
- $result->{err}
|
|
| 8360 |
- }; |
|
| 8361 |
- } |
|
| 8362 |
- |
|
| 8363 |
- # Despite possible conflicting versions, we choose to record |
|
| 8364 |
- # something rather than nothing |
|
| 8365 |
- $prime{$package}{file} = $result->{file};
|
|
| 8366 |
- $prime{$package}{version} = $result->{version}
|
|
| 8367 |
- if defined( $result->{version} );
|
|
| 8368 |
- } |
|
| 8521 |
+ |
|
| 8522 |
+ sub next_chr {
|
|
| 8523 |
+ return $ch = undef if($at >= $len); |
|
| 8524 |
+ $ch = substr($text, $at++, 1); |
|
| 8525 |
+ } |
|
| 8526 |
+ |
|
| 8527 |
+ |
|
| 8528 |
+ sub value {
|
|
| 8529 |
+ white(); |
|
| 8530 |
+ return if(!defined $ch); |
|
| 8531 |
+ return object() if($ch eq '{');
|
|
| 8532 |
+ return array() if($ch eq '['); |
|
| 8533 |
+ return string() if($ch eq '"' or ($singlequote and $ch eq "'")); |
|
| 8534 |
+ return number() if($ch =~ /[0-9]/ or $ch eq '-'); |
|
| 8535 |
+ return word(); |
|
| 8536 |
+ } |
|
| 8537 |
+ |
|
| 8538 |
+ sub string {
|
|
| 8539 |
+ my ($i, $s, $t, $u); |
|
| 8540 |
+ my $utf16; |
|
| 8541 |
+ my $is_utf8; |
|
| 8542 |
+ |
|
| 8543 |
+ ($is_valid_utf8, $utf8_len) = ('', 0);
|
|
| 8544 |
+ |
|
| 8545 |
+ $s = ''; # basically UTF8 flag on |
|
| 8546 |
+ |
|
| 8547 |
+ if($ch eq '"' or ($singlequote and $ch eq "'")){
|
|
| 8548 |
+ my $boundChar = $ch; |
|
| 8549 |
+ |
|
| 8550 |
+ OUTER: while( defined(next_chr()) ){
|
|
| 8551 |
+ |
|
| 8552 |
+ if($ch eq $boundChar){
|
|
| 8553 |
+ next_chr(); |
|
| 8554 |
+ |
|
| 8555 |
+ if ($utf16) {
|
|
| 8556 |
+ decode_error("missing low surrogate character in surrogate pair");
|
|
| 8557 |
+ } |
|
| 8558 |
+ |
|
| 8559 |
+ utf8::decode($s) if($is_utf8); |
|
| 8560 |
+ |
|
| 8561 |
+ return $s; |
|
| 8562 |
+ } |
|
| 8563 |
+ elsif($ch eq '\\'){
|
|
| 8564 |
+ next_chr(); |
|
| 8565 |
+ if(exists $escapes{$ch}){
|
|
| 8566 |
+ $s .= $escapes{$ch};
|
|
| 8567 |
+ } |
|
| 8568 |
+ elsif($ch eq 'u'){ # UNICODE handling
|
|
| 8569 |
+ my $u = ''; |
|
| 8570 |
+ |
|
| 8571 |
+ for(1..4){
|
|
| 8572 |
+ $ch = next_chr(); |
|
| 8573 |
+ last OUTER if($ch !~ /[0-9a-fA-F]/); |
|
| 8574 |
+ $u .= $ch; |
|
| 8575 |
+ } |
|
| 8576 |
+ |
|
| 8577 |
+ # U+D800 - U+DBFF |
|
| 8578 |
+ if ($u =~ /^[dD][89abAB][0-9a-fA-F]{2}/) { # UTF-16 high surrogate?
|
|
| 8579 |
+ $utf16 = $u; |
|
| 8580 |
+ } |
|
| 8581 |
+ # U+DC00 - U+DFFF |
|
| 8582 |
+ elsif ($u =~ /^[dD][c-fC-F][0-9a-fA-F]{2}/) { # UTF-16 low surrogate?
|
|
| 8583 |
+ unless (defined $utf16) {
|
|
| 8584 |
+ decode_error("missing high surrogate character in surrogate pair");
|
|
| 8585 |
+ } |
|
| 8586 |
+ $is_utf8 = 1; |
|
| 8587 |
+ $s .= JSON_PP_decode_surrogates($utf16, $u) || next; |
|
| 8588 |
+ $utf16 = undef; |
|
| 8589 |
+ } |
|
| 8590 |
+ else {
|
|
| 8591 |
+ if (defined $utf16) {
|
|
| 8592 |
+ decode_error("surrogate pair expected");
|
|
| 8593 |
+ } |
|
| 8594 |
+ |
|
| 8595 |
+ if ( ( my $hex = hex( $u ) ) > 127 ) {
|
|
| 8596 |
+ $is_utf8 = 1; |
|
| 8597 |
+ $s .= JSON_PP_decode_unicode($u) || next; |
|
| 8598 |
+ } |
|
| 8599 |
+ else {
|
|
| 8600 |
+ $s .= chr $hex; |
|
| 8601 |
+ } |
|
| 8602 |
+ } |
|
| 8603 |
+ |
|
| 8604 |
+ } |
|
| 8605 |
+ else{
|
|
| 8606 |
+ unless ($loose) {
|
|
| 8607 |
+ $at -= 2; |
|
| 8608 |
+ decode_error('illegal backslash escape sequence in string');
|
|
| 8609 |
+ } |
|
| 8610 |
+ $s .= $ch; |
|
| 8611 |
+ } |
|
| 8612 |
+ } |
|
| 8613 |
+ else{
|
|
| 8614 |
+ |
|
| 8615 |
+ if ( ord $ch > 127 ) {
|
|
| 8616 |
+ if ( $utf8 ) {
|
|
| 8617 |
+ unless( $ch = is_valid_utf8($ch) ) {
|
|
| 8618 |
+ $at -= 1; |
|
| 8619 |
+ decode_error("malformed UTF-8 character in JSON string");
|
|
| 8620 |
+ } |
|
| 8621 |
+ else {
|
|
| 8622 |
+ $at += $utf8_len - 1; |
|
| 8623 |
+ } |
|
| 8624 |
+ } |
|
| 8625 |
+ else {
|
|
| 8626 |
+ utf8::encode( $ch ); |
|
| 8627 |
+ } |
|
| 8628 |
+ |
|
| 8629 |
+ $is_utf8 = 1; |
|
| 8630 |
+ } |
|
| 8631 |
+ |
|
| 8632 |
+ if (!$loose) {
|
|
| 8633 |
+ if ($ch =~ /[\x00-\x1f\x22\x5c]/) { # '/' ok
|
|
| 8634 |
+ $at--; |
|
| 8635 |
+ decode_error('invalid character encountered while parsing JSON string');
|
|
| 8636 |
+ } |
|
| 8637 |
+ } |
|
| 8638 |
+ |
|
| 8639 |
+ $s .= $ch; |
|
| 8640 |
+ } |
|
| 8641 |
+ } |
|
| 8642 |
+ } |
|
| 8643 |
+ |
|
| 8644 |
+ decode_error("unexpected end of string while parsing JSON string");
|
|
| 8645 |
+ } |
|
| 8646 |
+ |
|
| 8647 |
+ |
|
| 8648 |
+ sub white {
|
|
| 8649 |
+ while( defined $ch ){
|
|
| 8650 |
+ if($ch le ' '){
|
|
| 8651 |
+ next_chr(); |
|
| 8652 |
+ } |
|
| 8653 |
+ elsif($ch eq '/'){
|
|
| 8654 |
+ next_chr(); |
|
| 8655 |
+ if(defined $ch and $ch eq '/'){
|
|
| 8656 |
+ 1 while(defined(next_chr()) and $ch ne "\n" and $ch ne "\r"); |
|
| 8657 |
+ } |
|
| 8658 |
+ elsif(defined $ch and $ch eq '*'){
|
|
| 8659 |
+ next_chr(); |
|
| 8660 |
+ while(1){
|
|
| 8661 |
+ if(defined $ch){
|
|
| 8662 |
+ if($ch eq '*'){
|
|
| 8663 |
+ if(defined(next_chr()) and $ch eq '/'){
|
|
| 8664 |
+ next_chr(); |
|
| 8665 |
+ last; |
|
| 8666 |
+ } |
|
| 8667 |
+ } |
|
| 8668 |
+ else{
|
|
| 8669 |
+ next_chr(); |
|
| 8670 |
+ } |
|
| 8671 |
+ } |
|
| 8672 |
+ else{
|
|
| 8673 |
+ decode_error("Unterminated comment");
|
|
| 8674 |
+ } |
|
| 8675 |
+ } |
|
| 8676 |
+ next; |
|
| 8677 |
+ } |
|
| 8678 |
+ else{
|
|
| 8679 |
+ $at--; |
|
| 8680 |
+ decode_error("malformed JSON string, neither array, object, number, string or atom");
|
|
| 8681 |
+ } |
|
| 8682 |
+ } |
|
| 8683 |
+ else{
|
|
| 8684 |
+ if ($relaxed and $ch eq '#') { # correctly?
|
|
| 8685 |
+ pos($text) = $at; |
|
| 8686 |
+ $text =~ /\G([^\n]*(?:\r\n|\r|\n|$))/g; |
|
| 8687 |
+ $at = pos($text); |
|
| 8688 |
+ next_chr; |
|
| 8689 |
+ next; |
|
| 8690 |
+ } |
|
| 8691 |
+ |
|
| 8692 |
+ last; |
|
| 8693 |
+ } |
|
| 8694 |
+ } |
|
| 8695 |
+ } |
|
| 8696 |
+ |
|
| 8697 |
+ |
|
| 8698 |
+ sub array {
|
|
| 8699 |
+ my $a = $_[0] || []; # you can use this code to use another array ref object. |
|
| 8700 |
+ |
|
| 8701 |
+ decode_error('json text or perl structure exceeds maximum nesting level (max_depth set too low?)')
|
|
| 8702 |
+ if (++$depth > $max_depth); |
|
| 8703 |
+ |
|
| 8704 |
+ next_chr(); |
|
| 8705 |
+ white(); |
|
| 8706 |
+ |
|
| 8707 |
+ if(defined $ch and $ch eq ']'){
|
|
| 8708 |
+ --$depth; |
|
| 8709 |
+ next_chr(); |
|
| 8710 |
+ return $a; |
|
| 8711 |
+ } |
|
| 8712 |
+ else {
|
|
| 8713 |
+ while(defined($ch)){
|
|
| 8714 |
+ push @$a, value(); |
|
| 8715 |
+ |
|
| 8716 |
+ white(); |
|
| 8717 |
+ |
|
| 8718 |
+ if (!defined $ch) {
|
|
| 8719 |
+ last; |
|
| 8720 |
+ } |
|
| 8721 |
+ |
|
| 8722 |
+ if($ch eq ']'){
|
|
| 8723 |
+ --$depth; |
|
| 8724 |
+ next_chr(); |
|
| 8725 |
+ return $a; |
|
| 8726 |
+ } |
|
| 8727 |
+ |
|
| 8728 |
+ if($ch ne ','){
|
|
| 8729 |
+ last; |
|
| 8730 |
+ } |
|
| 8731 |
+ |
|
| 8732 |
+ next_chr(); |
|
| 8733 |
+ white(); |
|
| 8734 |
+ |
|
| 8735 |
+ if ($relaxed and $ch eq ']') {
|
|
| 8736 |
+ --$depth; |
|
| 8737 |
+ next_chr(); |
|
| 8738 |
+ return $a; |
|
| 8739 |
+ } |
|
| 8740 |
+ |
|
| 8741 |
+ } |
|
| 8742 |
+ } |
|
| 8743 |
+ |
|
| 8744 |
+ decode_error(", or ] expected while parsing array");
|
|
| 8745 |
+ } |
|
| 8746 |
+ |
|
| 8747 |
+ |
|
| 8748 |
+ sub object {
|
|
| 8749 |
+ my $o = $_[0] || {}; # you can use this code to use another hash ref object.
|
|
| 8750 |
+ my $k; |
|
| 8751 |
+ |
|
| 8752 |
+ decode_error('json text or perl structure exceeds maximum nesting level (max_depth set too low?)')
|
|
| 8753 |
+ if (++$depth > $max_depth); |
|
| 8754 |
+ next_chr(); |
|
| 8755 |
+ white(); |
|
| 8756 |
+ |
|
| 8757 |
+ if(defined $ch and $ch eq '}'){
|
|
| 8758 |
+ --$depth; |
|
| 8759 |
+ next_chr(); |
|
| 8760 |
+ if ($F_HOOK) {
|
|
| 8761 |
+ return _json_object_hook($o); |
|
| 8762 |
+ } |
|
| 8763 |
+ return $o; |
|
| 8764 |
+ } |
|
| 8765 |
+ else {
|
|
| 8766 |
+ while (defined $ch) {
|
|
| 8767 |
+ $k = ($allow_barekey and $ch ne '"' and $ch ne "'") ? bareKey() : string(); |
|
| 8768 |
+ white(); |
|
| 8769 |
+ |
|
| 8770 |
+ if(!defined $ch or $ch ne ':'){
|
|
| 8771 |
+ $at--; |
|
| 8772 |
+ decode_error("':' expected");
|
|
| 8773 |
+ } |
|
| 8774 |
+ |
|
| 8775 |
+ next_chr(); |
|
| 8776 |
+ $o->{$k} = value();
|
|
| 8777 |
+ white(); |
|
| 8778 |
+ |
|
| 8779 |
+ last if (!defined $ch); |
|
| 8780 |
+ |
|
| 8781 |
+ if($ch eq '}'){
|
|
| 8782 |
+ --$depth; |
|
| 8783 |
+ next_chr(); |
|
| 8784 |
+ if ($F_HOOK) {
|
|
| 8785 |
+ return _json_object_hook($o); |
|
| 8786 |
+ } |
|
| 8787 |
+ return $o; |
|
| 8788 |
+ } |
|
| 8789 |
+ |
|
| 8790 |
+ if($ch ne ','){
|
|
| 8791 |
+ last; |
|
| 8792 |
+ } |
|
| 8793 |
+ |
|
| 8794 |
+ next_chr(); |
|
| 8795 |
+ white(); |
|
| 8796 |
+ |
|
| 8797 |
+ if ($relaxed and $ch eq '}') {
|
|
| 8798 |
+ --$depth; |
|
| 8799 |
+ next_chr(); |
|
| 8800 |
+ if ($F_HOOK) {
|
|
| 8801 |
+ return _json_object_hook($o); |
|
| 8802 |
+ } |
|
| 8803 |
+ return $o; |
|
| 8804 |
+ } |
|
| 8805 |
+ |
|
| 8806 |
+ } |
|
| 8807 |
+ |
|
| 8808 |
+ } |
|
| 8809 |
+ |
|
| 8810 |
+ $at--; |
|
| 8811 |
+ decode_error(", or } expected while parsing object/hash");
|
|
| 8812 |
+ } |
|
| 8813 |
+ |
|
| 8814 |
+ |
|
| 8815 |
+ sub bareKey { # doesn't strictly follow Standard ECMA-262 3rd Edition
|
|
| 8816 |
+ my $key; |
|
| 8817 |
+ while($ch =~ /[^\x00-\x23\x25-\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F]/){
|
|
| 8818 |
+ $key .= $ch; |
|
| 8819 |
+ next_chr(); |
|
| 8820 |
+ } |
|
| 8821 |
+ return $key; |
|
| 8822 |
+ } |
|
| 8823 |
+ |
|
| 8824 |
+ |
|
| 8825 |
+ sub word {
|
|
| 8826 |
+ my $word = substr($text,$at-1,4); |
|
| 8827 |
+ |
|
| 8828 |
+ if($word eq 'true'){
|
|
| 8829 |
+ $at += 3; |
|
| 8830 |
+ next_chr; |
|
| 8831 |
+ return $JSON::PP::true; |
|
| 8832 |
+ } |
|
| 8833 |
+ elsif($word eq 'null'){
|
|
| 8834 |
+ $at += 3; |
|
| 8835 |
+ next_chr; |
|
| 8836 |
+ return undef; |
|
| 8837 |
+ } |
|
| 8838 |
+ elsif($word eq 'fals'){
|
|
| 8839 |
+ $at += 3; |
|
| 8840 |
+ if(substr($text,$at,1) eq 'e'){
|
|
| 8841 |
+ $at++; |
|
| 8842 |
+ next_chr; |
|
| 8843 |
+ return $JSON::PP::false; |
|
| 8844 |
+ } |
|
| 8845 |
+ } |
|
| 8846 |
+ |
|
| 8847 |
+ $at--; # for decode_error report |
|
| 8848 |
+ |
|
| 8849 |
+ decode_error("'null' expected") if ($word =~ /^n/);
|
|
| 8850 |
+ decode_error("'true' expected") if ($word =~ /^t/);
|
|
| 8851 |
+ decode_error("'false' expected") if ($word =~ /^f/);
|
|
| 8852 |
+ decode_error("malformed JSON string, neither array, object, number, string or atom");
|
|
| 8853 |
+ } |
|
| 8854 |
+ |
|
| 8855 |
+ |
|
| 8856 |
+ sub number {
|
|
| 8857 |
+ my $n = ''; |
|
| 8858 |
+ my $v; |
|
| 8859 |
+ |
|
| 8860 |
+ # According to RFC4627, hex or oct digts are invalid. |
|
| 8861 |
+ if($ch eq '0'){
|
|
| 8862 |
+ my $peek = substr($text,$at,1); |
|
| 8863 |
+ my $hex = $peek =~ /[xX]/; # 0 or 1 |
|
| 8864 |
+ |
|
| 8865 |
+ if($hex){
|
|
| 8866 |
+ decode_error("malformed number (leading zero must not be followed by another digit)");
|
|
| 8867 |
+ ($n) = ( substr($text, $at+1) =~ /^([0-9a-fA-F]+)/); |
|
| 8868 |
+ } |
|
| 8869 |
+ else{ # oct
|
|
| 8870 |
+ ($n) = ( substr($text, $at) =~ /^([0-7]+)/); |
|
| 8871 |
+ if (defined $n and length $n > 1) {
|
|
| 8872 |
+ decode_error("malformed number (leading zero must not be followed by another digit)");
|
|
| 8873 |
+ } |
|
| 8874 |
+ } |
|
| 8875 |
+ |
|
| 8876 |
+ if(defined $n and length($n)){
|
|
| 8877 |
+ if (!$hex and length($n) == 1) {
|
|
| 8878 |
+ decode_error("malformed number (leading zero must not be followed by another digit)");
|
|
| 8879 |
+ } |
|
| 8880 |
+ $at += length($n) + $hex; |
|
| 8881 |
+ next_chr; |
|
| 8882 |
+ return $hex ? hex($n) : oct($n); |
|
| 8883 |
+ } |
|
| 8884 |
+ } |
|
| 8885 |
+ |
|
| 8886 |
+ if($ch eq '-'){
|
|
| 8887 |
+ $n = '-'; |
|
| 8888 |
+ next_chr; |
|
| 8889 |
+ if (!defined $ch or $ch !~ /\d/) {
|
|
| 8890 |
+ decode_error("malformed number (no digits after initial minus)");
|
|
| 8891 |
+ } |
|
| 8892 |
+ } |
|
| 8893 |
+ |
|
| 8894 |
+ while(defined $ch and $ch =~ /\d/){
|
|
| 8895 |
+ $n .= $ch; |
|
| 8896 |
+ next_chr; |
|
| 8897 |
+ } |
|
| 8898 |
+ |
|
| 8899 |
+ if(defined $ch and $ch eq '.'){
|
|
| 8900 |
+ $n .= '.'; |
|
| 8901 |
+ |
|
| 8902 |
+ next_chr; |
|
| 8903 |
+ if (!defined $ch or $ch !~ /\d/) {
|
|
| 8904 |
+ decode_error("malformed number (no digits after decimal point)");
|
|
| 8905 |
+ } |
|
| 8906 |
+ else {
|
|
| 8907 |
+ $n .= $ch; |
|
| 8908 |
+ } |
|
| 8909 |
+ |
|
| 8910 |
+ while(defined(next_chr) and $ch =~ /\d/){
|
|
| 8911 |
+ $n .= $ch; |
|
| 8912 |
+ } |
|
| 8913 |
+ } |
|
| 8914 |
+ |
|
| 8915 |
+ if(defined $ch and ($ch eq 'e' or $ch eq 'E')){
|
|
| 8916 |
+ $n .= $ch; |
|
| 8917 |
+ next_chr; |
|
| 8918 |
+ |
|
| 8919 |
+ if(defined($ch) and ($ch eq '+' or $ch eq '-')){
|
|
| 8920 |
+ $n .= $ch; |
|
| 8921 |
+ next_chr; |
|
| 8922 |
+ if (!defined $ch or $ch =~ /\D/) {
|
|
| 8923 |
+ decode_error("malformed number (no digits after exp sign)");
|
|
| 8924 |
+ } |
|
| 8925 |
+ $n .= $ch; |
|
| 8926 |
+ } |
|
| 8927 |
+ elsif(defined($ch) and $ch =~ /\d/){
|
|
| 8928 |
+ $n .= $ch; |
|
| 8929 |
+ } |
|
| 8930 |
+ else {
|
|
| 8931 |
+ decode_error("malformed number (no digits after exp sign)");
|
|
| 8932 |
+ } |
|
| 8933 |
+ |
|
| 8934 |
+ while(defined(next_chr) and $ch =~ /\d/){
|
|
| 8935 |
+ $n .= $ch; |
|
| 8936 |
+ } |
|
| 8937 |
+ |
|
| 8938 |
+ } |
|
| 8939 |
+ |
|
| 8940 |
+ $v .= $n; |
|
| 8941 |
+ |
|
| 8942 |
+ if ($v !~ /[.eE]/ and length $v > $max_intsize) {
|
|
| 8943 |
+ if ($allow_bigint) { # from Adam Sussman
|
|
| 8944 |
+ require Math::BigInt; |
|
| 8945 |
+ return Math::BigInt->new($v); |
|
| 8946 |
+ } |
|
| 8947 |
+ else {
|
|
| 8948 |
+ return "$v"; |
|
| 8949 |
+ } |
|
| 8950 |
+ } |
|
| 8951 |
+ elsif ($allow_bigint) {
|
|
| 8952 |
+ require Math::BigFloat; |
|
| 8953 |
+ return Math::BigFloat->new($v); |
|
| 8954 |
+ } |
|
| 8955 |
+ |
|
| 8956 |
+ return 0+$v; |
|
| 8957 |
+ } |
|
| 8958 |
+ |
|
| 8959 |
+ |
|
| 8960 |
+ sub is_valid_utf8 {
|
|
| 8961 |
+ |
|
| 8962 |
+ $utf8_len = $_[0] =~ /[\x00-\x7F]/ ? 1 |
|
| 8963 |
+ : $_[0] =~ /[\xC2-\xDF]/ ? 2 |
|
| 8964 |
+ : $_[0] =~ /[\xE0-\xEF]/ ? 3 |
|
| 8965 |
+ : $_[0] =~ /[\xF0-\xF4]/ ? 4 |
|
| 8966 |
+ : 0 |
|
| 8967 |
+ ; |
|
| 8968 |
+ |
|
| 8969 |
+ return unless $utf8_len; |
|
| 8970 |
+ |
|
| 8971 |
+ my $is_valid_utf8 = substr($text, $at - 1, $utf8_len); |
|
| 8972 |
+ |
|
| 8973 |
+ return ( $is_valid_utf8 =~ /^(?: |
|
| 8974 |
+ [\x00-\x7F] |
|
| 8975 |
+ |[\xC2-\xDF][\x80-\xBF] |
|
| 8976 |
+ |[\xE0][\xA0-\xBF][\x80-\xBF] |
|
| 8977 |
+ |[\xE1-\xEC][\x80-\xBF][\x80-\xBF] |
|
| 8978 |
+ |[\xED][\x80-\x9F][\x80-\xBF] |
|
| 8979 |
+ |[\xEE-\xEF][\x80-\xBF][\x80-\xBF] |
|
| 8980 |
+ |[\xF0][\x90-\xBF][\x80-\xBF][\x80-\xBF] |
|
| 8981 |
+ |[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF] |
|
| 8982 |
+ |[\xF4][\x80-\x8F][\x80-\xBF][\x80-\xBF] |
|
| 8983 |
+ )$/x ) ? $is_valid_utf8 : ''; |
|
| 8984 |
+ } |
|
| 8985 |
+ |
|
| 8986 |
+ |
|
| 8987 |
+ sub decode_error {
|
|
| 8988 |
+ my $error = shift; |
|
| 8989 |
+ my $no_rep = shift; |
|
| 8990 |
+ my $str = defined $text ? substr($text, $at) : ''; |
|
| 8991 |
+ my $mess = ''; |
|
| 8992 |
+ my $type = $] >= 5.008 ? 'U*' |
|
| 8993 |
+ : $] < 5.006 ? 'C*' |
|
| 8994 |
+ : utf8::is_utf8( $str ) ? 'U*' # 5.6 |
|
| 8995 |
+ : 'C*' |
|
| 8996 |
+ ; |
|
| 8997 |
+ |
|
| 8998 |
+ for my $c ( unpack( $type, $str ) ) { # emulate pv_uni_display() ?
|
|
| 8999 |
+ $mess .= $c == 0x07 ? '\a' |
|
| 9000 |
+ : $c == 0x09 ? '\t' |
|
| 9001 |
+ : $c == 0x0a ? '\n' |
|
| 9002 |
+ : $c == 0x0d ? '\r' |
|
| 9003 |
+ : $c == 0x0c ? '\f' |
|
| 9004 |
+ : $c < 0x20 ? sprintf('\x{%x}', $c)
|
|
| 9005 |
+ : $c == 0x5c ? '\\\\' |
|
| 9006 |
+ : $c < 0x80 ? chr($c) |
|
| 9007 |
+ : sprintf('\x{%x}', $c)
|
|
| 9008 |
+ ; |
|
| 9009 |
+ if ( length $mess >= 20 ) {
|
|
| 9010 |
+ $mess .= '...'; |
|
| 9011 |
+ last; |
|
| 9012 |
+ } |
|
| 9013 |
+ } |
|
| 9014 |
+ |
|
| 9015 |
+ unless ( length $mess ) {
|
|
| 9016 |
+ $mess = '(end of string)'; |
|
| 9017 |
+ } |
|
| 9018 |
+ |
|
| 9019 |
+ Carp::croak ( |
|
| 9020 |
+ $no_rep ? "$error" : "$error, at character offset $at (before \"$mess\")" |
|
| 9021 |
+ ); |
|
| 9022 |
+ |
|
| 9023 |
+ } |
|
| 9024 |
+ |
|
| 9025 |
+ |
|
| 9026 |
+ sub _json_object_hook {
|
|
| 9027 |
+ my $o = $_[0]; |
|
| 9028 |
+ my @ks = keys %{$o};
|
|
| 9029 |
+ |
|
| 9030 |
+ if ( $cb_sk_object and @ks == 1 and exists $cb_sk_object->{ $ks[0] } and ref $cb_sk_object->{ $ks[0] } ) {
|
|
| 9031 |
+ my @val = $cb_sk_object->{ $ks[0] }->( $o->{$ks[0]} );
|
|
| 9032 |
+ if (@val == 1) {
|
|
| 9033 |
+ return $val[0]; |
|
| 9034 |
+ } |
|
| 9035 |
+ } |
|
| 9036 |
+ |
|
| 9037 |
+ my @val = $cb_object->($o) if ($cb_object); |
|
| 9038 |
+ if (@val == 0 or @val > 1) {
|
|
| 9039 |
+ return $o; |
|
| 9040 |
+ } |
|
| 9041 |
+ else {
|
|
| 9042 |
+ return $val[0]; |
|
| 9043 |
+ } |
|
| 9044 |
+ } |
|
| 9045 |
+ |
|
| 9046 |
+ |
|
| 9047 |
+ sub PP_decode_box {
|
|
| 9048 |
+ {
|
|
| 9049 |
+ text => $text, |
|
| 9050 |
+ at => $at, |
|
| 9051 |
+ ch => $ch, |
|
| 9052 |
+ len => $len, |
|
| 9053 |
+ depth => $depth, |
|
| 9054 |
+ encoding => $encoding, |
|
| 9055 |
+ is_valid_utf8 => $is_valid_utf8, |
|
| 9056 |
+ }; |
|
| 9057 |
+ } |
|
| 9058 |
+ |
|
| 9059 |
+ } # PARSE |
|
| 9060 |
+ |
|
| 9061 |
+ |
|
| 9062 |
+ sub _decode_surrogates { # from perlunicode
|
|
| 9063 |
+ my $uni = 0x10000 + (hex($_[0]) - 0xD800) * 0x400 + (hex($_[1]) - 0xDC00); |
|
| 9064 |
+ my $un = pack('U*', $uni);
|
|
| 9065 |
+ utf8::encode( $un ); |
|
| 9066 |
+ return $un; |
|
| 9067 |
+ } |
|
| 9068 |
+ |
|
| 9069 |
+ |
|
| 9070 |
+ sub _decode_unicode {
|
|
| 9071 |
+ my $un = pack('U', hex shift);
|
|
| 9072 |
+ utf8::encode( $un ); |
|
| 9073 |
+ return $un; |
|
| 9074 |
+ } |
|
| 9075 |
+ |
|
| 9076 |
+ # |
|
| 9077 |
+ # Setup for various Perl versions (the code from JSON::PP58) |
|
| 9078 |
+ # |
|
| 9079 |
+ |
|
| 9080 |
+ BEGIN {
|
|
| 9081 |
+ |
|
| 9082 |
+ unless ( defined &utf8::is_utf8 ) {
|
|
| 9083 |
+ require Encode; |
|
| 9084 |
+ *utf8::is_utf8 = *Encode::is_utf8; |
|
| 9085 |
+ } |
|
| 9086 |
+ |
|
| 9087 |
+ if ( $] >= 5.008 ) {
|
|
| 9088 |
+ *JSON::PP::JSON_PP_encode_ascii = \&_encode_ascii; |
|
| 9089 |
+ *JSON::PP::JSON_PP_encode_latin1 = \&_encode_latin1; |
|
| 9090 |
+ *JSON::PP::JSON_PP_decode_surrogates = \&_decode_surrogates; |
|
| 9091 |
+ *JSON::PP::JSON_PP_decode_unicode = \&_decode_unicode; |
|
| 9092 |
+ } |
|
| 9093 |
+ |
|
| 9094 |
+ if ($] >= 5.008 and $] < 5.008003) { # join() in 5.8.0 - 5.8.2 is broken.
|
|
| 9095 |
+ package JSON::PP; |
|
| 9096 |
+ require subs; |
|
| 9097 |
+ subs->import('join');
|
|
| 9098 |
+ eval q| |
|
| 9099 |
+ sub join {
|
|
| 9100 |
+ return '' if (@_ < 2); |
|
| 9101 |
+ my $j = shift; |
|
| 9102 |
+ my $str = shift; |
|
| 9103 |
+ for (@_) { $str .= $j . $_; }
|
|
| 9104 |
+ return $str; |
|
| 9105 |
+ } |
|
| 9106 |
+ |; |
|
| 9107 |
+ } |
|
| 9108 |
+ |
|
| 9109 |
+ |
|
| 9110 |
+ sub JSON::PP::incr_parse {
|
|
| 9111 |
+ local $Carp::CarpLevel = 1; |
|
| 9112 |
+ ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_parse( @_ );
|
|
| 9113 |
+ } |
|
| 9114 |
+ |
|
| 9115 |
+ |
|
| 9116 |
+ sub JSON::PP::incr_skip {
|
|
| 9117 |
+ ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_skip;
|
|
| 8369 | 9118 |
} |
| 8370 |
- |
|
| 8371 |
- # Normalize versions. Can't use exists() here because of bug in YAML::Node. |
|
| 8372 |
- # XXX "bug in YAML::Node" comment seems irrelvant -- dagolden, 2009-05-18 |
|
| 8373 |
- for (grep defined $_->{version}, values %prime) {
|
|
| 8374 |
- $_->{version} = $normalize_version->( $_->{version} );
|
|
| 9119 |
+ |
|
| 9120 |
+ |
|
| 9121 |
+ sub JSON::PP::incr_reset {
|
|
| 9122 |
+ ( $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new )->incr_reset;
|
|
| 8375 | 9123 |
} |
| 8376 |
- |
|
| 8377 |
- return \%prime; |
|
| 8378 |
- } |
|
| 8379 |
- } |
|
| 8380 |
- |
|
| 8381 | 9124 |
|
| 8382 |
- sub _init {
|
|
| 8383 |
- my $class = shift; |
|
| 8384 |
- my $module = shift; |
|
| 8385 |
- my $filename = shift; |
|
| 8386 |
- my %props = @_; |
|
| 9125 |
+ eval q{
|
|
| 9126 |
+ sub JSON::PP::incr_text : lvalue {
|
|
| 9127 |
+ $_[0]->{_incr_parser} ||= JSON::PP::IncrParser->new;
|
|
| 8387 | 9128 |
|
| 8388 |
- my $handle = delete $props{handle};
|
|
| 8389 |
- my( %valid_props, @valid_props ); |
|
| 8390 |
- @valid_props = qw( collect_pod inc ); |
|
| 8391 |
- @valid_props{@valid_props} = delete( @props{@valid_props} );
|
|
| 8392 |
- warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
|
|
| 9129 |
+ if ( $_[0]->{_incr_parser}->{incr_parsing} ) {
|
|
| 9130 |
+ Carp::croak("incr_text can not be called when the incremental parser already started parsing");
|
|
| 9131 |
+ } |
|
| 9132 |
+ $_[0]->{_incr_parser}->{incr_text};
|
|
| 9133 |
+ } |
|
| 9134 |
+ } if ( $] >= 5.006 ); |
|
| 8393 | 9135 |
|
| 8394 |
- my %data = ( |
|
| 8395 |
- module => $module, |
|
| 8396 |
- filename => $filename, |
|
| 8397 |
- version => undef, |
|
| 8398 |
- packages => [], |
|
| 8399 |
- versions => {},
|
|
| 8400 |
- pod => {},
|
|
| 8401 |
- pod_headings => [], |
|
| 8402 |
- collect_pod => 0, |
|
| 9136 |
+ } # Setup for various Perl versions (the code from JSON::PP58) |
|
| 8403 | 9137 |
|
| 8404 |
- %valid_props, |
|
| 8405 |
- ); |
|
| 8406 | 9138 |
|
| 8407 |
- my $self = bless(\%data, $class); |
|
| 9139 |
+ ############################### |
|
| 9140 |
+ # Utilities |
|
| 9141 |
+ # |
|
| 8408 | 9142 |
|
| 8409 |
- if ( $handle ) {
|
|
| 8410 |
- $self->_parse_fh($handle); |
|
| 8411 |
- } |
|
| 8412 |
- else {
|
|
| 8413 |
- $self->_parse_file(); |
|
| 8414 |
- } |
|
| 9143 |
+ BEGIN {
|
|
| 9144 |
+ eval 'require Scalar::Util'; |
|
| 9145 |
+ unless($@){
|
|
| 9146 |
+ *JSON::PP::blessed = \&Scalar::Util::blessed; |
|
| 9147 |
+ *JSON::PP::reftype = \&Scalar::Util::reftype; |
|
| 9148 |
+ *JSON::PP::refaddr = \&Scalar::Util::refaddr; |
|
| 9149 |
+ } |
|
| 9150 |
+ else{ # This code is from Sclar::Util.
|
|
| 9151 |
+ # warn $@; |
|
| 9152 |
+ eval 'sub UNIVERSAL::a_sub_not_likely_to_be_here { ref($_[0]) }';
|
|
| 9153 |
+ *JSON::PP::blessed = sub {
|
|
| 9154 |
+ local($@, $SIG{__DIE__}, $SIG{__WARN__});
|
|
| 9155 |
+ ref($_[0]) ? eval { $_[0]->a_sub_not_likely_to_be_here } : undef;
|
|
| 9156 |
+ }; |
|
| 9157 |
+ my %tmap = qw( |
|
| 9158 |
+ B::NULL SCALAR |
|
| 9159 |
+ B::HV HASH |
|
| 9160 |
+ B::AV ARRAY |
|
| 9161 |
+ B::CV CODE |
|
| 9162 |
+ B::IO IO |
|
| 9163 |
+ B::GV GLOB |
|
| 9164 |
+ B::REGEXP REGEXP |
|
| 9165 |
+ ); |
|
| 9166 |
+ *JSON::PP::reftype = sub {
|
|
| 9167 |
+ my $r = shift; |
|
| 8415 | 9168 |
|
| 8416 |
- unless($self->{module} and length($self->{module})) {
|
|
| 8417 |
- my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
|
|
| 8418 |
- if($f =~ /\.pm$/) {
|
|
| 8419 |
- $f =~ s/\..+$//; |
|
| 8420 |
- my @candidates = grep /$f$/, @{$self->{packages}};
|
|
| 8421 |
- $self->{module} = shift(@candidates); # punt
|
|
| 9169 |
+ return undef unless length(ref($r)); |
|
| 9170 |
+ |
|
| 9171 |
+ my $t = ref(B::svref_2object($r)); |
|
| 9172 |
+ |
|
| 9173 |
+ return |
|
| 9174 |
+ exists $tmap{$t} ? $tmap{$t}
|
|
| 9175 |
+ : length(ref($$r)) ? 'REF' |
|
| 9176 |
+ : 'SCALAR'; |
|
| 9177 |
+ }; |
|
| 9178 |
+ *JSON::PP::refaddr = sub {
|
|
| 9179 |
+ return undef unless length(ref($_[0])); |
|
| 9180 |
+ |
|
| 9181 |
+ my $addr; |
|
| 9182 |
+ if(defined(my $pkg = blessed($_[0]))) {
|
|
| 9183 |
+ $addr .= bless $_[0], 'Scalar::Util::Fake'; |
|
| 9184 |
+ bless $_[0], $pkg; |
|
| 9185 |
+ } |
|
| 9186 |
+ else {
|
|
| 9187 |
+ $addr .= $_[0] |
|
| 9188 |
+ } |
|
| 9189 |
+ |
|
| 9190 |
+ $addr =~ /0x(\w+)/; |
|
| 9191 |
+ local $^W; |
|
| 9192 |
+ #no warnings 'portable'; |
|
| 9193 |
+ hex($1); |
|
| 9194 |
+ } |
|
| 8422 | 9195 |
} |
| 8423 |
- else {
|
|
| 8424 |
- if(grep /main/, @{$self->{packages}}) {
|
|
| 8425 |
- $self->{module} = 'main';
|
|
| 8426 |
- } |
|
| 8427 |
- else {
|
|
| 8428 |
- $self->{module} = $self->{packages}[0] || '';
|
|
| 8429 |
- } |
|
| 9196 |
+ } |
|
| 9197 |
+ |
|
| 9198 |
+ |
|
| 9199 |
+ # shamely copied and modified from JSON::XS code. |
|
| 9200 |
+ |
|
| 9201 |
+ $JSON::PP::true = do { bless \(my $dummy = 1), "JSON::PP::Boolean" };
|
|
| 9202 |
+ $JSON::PP::false = do { bless \(my $dummy = 0), "JSON::PP::Boolean" };
|
|
| 9203 |
+ |
|
| 9204 |
+ sub is_bool { defined $_[0] and UNIVERSAL::isa($_[0], "JSON::PP::Boolean"); }
|
|
| 9205 |
+ |
|
| 9206 |
+ sub true { $JSON::PP::true }
|
|
| 9207 |
+ sub false { $JSON::PP::false }
|
|
| 9208 |
+ sub null { undef; }
|
|
| 9209 |
+ |
|
| 9210 |
+ ############################### |
|
| 9211 |
+ |
|
| 9212 |
+ package JSON::PP::Boolean; |
|
| 9213 |
+ |
|
| 9214 |
+ use overload ( |
|
| 9215 |
+ "0+" => sub { ${$_[0]} },
|
|
| 9216 |
+ "++" => sub { $_[0] = ${$_[0]} + 1 },
|
|
| 9217 |
+ "--" => sub { $_[0] = ${$_[0]} - 1 },
|
|
| 9218 |
+ fallback => 1, |
|
| 9219 |
+ ); |
|
| 9220 |
+ |
|
| 9221 |
+ |
|
| 9222 |
+ ############################### |
|
| 9223 |
+ |
|
| 9224 |
+ package JSON::PP::IncrParser; |
|
| 9225 |
+ |
|
| 9226 |
+ use strict; |
|
| 9227 |
+ |
|
| 9228 |
+ use constant INCR_M_WS => 0; # initial whitespace skipping |
|
| 9229 |
+ use constant INCR_M_STR => 1; # inside string |
|
| 9230 |
+ use constant INCR_M_BS => 2; # inside backslash |
|
| 9231 |
+ use constant INCR_M_JSON => 3; # outside anything, count nesting |
|
| 9232 |
+ use constant INCR_M_C0 => 4; |
|
| 9233 |
+ use constant INCR_M_C1 => 5; |
|
| 9234 |
+ |
|
| 9235 |
+ $JSON::PP::IncrParser::VERSION = '1.01'; |
|
| 9236 |
+ |
|
| 9237 |
+ my $unpack_format = $] < 5.006 ? 'C*' : 'U*'; |
|
| 9238 |
+ |
|
| 9239 |
+ sub new {
|
|
| 9240 |
+ my ( $class ) = @_; |
|
| 9241 |
+ |
|
| 9242 |
+ bless {
|
|
| 9243 |
+ incr_nest => 0, |
|
| 9244 |
+ incr_text => undef, |
|
| 9245 |
+ incr_parsing => 0, |
|
| 9246 |
+ incr_p => 0, |
|
| 9247 |
+ }, $class; |
|
| 9248 |
+ } |
|
| 9249 |
+ |
|
| 9250 |
+ |
|
| 9251 |
+ sub incr_parse {
|
|
| 9252 |
+ my ( $self, $coder, $text ) = @_; |
|
| 9253 |
+ |
|
| 9254 |
+ $self->{incr_text} = '' unless ( defined $self->{incr_text} );
|
|
| 9255 |
+ |
|
| 9256 |
+ if ( defined $text ) {
|
|
| 9257 |
+ if ( utf8::is_utf8( $text ) and !utf8::is_utf8( $self->{incr_text} ) ) {
|
|
| 9258 |
+ utf8::upgrade( $self->{incr_text} ) ;
|
|
| 9259 |
+ utf8::decode( $self->{incr_text} ) ;
|
|
| 9260 |
+ } |
|
| 9261 |
+ $self->{incr_text} .= $text;
|
|
| 8430 | 9262 |
} |
| 8431 |
- } |
|
| 8432 | 9263 |
|
| 8433 |
- $self->{version} = $self->{versions}{$self->{module}}
|
|
| 8434 |
- if defined( $self->{module} );
|
|
| 8435 | 9264 |
|
| 8436 |
- return $self; |
|
| 9265 |
+ my $max_size = $coder->get_max_size; |
|
| 9266 |
+ |
|
| 9267 |
+ if ( defined wantarray ) {
|
|
| 9268 |
+ |
|
| 9269 |
+ $self->{incr_mode} = INCR_M_WS unless defined $self->{incr_mode};
|
|
| 9270 |
+ |
|
| 9271 |
+ if ( wantarray ) {
|
|
| 9272 |
+ my @ret; |
|
| 9273 |
+ |
|
| 9274 |
+ $self->{incr_parsing} = 1;
|
|
| 9275 |
+ |
|
| 9276 |
+ do {
|
|
| 9277 |
+ push @ret, $self->_incr_parse( $coder, $self->{incr_text} );
|
|
| 9278 |
+ |
|
| 9279 |
+ unless ( !$self->{incr_nest} and $self->{incr_mode} == INCR_M_JSON ) {
|
|
| 9280 |
+ $self->{incr_mode} = INCR_M_WS if $self->{incr_mode} != INCR_M_STR;
|
|
| 9281 |
+ } |
|
| 9282 |
+ |
|
| 9283 |
+ } until ( length $self->{incr_text} >= $self->{incr_p} );
|
|
| 9284 |
+ |
|
| 9285 |
+ $self->{incr_parsing} = 0;
|
|
| 9286 |
+ |
|
| 9287 |
+ return @ret; |
|
| 9288 |
+ } |
|
| 9289 |
+ else { # in scalar context
|
|
| 9290 |
+ $self->{incr_parsing} = 1;
|
|
| 9291 |
+ my $obj = $self->_incr_parse( $coder, $self->{incr_text} );
|
|
| 9292 |
+ $self->{incr_parsing} = 0 if defined $obj; # pointed by Martin J. Evans
|
|
| 9293 |
+ return $obj ? $obj : undef; # $obj is an empty string, parsing was completed. |
|
| 9294 |
+ } |
|
| 9295 |
+ |
|
| 9296 |
+ } |
|
| 9297 |
+ |
|
| 8437 | 9298 |
} |
| 8438 | 9299 |
|
| 8439 |
- # class method |
|
| 8440 |
- sub _do_find_module {
|
|
| 8441 |
- my $class = shift; |
|
| 8442 |
- my $module = shift || die 'find_module_by_name() requires a package name'; |
|
| 8443 |
- my $dirs = shift || \@INC; |
|
| 8444 | 9300 |
|
| 8445 |
- my $file = File::Spec->catfile(split( /::/, $module)); |
|
| 8446 |
- foreach my $dir ( @$dirs ) {
|
|
| 8447 |
- my $testfile = File::Spec->catfile($dir, $file); |
|
| 8448 |
- return [ File::Spec->rel2abs( $testfile ), $dir ] |
|
| 8449 |
- if -e $testfile and !-d _; # For stuff like ExtUtils::xsubpp |
|
| 8450 |
- return [ File::Spec->rel2abs( "$testfile.pm" ), $dir ] |
|
| 8451 |
- if -e "$testfile.pm"; |
|
| 8452 |
- } |
|
| 8453 |
- return; |
|
| 8454 |
- } |
|
| 9301 |
+ sub _incr_parse {
|
|
| 9302 |
+ my ( $self, $coder, $text, $skip ) = @_; |
|
| 9303 |
+ my $p = $self->{incr_p};
|
|
| 9304 |
+ my $restore = $p; |
|
| 8455 | 9305 |
|
| 8456 |
- # class method |
|
| 8457 |
- sub find_module_by_name {
|
|
| 8458 |
- my $found = shift()->_do_find_module(@_) or return; |
|
| 8459 |
- return $found->[0]; |
|
| 8460 |
- } |
|
| 9306 |
+ my @obj; |
|
| 9307 |
+ my $len = length $text; |
|
| 8461 | 9308 |
|
| 8462 |
- # class method |
|
| 8463 |
- sub find_module_dir_by_name {
|
|
| 8464 |
- my $found = shift()->_do_find_module(@_) or return; |
|
| 8465 |
- return $found->[1]; |
|
| 8466 |
- } |
|
| 9309 |
+ if ( $self->{incr_mode} == INCR_M_WS ) {
|
|
| 9310 |
+ while ( $len > $p ) {
|
|
| 9311 |
+ my $s = substr( $text, $p, 1 ); |
|
| 9312 |
+ $p++ and next if ( 0x20 >= unpack($unpack_format, $s) ); |
|
| 9313 |
+ $self->{incr_mode} = INCR_M_JSON;
|
|
| 9314 |
+ last; |
|
| 9315 |
+ } |
|
| 9316 |
+ } |
|
| 8467 | 9317 |
|
| 9318 |
+ while ( $len > $p ) {
|
|
| 9319 |
+ my $s = substr( $text, $p++, 1 ); |
|
| 8468 | 9320 |
|
| 8469 |
- # given a line of perl code, attempt to parse it if it looks like a |
|
| 8470 |
- # $VERSION assignment, returning sigil, full name, & package name |
|
| 8471 |
- sub _parse_version_expression {
|
|
| 8472 |
- my $self = shift; |
|
| 8473 |
- my $line = shift; |
|
| 9321 |
+ if ( $s eq '"' ) {
|
|
| 9322 |
+ if (substr( $text, $p - 2, 1 ) eq '\\' ) {
|
|
| 9323 |
+ next; |
|
| 9324 |
+ } |
|
| 8474 | 9325 |
|
| 8475 |
- my( $sig, $var, $pkg ); |
|
| 8476 |
- if ( $line =~ $VERS_REGEXP ) {
|
|
| 8477 |
- ( $sig, $var, $pkg ) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 ); |
|
| 8478 |
- if ( $pkg ) {
|
|
| 8479 |
- $pkg = ($pkg eq '::') ? 'main' : $pkg; |
|
| 8480 |
- $pkg =~ s/::$//; |
|
| 8481 |
- } |
|
| 8482 |
- } |
|
| 9326 |
+ if ( $self->{incr_mode} != INCR_M_STR ) {
|
|
| 9327 |
+ $self->{incr_mode} = INCR_M_STR;
|
|
| 9328 |
+ } |
|
| 9329 |
+ else {
|
|
| 9330 |
+ $self->{incr_mode} = INCR_M_JSON;
|
|
| 9331 |
+ unless ( $self->{incr_nest} ) {
|
|
| 9332 |
+ last; |
|
| 9333 |
+ } |
|
| 9334 |
+ } |
|
| 9335 |
+ } |
|
| 8483 | 9336 |
|
| 8484 |
- return ( $sig, $var, $pkg ); |
|
| 8485 |
- } |
|
| 9337 |
+ if ( $self->{incr_mode} == INCR_M_JSON ) {
|
|
| 8486 | 9338 |
|
| 8487 |
- sub _parse_file {
|
|
| 8488 |
- my $self = shift; |
|
| 9339 |
+ if ( $s eq '[' or $s eq '{' ) {
|
|
| 9340 |
+ if ( ++$self->{incr_nest} > $coder->get_max_depth ) {
|
|
| 9341 |
+ Carp::croak('json text or perl structure exceeds maximum nesting level (max_depth set too low?)');
|
|
| 9342 |
+ } |
|
| 9343 |
+ } |
|
| 9344 |
+ elsif ( $s eq ']' or $s eq '}' ) {
|
|
| 9345 |
+ last if ( --$self->{incr_nest} <= 0 );
|
|
| 9346 |
+ } |
|
| 9347 |
+ elsif ( $s eq '#' ) {
|
|
| 9348 |
+ while ( $len > $p ) {
|
|
| 9349 |
+ last if substr( $text, $p++, 1 ) eq "\n"; |
|
| 9350 |
+ } |
|
| 9351 |
+ } |
|
| 8489 | 9352 |
|
| 8490 |
- my $filename = $self->{filename};
|
|
| 8491 |
- my $fh = IO::File->new( $filename ) |
|
| 8492 |
- or die( "Can't open '$filename': $!" ); |
|
| 9353 |
+ } |
|
| 8493 | 9354 |
|
| 8494 |
- $self->_parse_fh($fh); |
|
| 8495 |
- } |
|
| 9355 |
+ } |
|
| 8496 | 9356 |
|
| 8497 |
- sub _parse_fh {
|
|
| 8498 |
- my ($self, $fh) = @_; |
|
| 9357 |
+ $self->{incr_p} = $p;
|
|
| 8499 | 9358 |
|
| 8500 |
- my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 ); |
|
| 8501 |
- my( @pkgs, %vers, %pod, @pod ); |
|
| 8502 |
- my $pkg = 'main'; |
|
| 8503 |
- my $pod_sect = ''; |
|
| 8504 |
- my $pod_data = ''; |
|
| 9359 |
+ return if ( $self->{incr_mode} == INCR_M_STR and not $self->{incr_nest} );
|
|
| 9360 |
+ return if ( $self->{incr_mode} == INCR_M_JSON and $self->{incr_nest} > 0 );
|
|
| 8505 | 9361 |
|
| 8506 |
- while (defined( my $line = <$fh> )) {
|
|
| 8507 |
- my $line_num = $.; |
|
| 9362 |
+ return '' unless ( length substr( $self->{incr_text}, 0, $p ) );
|
|
| 8508 | 9363 |
|
| 8509 |
- chomp( $line ); |
|
| 8510 |
- next if $line =~ /^\s*#/; |
|
| 9364 |
+ local $Carp::CarpLevel = 2; |
|
| 8511 | 9365 |
|
| 8512 |
- $in_pod = ($line =~ /^=(?!cut)/) ? 1 : ($line =~ /^=cut/) ? 0 : $in_pod; |
|
| 9366 |
+ $self->{incr_p} = $restore;
|
|
| 9367 |
+ $self->{incr_c} = $p;
|
|
| 8513 | 9368 |
|
| 8514 |
- # Would be nice if we could also check $in_string or something too |
|
| 8515 |
- last if !$in_pod && $line =~ /^__(?:DATA|END)__$/; |
|
| 9369 |
+ my ( $obj, $tail ) = $coder->PP_decode_json( substr( $self->{incr_text}, 0, $p ), 0x10000001 );
|
|
| 8516 | 9370 |
|
| 8517 |
- if ( $in_pod || $line =~ /^=cut/ ) {
|
|
| 9371 |
+ $self->{incr_text} = substr( $self->{incr_text}, $p );
|
|
| 9372 |
+ $self->{incr_p} = 0;
|
|
| 8518 | 9373 |
|
| 8519 |
- if ( $line =~ /^=head\d\s+(.+)\s*$/ ) {
|
|
| 8520 |
- push( @pod, $1 ); |
|
| 8521 |
- if ( $self->{collect_pod} && length( $pod_data ) ) {
|
|
| 8522 |
- $pod{$pod_sect} = $pod_data;
|
|
| 8523 |
- $pod_data = ''; |
|
| 8524 |
- } |
|
| 8525 |
- $pod_sect = $1; |
|
| 9374 |
+ return $obj or ''; |
|
| 9375 |
+ } |
|
| 8526 | 9376 |
|
| 8527 | 9377 |
|
| 8528 |
- } elsif ( $self->{collect_pod} ) {
|
|
| 8529 |
- $pod_data .= "$line\n"; |
|
| 9378 |
+ sub incr_text {
|
|
| 9379 |
+ if ( $_[0]->{incr_parsing} ) {
|
|
| 9380 |
+ Carp::croak("incr_text can not be called when the incremental parser already started parsing");
|
|
| 9381 |
+ } |
|
| 9382 |
+ $_[0]->{incr_text};
|
|
| 9383 |
+ } |
|
| 8530 | 9384 |
|
| 8531 |
- } |
|
| 8532 | 9385 |
|
| 8533 |
- } else {
|
|
| 9386 |
+ sub incr_skip {
|
|
| 9387 |
+ my $self = shift; |
|
| 9388 |
+ $self->{incr_text} = substr( $self->{incr_text}, $self->{incr_c} );
|
|
| 9389 |
+ $self->{incr_p} = 0;
|
|
| 9390 |
+ } |
|
| 8534 | 9391 |
|
| 8535 |
- $pod_sect = ''; |
|
| 8536 |
- $pod_data = ''; |
|
| 8537 | 9392 |
|
| 8538 |
- # parse $line to see if it's a $VERSION declaration |
|
| 8539 |
- my( $vers_sig, $vers_fullname, $vers_pkg ) = |
|
| 8540 |
- $self->_parse_version_expression( $line ); |
|
| 9393 |
+ sub incr_reset {
|
|
| 9394 |
+ my $self = shift; |
|
| 9395 |
+ $self->{incr_text} = undef;
|
|
| 9396 |
+ $self->{incr_p} = 0;
|
|
| 9397 |
+ $self->{incr_mode} = 0;
|
|
| 9398 |
+ $self->{incr_nest} = 0;
|
|
| 9399 |
+ $self->{incr_parsing} = 0;
|
|
| 9400 |
+ } |
|
| 8541 | 9401 |
|
| 8542 |
- if ( $line =~ $PKG_REGEXP ) {
|
|
| 8543 |
- $pkg = $1; |
|
| 8544 |
- push( @pkgs, $pkg ) unless grep( $pkg eq $_, @pkgs ); |
|
| 8545 |
- $vers{$pkg} = (defined $2 ? $2 : undef) unless exists( $vers{$pkg} );
|
|
| 8546 |
- $need_vers = defined $2 ? 0 : 1; |
|
| 9402 |
+ ############################### |
|
| 8547 | 9403 |
|
| 8548 |
- # VERSION defined with full package spec, i.e. $Module::VERSION |
|
| 8549 |
- } elsif ( $vers_fullname && $vers_pkg ) {
|
|
| 8550 |
- push( @pkgs, $vers_pkg ) unless grep( $vers_pkg eq $_, @pkgs ); |
|
| 8551 |
- $need_vers = 0 if $vers_pkg eq $pkg; |
|
| 8552 | 9404 |
|
| 8553 |
- unless ( defined $vers{$vers_pkg} && length $vers{$vers_pkg} ) {
|
|
| 8554 |
- $vers{$vers_pkg} =
|
|
| 8555 |
- $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line ); |
|
| 8556 |
- } else {
|
|
| 8557 |
- # Warn unless the user is using the "$VERSION = eval |
|
| 8558 |
- # $VERSION" idiom (though there are probably other idioms |
|
| 8559 |
- # that we should watch out for...) |
|
| 8560 |
- warn <<"EOM" unless $line =~ /=\s*eval/; |
|
| 8561 |
- Package '$vers_pkg' already declared with version '$vers{$vers_pkg}',
|
|
| 8562 |
- ignoring subsequent declaration on line $line_num. |
|
| 8563 |
- EOM |
|
| 8564 |
- } |
|
| 9405 |
+ 1; |
|
| 9406 |
+ __END__ |
|
| 9407 |
+ =pod |
|
| 8565 | 9408 |
|
| 8566 |
- # first non-comment line in undeclared package main is VERSION |
|
| 8567 |
- } elsif ( !exists($vers{main}) && $pkg eq 'main' && $vers_fullname ) {
|
|
| 8568 |
- $need_vers = 0; |
|
| 8569 |
- my $v = |
|
| 8570 |
- $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line ); |
|
| 8571 |
- $vers{$pkg} = $v;
|
|
| 8572 |
- push( @pkgs, 'main' ); |
|
| 9409 |
+JSON_PP |
|
| 9410 |
+ |
|
| 9411 |
+$fatpacked{"JSON/PP/Boolean.pm"} = <<'JSON_PP_BOOLEAN';
|
|
| 9412 |
+ use JSON::PP (); |
|
| 9413 |
+ use strict; |
|
| 8573 | 9414 |
|
| 8574 |
- # first non-comment line in undeclared package defines package main |
|
| 8575 |
- } elsif ( !exists($vers{main}) && $pkg eq 'main' && $line =~ /\w+/ ) {
|
|
| 8576 |
- $need_vers = 1; |
|
| 8577 |
- $vers{main} = '';
|
|
| 8578 |
- push( @pkgs, 'main' ); |
|
| 9415 |
+ 1; |
|
| 8579 | 9416 |
|
| 8580 |
- # only keep if this is the first $VERSION seen |
|
| 8581 |
- } elsif ( $vers_fullname && $need_vers ) {
|
|
| 8582 |
- $need_vers = 0; |
|
| 8583 |
- my $v = |
|
| 8584 |
- $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line ); |
|
| 9417 |
+JSON_PP_BOOLEAN |
|
| 9418 |
+ |
|
| 9419 |
+$fatpacked{"Module/CPANfile.pm"} = <<'MODULE_CPANFILE';
|
|
| 9420 |
+ package Module::CPANfile; |
|
| 9421 |
+ use strict; |
|
| 9422 |
+ use warnings; |
|
| 9423 |
+ use Cwd; |
|
| 8585 | 9424 |
|
| 9425 |
+ our $VERSION = '0.9010'; |
|
| 8586 | 9426 |
|
| 8587 |
- unless ( defined $vers{$pkg} && length $vers{$pkg} ) {
|
|
| 8588 |
- $vers{$pkg} = $v;
|
|
| 8589 |
- } else {
|
|
| 8590 |
- warn <<"EOM"; |
|
| 8591 |
- Package '$pkg' already declared with version '$vers{$pkg}'
|
|
| 8592 |
- ignoring new version '$v' on line $line_num. |
|
| 8593 |
- EOM |
|
| 8594 |
- } |
|
| 9427 |
+ sub new {
|
|
| 9428 |
+ my($class, $file) = @_; |
|
| 9429 |
+ bless {}, $class;
|
|
| 9430 |
+ } |
|
| 8595 | 9431 |
|
| 8596 |
- } |
|
| 9432 |
+ sub load {
|
|
| 9433 |
+ my($proto, $file) = @_; |
|
| 9434 |
+ my $self = ref $proto ? $proto : $proto->new; |
|
| 9435 |
+ $self->{file} = $file || "cpanfile";
|
|
| 9436 |
+ $self->parse; |
|
| 9437 |
+ $self; |
|
| 9438 |
+ } |
|
| 8597 | 9439 |
|
| 8598 |
- } |
|
| 9440 |
+ sub parse {
|
|
| 9441 |
+ my $self = shift; |
|
| 8599 | 9442 |
|
| 8600 |
- } |
|
| 9443 |
+ my $file = Cwd::abs_path($self->{file});
|
|
| 9444 |
+ $self->{result} = Module::CPANfile::Environment::parse($file) or die $@;
|
|
| 9445 |
+ } |
|
| 8601 | 9446 |
|
| 8602 |
- if ( $self->{collect_pod} && length($pod_data) ) {
|
|
| 8603 |
- $pod{$pod_sect} = $pod_data;
|
|
| 8604 |
- } |
|
| 9447 |
+ sub prereqs { shift->prereq }
|
|
| 8605 | 9448 |
|
| 8606 |
- $self->{versions} = \%vers;
|
|
| 8607 |
- $self->{packages} = \@pkgs;
|
|
| 8608 |
- $self->{pod} = \%pod;
|
|
| 8609 |
- $self->{pod_headings} = \@pod;
|
|
| 9449 |
+ sub prereq {
|
|
| 9450 |
+ my $self = shift; |
|
| 9451 |
+ require CPAN::Meta::Prereqs; |
|
| 9452 |
+ CPAN::Meta::Prereqs->new($self->prereq_specs); |
|
| 8610 | 9453 |
} |
| 8611 | 9454 |
|
| 8612 |
- {
|
|
| 8613 |
- my $pn = 0; |
|
| 8614 |
- sub _evaluate_version_line {
|
|
| 8615 |
- my $self = shift; |
|
| 8616 |
- my( $sigil, $var, $line ) = @_; |
|
| 9455 |
+ sub prereq_specs {
|
|
| 9456 |
+ my $self = shift; |
|
| 9457 |
+ $self->{result}{spec};
|
|
| 9458 |
+ } |
|
| 8617 | 9459 |
|
| 8618 |
- # Some of this code came from the ExtUtils:: hierarchy. |
|
| 9460 |
+ sub merge_meta {
|
|
| 9461 |
+ my($self, $file, $version) = @_; |
|
| 8619 | 9462 |
|
| 8620 |
- # We compile into $vsub because 'use version' would cause |
|
| 8621 |
- # compiletime/runtime issues with local() |
|
| 8622 |
- my $vsub; |
|
| 8623 |
- $pn++; # everybody gets their own package |
|
| 8624 |
- my $eval = qq{BEGIN { q# Hide from _packages_inside()
|
|
| 8625 |
- #; package Module::Metadata::_version::p$pn; |
|
| 8626 |
- use version; |
|
| 8627 |
- no strict; |
|
| 9463 |
+ require CPAN::Meta; |
|
| 8628 | 9464 |
|
| 8629 |
- \$vsub = sub {
|
|
| 8630 |
- local $sigil$var; |
|
| 8631 |
- \$$var=undef; |
|
| 8632 |
- $line; |
|
| 8633 |
- \$$var |
|
| 8634 |
- }; |
|
| 8635 |
- }}; |
|
| 9465 |
+ $version ||= $file =~ /\.yml$/ ? '1.4' : '2'; |
|
| 8636 | 9466 |
|
| 8637 |
- local $^W; |
|
| 8638 |
- # Try to get the $VERSION |
|
| 8639 |
- eval $eval; |
|
| 8640 |
- # some modules say $VERSION = $Foo::Bar::VERSION, but Foo::Bar isn't |
|
| 8641 |
- # installed, so we need to hunt in ./lib for it |
|
| 8642 |
- if ( $@ =~ /Can't locate/ && -d 'lib' ) {
|
|
| 8643 |
- local @INC = ('lib',@INC);
|
|
| 8644 |
- eval $eval; |
|
| 8645 |
- } |
|
| 8646 |
- warn "Error evaling version line '$eval' in $self->{filename}: $@\n"
|
|
| 8647 |
- if $@; |
|
| 8648 |
- (ref($vsub) eq 'CODE') or |
|
| 8649 |
- die "failed to build version sub for $self->{filename}";
|
|
| 8650 |
- my $result = eval { $vsub->() };
|
|
| 8651 |
- die "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n"
|
|
| 8652 |
- if $@; |
|
| 9467 |
+ my $prereq = $self->prereqs; |
|
| 8653 | 9468 |
|
| 8654 |
- # Upgrade it into a version object |
|
| 8655 |
- my $version = eval { _dwim_version($result) };
|
|
| 9469 |
+ my $meta = CPAN::Meta->load_file($file); |
|
| 9470 |
+ my $prereqs_hash = $prereq->with_merged_prereqs($meta->effective_prereqs)->as_string_hash; |
|
| 9471 |
+ my $struct = { %{$meta->as_struct}, prereqs => $prereqs_hash };
|
|
| 9472 |
+ |
|
| 9473 |
+ CPAN::Meta->new($struct)->save($file, { version => $version });
|
|
| 9474 |
+ } |
|
| 9475 |
+ |
|
| 9476 |
+ package Module::CPANfile::Environment; |
|
| 9477 |
+ use strict; |
|
| 9478 |
+ |
|
| 9479 |
+ my @bindings = qw( |
|
| 9480 |
+ on requires recommends suggests conflicts |
|
| 9481 |
+ osname perl |
|
| 9482 |
+ configure_requires build_requires test_requires author_requires |
|
| 9483 |
+ ); |
|
| 9484 |
+ |
|
| 9485 |
+ my $file_id = 1; |
|
| 8656 | 9486 |
|
| 8657 |
- die "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n"
|
|
| 8658 |
- unless defined $version; # "0" is OK! |
|
| 9487 |
+ sub import {
|
|
| 9488 |
+ my($class, $result_ref) = @_; |
|
| 9489 |
+ my $pkg = caller; |
|
| 8659 | 9490 |
|
| 8660 |
- return $version; |
|
| 8661 |
- } |
|
| 9491 |
+ $$result_ref = Module::CPANfile::Result->new; |
|
| 9492 |
+ for my $binding (@bindings) {
|
|
| 9493 |
+ no strict 'refs'; |
|
| 9494 |
+ *{"$pkg\::$binding"} = sub { $$result_ref->$binding(@_) };
|
|
| 9495 |
+ } |
|
| 8662 | 9496 |
} |
| 8663 | 9497 |
|
| 8664 |
- # Try to DWIM when things fail the lax version test in obvious ways |
|
| 8665 |
- {
|
|
| 8666 |
- my @version_prep = ( |
|
| 8667 |
- # Best case, it just works |
|
| 8668 |
- sub { return shift },
|
|
| 9498 |
+ sub parse {
|
|
| 9499 |
+ my $file = shift; |
|
| 8669 | 9500 |
|
| 8670 |
- # If we still don't have a version, try stripping any |
|
| 8671 |
- # trailing junk that is prohibited by lax rules |
|
| 8672 |
- sub {
|
|
| 8673 |
- my $v = shift; |
|
| 8674 |
- $v =~ s{([0-9])[a-z-].*$}{$1}i; # 1.23-alpha or 1.23b
|
|
| 8675 |
- return $v; |
|
| 8676 |
- }, |
|
| 9501 |
+ my $code = do {
|
|
| 9502 |
+ open my $fh, "<", $file or die "$file: $!"; |
|
| 9503 |
+ join '', <$fh>; |
|
| 9504 |
+ }; |
|
| 8677 | 9505 |
|
| 8678 |
- # Activestate apparently creates custom versions like '1.23_45_01', which |
|
| 8679 |
- # cause version.pm to think it's an invalid alpha. So check for that |
|
| 8680 |
- # and strip them |
|
| 8681 |
- sub {
|
|
| 8682 |
- my $v = shift; |
|
| 8683 |
- my $num_dots = () = $v =~ m{(\.)}g;
|
|
| 8684 |
- my $num_unders = () = $v =~ m{(_)}g;
|
|
| 8685 |
- my $leading_v = substr($v,0,1) eq 'v'; |
|
| 8686 |
- if ( ! $leading_v && $num_dots < 2 && $num_unders > 1 ) {
|
|
| 8687 |
- $v =~ s{_}{}g;
|
|
| 8688 |
- $num_unders = () = $v =~ m{(_)}g;
|
|
| 8689 |
- } |
|
| 8690 |
- return $v; |
|
| 8691 |
- }, |
|
| 9506 |
+ my($res, $err); |
|
| 8692 | 9507 |
|
| 8693 |
- # Worst case, try numifying it like we would have before version objects |
|
| 8694 |
- sub {
|
|
| 8695 |
- my $v = shift; |
|
| 8696 |
- no warnings 'numeric'; |
|
| 8697 |
- return 0 + $v; |
|
| 8698 |
- }, |
|
| 9508 |
+ {
|
|
| 9509 |
+ local $@; |
|
| 9510 |
+ $res = eval sprintf <<EVAL, $file_id++; |
|
| 9511 |
+ package Module::CPANfile::Sandbox%d; |
|
| 9512 |
+ no warnings; |
|
| 9513 |
+ my \$_result; |
|
| 9514 |
+ BEGIN { import Module::CPANfile::Environment \\\$_result };
|
|
| 8699 | 9515 |
|
| 8700 |
- ); |
|
| 9516 |
+ $code; |
|
| 8701 | 9517 |
|
| 8702 |
- sub _dwim_version {
|
|
| 8703 |
- my ($result) = shift; |
|
| 9518 |
+ \$_result; |
|
| 9519 |
+ EVAL |
|
| 9520 |
+ $err = $@; |
|
| 9521 |
+ } |
|
| 8704 | 9522 |
|
| 8705 |
- return $result if ref($result) eq 'version'; |
|
| 9523 |
+ if ($err) { die "Parsing $file failed: $err" };
|
|
| 8706 | 9524 |
|
| 8707 |
- my ($version, $error); |
|
| 8708 |
- for my $f (@version_prep) {
|
|
| 8709 |
- $result = $f->($result); |
|
| 8710 |
- $version = eval { version->new($result) };
|
|
| 8711 |
- $error ||= $@ if $@; # capture first failure |
|
| 8712 |
- last if defined $version; |
|
| 8713 |
- } |
|
| 9525 |
+ return $res; |
|
| 9526 |
+ } |
|
| 8714 | 9527 |
|
| 8715 |
- die $error unless defined $version; |
|
| 9528 |
+ package Module::CPANfile::Result; |
|
| 9529 |
+ use strict; |
|
| 8716 | 9530 |
|
| 8717 |
- return $version; |
|
| 8718 |
- } |
|
| 9531 |
+ sub new {
|
|
| 9532 |
+ bless {
|
|
| 9533 |
+ phase => 'runtime', # default phase |
|
| 9534 |
+ spec => {},
|
|
| 9535 |
+ }, shift; |
|
| 8719 | 9536 |
} |
| 8720 | 9537 |
|
| 8721 |
- ############################################################ |
|
| 9538 |
+ sub on {
|
|
| 9539 |
+ my($self, $phase, $code) = @_; |
|
| 9540 |
+ local $self->{phase} = $phase;
|
|
| 9541 |
+ $code->() |
|
| 9542 |
+ } |
|
| 8722 | 9543 |
|
| 8723 |
- # accessors |
|
| 8724 |
- sub name { $_[0]->{module} }
|
|
| 9544 |
+ sub osname { die "TODO" }
|
|
| 9545 |
+ sub perl { die "TODO" }
|
|
| 8725 | 9546 |
|
| 8726 |
- sub filename { $_[0]->{filename} }
|
|
| 8727 |
- sub packages_inside { @{$_[0]->{packages}} }
|
|
| 8728 |
- sub pod_inside { @{$_[0]->{pod_headings}} }
|
|
| 8729 |
- sub contains_pod { $#{$_[0]->{pod_headings}} }
|
|
| 9547 |
+ sub requires {
|
|
| 9548 |
+ my($self, $module, $requirement) = @_; |
|
| 9549 |
+ $self->{spec}{$self->{phase}}{requires}{$module} = $requirement || 0;
|
|
| 9550 |
+ } |
|
| 8730 | 9551 |
|
| 8731 |
- sub version {
|
|
| 8732 |
- my $self = shift; |
|
| 8733 |
- my $mod = shift || $self->{module};
|
|
| 8734 |
- my $vers; |
|
| 8735 |
- if ( defined( $mod ) && length( $mod ) && |
|
| 8736 |
- exists( $self->{versions}{$mod} ) ) {
|
|
| 8737 |
- return $self->{versions}{$mod};
|
|
| 8738 |
- } else {
|
|
| 8739 |
- return undef; |
|
| 8740 |
- } |
|
| 9552 |
+ sub recommends {
|
|
| 9553 |
+ my($self, $module, $requirement) = @_; |
|
| 9554 |
+ $self->{spec}->{$self->{phase}}{recommends}{$module} = $requirement || 0;
|
|
| 8741 | 9555 |
} |
| 8742 | 9556 |
|
| 8743 |
- sub pod {
|
|
| 8744 |
- my $self = shift; |
|
| 8745 |
- my $sect = shift; |
|
| 8746 |
- if ( defined( $sect ) && length( $sect ) && |
|
| 8747 |
- exists( $self->{pod}{$sect} ) ) {
|
|
| 8748 |
- return $self->{pod}{$sect};
|
|
| 8749 |
- } else {
|
|
| 8750 |
- return undef; |
|
| 8751 |
- } |
|
| 9557 |
+ sub suggests {
|
|
| 9558 |
+ my($self, $module, $requirement) = @_; |
|
| 9559 |
+ $self->{spec}->{$self->{phase}}{suggests}{$module} = $requirement || 0;
|
|
| 9560 |
+ } |
|
| 9561 |
+ |
|
| 9562 |
+ sub conflicts {
|
|
| 9563 |
+ my($self, $module, $requirement) = @_; |
|
| 9564 |
+ $self->{spec}->{$self->{phase}}{conflicts}{$module} = $requirement || 0;
|
|
| 9565 |
+ } |
|
| 9566 |
+ |
|
| 9567 |
+ # Module::Install compatible shortcuts |
|
| 9568 |
+ |
|
| 9569 |
+ sub configure_requires {
|
|
| 9570 |
+ my($self, @args) = @_; |
|
| 9571 |
+ $self->on(configure => sub { $self->requires(@args) });
|
|
| 9572 |
+ } |
|
| 9573 |
+ |
|
| 9574 |
+ sub build_requires {
|
|
| 9575 |
+ my($self, @args) = @_; |
|
| 9576 |
+ $self->on(build => sub { $self->requires(@args) });
|
|
| 9577 |
+ } |
|
| 9578 |
+ |
|
| 9579 |
+ sub test_requires {
|
|
| 9580 |
+ my($self, @args) = @_; |
|
| 9581 |
+ $self->on(test => sub { $self->requires(@args) });
|
|
| 9582 |
+ } |
|
| 9583 |
+ |
|
| 9584 |
+ sub author_requires {
|
|
| 9585 |
+ my($self, @args) = @_; |
|
| 9586 |
+ $self->on(develop => sub { $self->requires(@args) });
|
|
| 8752 | 9587 |
} |
| 8753 | 9588 |
|
| 9589 |
+ package Module::CPANfile; |
|
| 9590 |
+ |
|
| 8754 | 9591 |
1; |
| 8755 | 9592 |
|
| 8756 |
-MODULE_METADATA |
|
| 8757 |
- |
|
| 8758 |
-$fatpacked{"Parse/CPAN/Meta.pm"} = <<'PARSE_CPAN_META';
|
|
| 8759 |
- package Parse::CPAN::Meta; |
|
| 8760 |
- |
|
| 8761 |
- use strict; |
|
| 8762 |
- use Carp 'croak'; |
|
| 8763 |
- |
|
| 8764 |
- # UTF Support? |
|
| 8765 |
- sub HAVE_UTF8 () { $] >= 5.007003 }
|
|
| 8766 |
- sub IO_LAYER () { $] >= 5.008001 ? ":utf8" : "" }
|
|
| 8767 |
- |
|
| 8768 |
- BEGIN {
|
|
| 8769 |
- if ( HAVE_UTF8 ) {
|
|
| 8770 |
- # The string eval helps hide this from Test::MinimumVersion |
|
| 8771 |
- eval "require utf8;"; |
|
| 8772 |
- die "Failed to load UTF-8 support" if $@; |
|
| 8773 |
- } |
|
| 8774 |
- |
|
| 8775 |
- # Class structure |
|
| 8776 |
- require 5.004; |
|
| 8777 |
- require Exporter; |
|
| 8778 |
- $Parse::CPAN::Meta::VERSION = '1.4401'; |
|
| 8779 |
- @Parse::CPAN::Meta::ISA = qw{ Exporter };
|
|
| 8780 |
- @Parse::CPAN::Meta::EXPORT_OK = qw{ Load LoadFile };
|
|
| 8781 |
- } |
|
| 8782 |
- |
|
| 8783 |
- sub load_file {
|
|
| 8784 |
- my ($class, $filename) = @_; |
|
| 8785 |
- |
|
| 8786 |
- if ($filename =~ /\.ya?ml$/) {
|
|
| 8787 |
- return $class->load_yaml_string(_slurp($filename)); |
|
| 8788 |
- } |
|
| 8789 |
- |
|
| 8790 |
- if ($filename =~ /\.json$/) {
|
|
| 8791 |
- return $class->load_json_string(_slurp($filename)); |
|
| 8792 |
- } |
|
| 8793 |
- |
|
| 8794 |
- croak("file type cannot be determined by filename");
|
|
| 8795 |
- } |
|
| 8796 |
- |
|
| 8797 |
- sub load_yaml_string {
|
|
| 8798 |
- my ($class, $string) = @_; |
|
| 8799 |
- my $backend = $class->yaml_backend(); |
|
| 8800 |
- my $data = eval { no strict 'refs'; &{"$backend\::Load"}($string) };
|
|
| 8801 |
- if ( $@ ) {
|
|
| 8802 |
- croak $backend->can('errstr') ? $backend->errstr : $@
|
|
| 8803 |
- } |
|
| 8804 |
- return $data || {}; # in case document was valid but empty
|
|
| 8805 |
- } |
|
| 8806 |
- |
|
| 8807 |
- sub load_json_string {
|
|
| 8808 |
- my ($class, $string) = @_; |
|
| 8809 |
- return $class->json_backend()->new->decode($string); |
|
| 8810 |
- } |
|
| 8811 |
- |
|
| 8812 |
- sub yaml_backend {
|
|
| 8813 |
- local $Module::Load::Conditional::CHECK_INC_HASH = 1; |
|
| 8814 |
- if (! defined $ENV{PERL_YAML_BACKEND} ) {
|
|
| 8815 |
- _can_load( 'CPAN::Meta::YAML', 0.002 ) |
|
| 8816 |
- or croak "CPAN::Meta::YAML 0.002 is not available\n"; |
|
| 8817 |
- return "CPAN::Meta::YAML"; |
|
| 8818 |
- } |
|
| 8819 |
- else {
|
|
| 8820 |
- my $backend = $ENV{PERL_YAML_BACKEND};
|
|
| 8821 |
- _can_load( $backend ) |
|
| 8822 |
- or croak "Could not load PERL_YAML_BACKEND '$backend'\n"; |
|
| 8823 |
- $backend->can("Load")
|
|
| 8824 |
- or croak "PERL_YAML_BACKEND '$backend' does not implement Load()\n"; |
|
| 8825 |
- return $backend; |
|
| 8826 |
- } |
|
| 8827 |
- } |
|
| 8828 |
- |
|
| 8829 |
- sub json_backend {
|
|
| 8830 |
- local $Module::Load::Conditional::CHECK_INC_HASH = 1; |
|
| 8831 |
- if (! $ENV{PERL_JSON_BACKEND} or $ENV{PERL_JSON_BACKEND} eq 'JSON::PP') {
|
|
| 8832 |
- _can_load( 'JSON::PP' => 2.27103 ) |
|
| 8833 |
- or croak "JSON::PP 2.27103 is not available\n"; |
|
| 8834 |
- return 'JSON::PP'; |
|
| 8835 |
- } |
|
| 8836 |
- else {
|
|
| 8837 |
- _can_load( 'JSON' => 2.5 ) |
|
| 8838 |
- or croak "JSON 2.5 is required for " . |
|
| 8839 |
- "\$ENV{PERL_JSON_BACKEND} = '$ENV{PERL_JSON_BACKEND}'\n";
|
|
| 8840 |
- return "JSON"; |
|
| 8841 |
- } |
|
| 8842 |
- } |
|
| 8843 |
- |
|
| 8844 |
- sub _slurp {
|
|
| 8845 |
- open my $fh, "<" . IO_LAYER, "$_[0]" |
|
| 8846 |
- or die "can't open $_[0] for reading: $!"; |
|
| 8847 |
- return do { local $/; <$fh> };
|
|
| 8848 |
- } |
|
| 8849 |
- |
|
| 8850 |
- sub _can_load {
|
|
| 8851 |
- my ($module, $version) = @_; |
|
| 8852 |
- (my $file = $module) =~ s{::}{/}g;
|
|
| 8853 |
- $file .= ".pm"; |
|
| 8854 |
- return 1 if $INC{$file};
|
|
| 8855 |
- return 0 if exists $INC{$file}; # prior load failed
|
|
| 8856 |
- eval { require $file; 1 }
|
|
| 8857 |
- or return 0; |
|
| 8858 |
- if ( defined $version ) {
|
|
| 8859 |
- eval { $module->VERSION($version); 1 }
|
|
| 8860 |
- or return 0; |
|
| 8861 |
- } |
|
| 8862 |
- return 1; |
|
| 8863 |
- } |
|
| 8864 |
- |
|
| 8865 |
- # Kept for backwards compatibility only |
|
| 8866 |
- # Create an object from a file |
|
| 8867 |
- sub LoadFile ($) {
|
|
| 8868 |
- require CPAN::Meta::YAML; |
|
| 8869 |
- return CPAN::Meta::YAML::LoadFile(shift) |
|
| 8870 |
- or die CPAN::Meta::YAML->errstr; |
|
| 8871 |
- } |
|
| 8872 |
- |
|
| 8873 |
- # Parse a document from a string. |
|
| 8874 |
- sub Load ($) {
|
|
| 8875 |
- require CPAN::Meta::YAML; |
|
| 8876 |
- return CPAN::Meta::YAML::Load(shift) |
|
| 8877 |
- or die CPAN::Meta::YAML->errstr; |
|
| 8878 |
- } |
|
| 8879 |
- |
|
| 8880 |
- 1; |
|
| 8881 |
- |
|
| 8882 |
- __END__ |
|
| 8883 |
- |
|
| 8884 |
-PARSE_CPAN_META |
|
| 9593 |
+ __END__ |
|
| 9594 |
+ |
|
| 9595 |
+ |
|
| 9596 |
+MODULE_CPANFILE |
|
| 8885 | 9597 |
|
| 8886 |
-$fatpacked{"Try/Tiny.pm"} = <<'TRY_TINY';
|
|
| 8887 |
- package Try::Tiny; |
|
| 9598 |
+$fatpacked{"Module/Metadata.pm"} = <<'MODULE_METADATA';
|
|
| 9599 |
+ # -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*- |
|
| 9600 |
+ # vim:ts=8:sw=2:et:sta:sts=2 |
|
| 9601 |
+ package Module::Metadata; |
|
| 8888 | 9602 |
|
| 8889 |
- use strict; |
|
| 8890 |
- #use warnings; |
|
| 9603 |
+ # Adapted from Perl-licensed code originally distributed with |
|
| 9604 |
+ # Module-Build by Ken Williams |
|
| 8891 | 9605 |
|
| 8892 |
- use vars qw(@EXPORT @EXPORT_OK $VERSION @ISA); |
|
| 9606 |
+ # This module provides routines to gather information about |
|
| 9607 |
+ # perl modules (assuming this may be expanded in the distant |
|
| 9608 |
+ # parrot future to look at other types of modules). |
|
| 9609 |
+ |
|
| 9610 |
+ use strict; |
|
| 9611 |
+ use vars qw($VERSION); |
|
| 9612 |
+ $VERSION = '1.000011'; |
|
| 9613 |
+ $VERSION = eval $VERSION; |
|
| 8893 | 9614 |
|
| 9615 |
+ use Carp qw/croak/; |
|
| 9616 |
+ use File::Spec; |
|
| 9617 |
+ use IO::File; |
|
| 9618 |
+ use version 0.87; |
|
| 8894 | 9619 |
BEGIN {
|
| 8895 |
- require Exporter; |
|
| 8896 |
- @ISA = qw(Exporter); |
|
| 9620 |
+ if ($INC{'Log/Contextual.pm'}) {
|
|
| 9621 |
+ Log::Contextual->import('log_info');
|
|
| 9622 |
+ } else {
|
|
| 9623 |
+ *log_info = sub (&) { warn $_[0]->() };
|
|
| 9624 |
+ } |
|
| 8897 | 9625 |
} |
| 9626 |
+ use File::Find qw(find); |
|
| 8898 | 9627 |
|
| 8899 |
- $VERSION = "0.09"; |
|
| 9628 |
+ my $V_NUM_REGEXP = qr{v?[0-9._]+}; # crudely, a v-string or decimal
|
|
| 8900 | 9629 |
|
| 8901 |
- $VERSION = eval $VERSION; |
|
| 9630 |
+ my $PKG_REGEXP = qr{ # match a package declaration
|
|
| 9631 |
+ ^[\s\{;]* # intro chars on a line
|
|
| 9632 |
+ package # the word 'package' |
|
| 9633 |
+ \s+ # whitespace |
|
| 9634 |
+ ([\w:]+) # a package name |
|
| 9635 |
+ \s* # optional whitespace |
|
| 9636 |
+ ($V_NUM_REGEXP)? # optional version number |
|
| 9637 |
+ \s* # optional whitesapce |
|
| 9638 |
+ [;\{] # semicolon line terminator or block start (since 5.16)
|
|
| 9639 |
+ }x; |
|
| 9640 |
+ |
|
| 9641 |
+ my $VARNAME_REGEXP = qr{ # match fully-qualified VERSION name
|
|
| 9642 |
+ ([\$*]) # sigil - $ or * |
|
| 9643 |
+ ( |
|
| 9644 |
+ ( # optional leading package name |
|
| 9645 |
+ (?:::|\')? # possibly starting like just :: (� la $::VERSION) |
|
| 9646 |
+ (?:\w+(?:::|\'))* # Foo::Bar:: ... |
|
| 9647 |
+ )? |
|
| 9648 |
+ VERSION |
|
| 9649 |
+ )\b |
|
| 9650 |
+ }x; |
|
| 9651 |
+ |
|
| 9652 |
+ my $VERS_REGEXP = qr{ # match a VERSION definition
|
|
| 9653 |
+ (?: |
|
| 9654 |
+ \(\s*$VARNAME_REGEXP\s*\) # with parens |
|
| 9655 |
+ | |
|
| 9656 |
+ $VARNAME_REGEXP # without parens |
|
| 9657 |
+ ) |
|
| 9658 |
+ \s* |
|
| 9659 |
+ =[^=~] # = but not ==, nor =~ |
|
| 9660 |
+ }x; |
|
| 9661 |
+ |
|
| 9662 |
+ sub new_from_file {
|
|
| 9663 |
+ my $class = shift; |
|
| 9664 |
+ my $filename = File::Spec->rel2abs( shift ); |
|
| 9665 |
+ |
|
| 9666 |
+ return undef unless defined( $filename ) && -f $filename; |
|
| 9667 |
+ return $class->_init(undef, $filename, @_); |
|
| 9668 |
+ } |
|
| 9669 |
+ |
|
| 9670 |
+ sub new_from_handle {
|
|
| 9671 |
+ my $class = shift; |
|
| 9672 |
+ my $handle = shift; |
|
| 9673 |
+ my $filename = shift; |
|
| 9674 |
+ return undef unless defined($handle) && defined($filename); |
|
| 9675 |
+ $filename = File::Spec->rel2abs( $filename ); |
|
| 9676 |
+ |
|
| 9677 |
+ return $class->_init(undef, $filename, @_, handle => $handle); |
|
| 9678 |
+ |
|
| 9679 |
+ } |
|
| 9680 |
+ |
|
| 9681 |
+ |
|
| 9682 |
+ sub new_from_module {
|
|
| 9683 |
+ my $class = shift; |
|
| 9684 |
+ my $module = shift; |
|
| 9685 |
+ my %props = @_; |
|
| 9686 |
+ |
|
| 9687 |
+ $props{inc} ||= \@INC;
|
|
| 9688 |
+ my $filename = $class->find_module_by_name( $module, $props{inc} );
|
|
| 9689 |
+ return undef unless defined( $filename ) && -f $filename; |
|
| 9690 |
+ return $class->_init($module, $filename, %props); |
|
| 9691 |
+ } |
|
| 8902 | 9692 |
|
| 8903 |
- @EXPORT = @EXPORT_OK = qw(try catch finally); |
|
| 9693 |
+ {
|
|
| 9694 |
+ |
|
| 9695 |
+ my $compare_versions = sub {
|
|
| 9696 |
+ my ($v1, $op, $v2) = @_; |
|
| 9697 |
+ $v1 = version->new($v1) |
|
| 9698 |
+ unless UNIVERSAL::isa($v1,'version'); |
|
| 9699 |
+ |
|
| 9700 |
+ my $eval_str = "\$v1 $op \$v2"; |
|
| 9701 |
+ my $result = eval $eval_str; |
|
| 9702 |
+ log_info { "error comparing versions: '$eval_str' $@" } if $@;
|
|
| 9703 |
+ |
|
| 9704 |
+ return $result; |
|
| 9705 |
+ }; |
|
| 8904 | 9706 |
|
| 8905 |
- $Carp::Internal{+__PACKAGE__}++;
|
|
| 9707 |
+ my $normalize_version = sub {
|
|
| 9708 |
+ my ($version) = @_; |
|
| 9709 |
+ if ( $version =~ /[=<>!,]/ ) { # logic, not just version
|
|
| 9710 |
+ # take as is without modification |
|
| 9711 |
+ } |
|
| 9712 |
+ elsif ( ref $version eq 'version' ) { # version objects
|
|
| 9713 |
+ $version = $version->is_qv ? $version->normal : $version->stringify; |
|
| 9714 |
+ } |
|
| 9715 |
+ elsif ( $version =~ /^[^v][^.]*\.[^.]+\./ ) { # no leading v, multiple dots
|
|
| 9716 |
+ # normalize string tuples without "v": "1.2.3" -> "v1.2.3" |
|
| 9717 |
+ $version = "v$version"; |
|
| 9718 |
+ } |
|
| 9719 |
+ else {
|
|
| 9720 |
+ # leave alone |
|
| 9721 |
+ } |
|
| 9722 |
+ return $version; |
|
| 9723 |
+ }; |
|
| 8906 | 9724 |
|
| 8907 |
- # Need to prototype as @ not $$ because of the way Perl evaluates the prototype. |
|
| 8908 |
- # Keeping it at $$ means you only ever get 1 sub because we need to eval in a list |
|
| 8909 |
- # context & not a scalar one |
|
| 9725 |
+ # separate out some of the conflict resolution logic |
|
| 8910 | 9726 |
|
| 8911 |
- sub try (&;@) {
|
|
| 8912 |
- my ( $try, @code_refs ) = @_; |
|
| 9727 |
+ my $resolve_module_versions = sub {
|
|
| 9728 |
+ my $packages = shift; |
|
| 9729 |
+ |
|
| 9730 |
+ my( $file, $version ); |
|
| 9731 |
+ my $err = ''; |
|
| 9732 |
+ foreach my $p ( @$packages ) {
|
|
| 9733 |
+ if ( defined( $p->{version} ) ) {
|
|
| 9734 |
+ if ( defined( $version ) ) {
|
|
| 9735 |
+ if ( $compare_versions->( $version, '!=', $p->{version} ) ) {
|
|
| 9736 |
+ $err .= " $p->{file} ($p->{version})\n";
|
|
| 9737 |
+ } else {
|
|
| 9738 |
+ # same version declared multiple times, ignore |
|
| 9739 |
+ } |
|
| 9740 |
+ } else {
|
|
| 9741 |
+ $file = $p->{file};
|
|
| 9742 |
+ $version = $p->{version};
|
|
| 9743 |
+ } |
|
| 9744 |
+ } |
|
| 9745 |
+ $file ||= $p->{file} if defined( $p->{file} );
|
|
| 9746 |
+ } |
|
| 9747 |
+ |
|
| 9748 |
+ if ( $err ) {
|
|
| 9749 |
+ $err = " $file ($version)\n" . $err; |
|
| 9750 |
+ } |
|
| 9751 |
+ |
|
| 9752 |
+ my %result = ( |
|
| 9753 |
+ file => $file, |
|
| 9754 |
+ version => $version, |
|
| 9755 |
+ err => $err |
|
| 9756 |
+ ); |
|
| 9757 |
+ |
|
| 9758 |
+ return \%result; |
|
| 9759 |
+ }; |
|
| 8913 | 9760 |
|
| 8914 |
- # we need to save this here, the eval block will be in scalar context due |
|
| 8915 |
- # to $failed |
|
| 8916 |
- my $wantarray = wantarray; |
|
| 9761 |
+ sub provides {
|
|
| 9762 |
+ my $class = shift; |
|
| 8917 | 9763 |
|
| 8918 |
- my ( $catch, @finally ); |
|
| 9764 |
+ croak "provides() requires key/value pairs \n" if @_ % 2; |
|
| 9765 |
+ my %args = @_; |
|
| 8919 | 9766 |
|
| 8920 |
- # find labeled blocks in the argument list. |
|
| 8921 |
- # catch and finally tag the blocks by blessing a scalar reference to them. |
|
| 8922 |
- foreach my $code_ref (@code_refs) {
|
|
| 8923 |
- next unless $code_ref; |
|
| 9767 |
+ croak "provides() takes only one of 'dir' or 'files'\n" |
|
| 9768 |
+ if $args{dir} && $args{files};
|
|
| 8924 | 9769 |
|
| 8925 |
- my $ref = ref($code_ref); |
|
| 9770 |
+ croak "provides() requires a 'version' argument" |
|
| 9771 |
+ unless defined $args{version};
|
|
| 8926 | 9772 |
|
| 8927 |
- if ( $ref eq 'Try::Tiny::Catch' ) {
|
|
| 8928 |
- $catch = ${$code_ref};
|
|
| 8929 |
- } elsif ( $ref eq 'Try::Tiny::Finally' ) {
|
|
| 8930 |
- push @finally, ${$code_ref};
|
|
| 8931 |
- } else {
|
|
| 8932 |
- use Carp; |
|
| 8933 |
- confess("Unknown code ref type given '${ref}'. Check your usage & try again");
|
|
| 8934 |
- } |
|
| 8935 |
- } |
|
| 9773 |
+ croak "provides() does not support version '$args{version}' metadata"
|
|
| 9774 |
+ unless grep { $args{version} eq $_ } qw/1.4 2/;
|
|
| 8936 | 9775 |
|
| 8937 |
- # save the value of $@ so we can set $@ back to it in the beginning of the eval |
|
| 8938 |
- my $prev_error = $@; |
|
| 9776 |
+ $args{prefix} = 'lib' unless defined $args{prefix};
|
|
| 8939 | 9777 |
|
| 8940 |
- my ( @ret, $error, $failed ); |
|
| 9778 |
+ my $p; |
|
| 9779 |
+ if ( $args{dir} ) {
|
|
| 9780 |
+ $p = $class->package_versions_from_directory($args{dir});
|
|
| 9781 |
+ } |
|
| 9782 |
+ else {
|
|
| 9783 |
+ croak "provides() requires 'files' to be an array reference\n" |
|
| 9784 |
+ unless ref $args{files} eq 'ARRAY';
|
|
| 9785 |
+ $p = $class->package_versions_from_directory($args{files});
|
|
| 9786 |
+ } |
|
| 8941 | 9787 |
|
| 8942 |
- # FIXME consider using local $SIG{__DIE__} to accumulate all errors. It's
|
|
| 8943 |
- # not perfect, but we could provide a list of additional errors for |
|
| 8944 |
- # $catch->(); |
|
| 9788 |
+ # Now, fix up files with prefix |
|
| 9789 |
+ if ( length $args{prefix} ) { # check in case disabled with q{}
|
|
| 9790 |
+ $args{prefix} =~ s{/$}{};
|
|
| 9791 |
+ for my $v ( values %$p ) {
|
|
| 9792 |
+ $v->{file} = "$args{prefix}/$v->{file}";
|
|
| 9793 |
+ } |
|
| 9794 |
+ } |
|
| 8945 | 9795 |
|
| 8946 |
- {
|
|
| 8947 |
- # localize $@ to prevent clobbering of previous value by a successful |
|
| 8948 |
- # eval. |
|
| 8949 |
- local $@; |
|
| 8950 |
- |
|
| 8951 |
- # failed will be true if the eval dies, because 1 will not be returned |
|
| 8952 |
- # from the eval body |
|
| 8953 |
- $failed = not eval {
|
|
| 8954 |
- $@ = $prev_error; |
|
| 8955 |
- |
|
| 8956 |
- # evaluate the try block in the correct context |
|
| 8957 |
- if ( $wantarray ) {
|
|
| 8958 |
- @ret = $try->(); |
|
| 8959 |
- } elsif ( defined $wantarray ) {
|
|
| 8960 |
- $ret[0] = $try->(); |
|
| 8961 |
- } else {
|
|
| 8962 |
- $try->(); |
|
| 8963 |
- }; |
|
| 9796 |
+ return $p |
|
| 9797 |
+ } |
|
| 8964 | 9798 |
|
| 8965 |
- return 1; # properly set $fail to false |
|
| 8966 |
- }; |
|
| 9799 |
+ sub package_versions_from_directory {
|
|
| 9800 |
+ my ( $class, $dir, $files ) = @_; |
|
| 8967 | 9801 |
|
| 8968 |
- # copy $@ to $error; when we leave this scope, local $@ will revert $@ |
|
| 8969 |
- # back to its previous value |
|
| 8970 |
- $error = $@; |
|
| 8971 |
- } |
|
| 9802 |
+ my @files; |
|
| 8972 | 9803 |
|
| 8973 |
- # set up a scope guard to invoke the finally block at the end |
|
| 8974 |
- my @guards = |
|
| 8975 |
- map { Try::Tiny::ScopeGuard->_new($_, $failed ? $error : ()) }
|
|
| 8976 |
- @finally; |
|
| 8977 |
- |
|
| 8978 |
- # at this point $failed contains a true value if the eval died, even if some |
|
| 8979 |
- # destructor overwrote $@ as the eval was unwinding. |
|
| 8980 |
- if ( $failed ) {
|
|
| 8981 |
- # if we got an error, invoke the catch block. |
|
| 8982 |
- if ( $catch ) {
|
|
| 8983 |
- # This works like given($error), but is backwards compatible and |
|
| 8984 |
- # sets $_ in the dynamic scope for the body of C<$catch> |
|
| 8985 |
- for ($error) {
|
|
| 8986 |
- return $catch->($error); |
|
| 8987 |
- } |
|
| 9804 |
+ if ( $files ) {
|
|
| 9805 |
+ @files = @$files; |
|
| 9806 |
+ } else {
|
|
| 9807 |
+ find( {
|
|
| 9808 |
+ wanted => sub {
|
|
| 9809 |
+ push @files, $_ if -f $_ && /\.pm$/; |
|
| 9810 |
+ }, |
|
| 9811 |
+ no_chdir => 1, |
|
| 9812 |
+ }, $dir ); |
|
| 9813 |
+ } |
|
| 8988 | 9814 |
|
| 8989 |
- # in case when() was used without an explicit return, the C<for> |
|
| 8990 |
- # loop will be aborted and there's no useful return value |
|
| 8991 |
- } |
|
| 9815 |
+ # First, we enumerate all packages & versions, |
|
| 9816 |
+ # separating into primary & alternative candidates |
|
| 9817 |
+ my( %prime, %alt ); |
|
| 9818 |
+ foreach my $file (@files) {
|
|
| 9819 |
+ my $mapped_filename = File::Spec::Unix->abs2rel( $file, $dir ); |
|
| 9820 |
+ my @path = split( /\//, $mapped_filename ); |
|
| 9821 |
+ (my $prime_package = join( '::', @path )) =~ s/\.pm$//; |
|
| 9822 |
+ |
|
| 9823 |
+ my $pm_info = $class->new_from_file( $file ); |
|
| 9824 |
+ |
|
| 9825 |
+ foreach my $package ( $pm_info->packages_inside ) {
|
|
| 9826 |
+ next if $package eq 'main'; # main can appear numerous times, ignore |
|
| 9827 |
+ next if $package eq 'DB'; # special debugging package, ignore |
|
| 9828 |
+ next if grep /^_/, split( /::/, $package ); # private package, ignore |
|
| 9829 |
+ |
|
| 9830 |
+ my $version = $pm_info->version( $package ); |
|
| 9831 |
+ |
|
| 9832 |
+ $prime_package = $package if lc($prime_package) eq lc($package); |
|
| 9833 |
+ if ( $package eq $prime_package ) {
|
|
| 9834 |
+ if ( exists( $prime{$package} ) ) {
|
|
| 9835 |
+ croak "Unexpected conflict in '$package'; multiple versions found.\n"; |
|
| 9836 |
+ } else {
|
|
| 9837 |
+ $mapped_filename = "$package.pm" if lc("$package.pm") eq lc($mapped_filename);
|
|
| 9838 |
+ $prime{$package}{file} = $mapped_filename;
|
|
| 9839 |
+ $prime{$package}{version} = $version if defined( $version );
|
|
| 9840 |
+ } |
|
| 9841 |
+ } else {
|
|
| 9842 |
+ push( @{$alt{$package}}, {
|
|
| 9843 |
+ file => $mapped_filename, |
|
| 9844 |
+ version => $version, |
|
| 9845 |
+ } ); |
|
| 9846 |
+ } |
|
| 9847 |
+ } |
|
| 9848 |
+ } |
|
| 9849 |
+ |
|
| 9850 |
+ # Then we iterate over all the packages found above, identifying conflicts |
|
| 9851 |
+ # and selecting the "best" candidate for recording the file & version |
|
| 9852 |
+ # for each package. |
|
| 9853 |
+ foreach my $package ( keys( %alt ) ) {
|
|
| 9854 |
+ my $result = $resolve_module_versions->( $alt{$package} );
|
|
| 9855 |
+ |
|
| 9856 |
+ if ( exists( $prime{$package} ) ) { # primary package selected
|
|
| 9857 |
+ |
|
| 9858 |
+ if ( $result->{err} ) {
|
|
| 9859 |
+ # Use the selected primary package, but there are conflicting |
|
| 9860 |
+ # errors among multiple alternative packages that need to be |
|
| 9861 |
+ # reported |
|
| 9862 |
+ log_info {
|
|
| 9863 |
+ "Found conflicting versions for package '$package'\n" . |
|
| 9864 |
+ " $prime{$package}{file} ($prime{$package}{version})\n" .
|
|
| 9865 |
+ $result->{err}
|
|
| 9866 |
+ }; |
|
| 9867 |
+ |
|
| 9868 |
+ } elsif ( defined( $result->{version} ) ) {
|
|
| 9869 |
+ # There is a primary package selected, and exactly one |
|
| 9870 |
+ # alternative package |
|
| 9871 |
+ |
|
| 9872 |
+ if ( exists( $prime{$package}{version} ) &&
|
|
| 9873 |
+ defined( $prime{$package}{version} ) ) {
|
|
| 9874 |
+ # Unless the version of the primary package agrees with the |
|
| 9875 |
+ # version of the alternative package, report a conflict |
|
| 9876 |
+ if ( $compare_versions->( |
|
| 9877 |
+ $prime{$package}{version}, '!=', $result->{version}
|
|
| 9878 |
+ ) |
|
| 9879 |
+ ) {
|
|
| 8992 | 9880 |
|
| 8993 |
- return; |
|
| 8994 |
- } else {
|
|
| 8995 |
- # no failure, $@ is back to what it was, everything is fine |
|
| 8996 |
- return $wantarray ? @ret : $ret[0]; |
|
| 8997 |
- } |
|
| 8998 |
- } |
|
| 9881 |
+ log_info {
|
|
| 9882 |
+ "Found conflicting versions for package '$package'\n" . |
|
| 9883 |
+ " $prime{$package}{file} ($prime{$package}{version})\n" .
|
|
| 9884 |
+ " $result->{file} ($result->{version})\n"
|
|
| 9885 |
+ }; |
|
| 9886 |
+ } |
|
| 9887 |
+ |
|
| 9888 |
+ } else {
|
|
| 9889 |
+ # The prime package selected has no version so, we choose to |
|
| 9890 |
+ # use any alternative package that does have a version |
|
| 9891 |
+ $prime{$package}{file} = $result->{file};
|
|
| 9892 |
+ $prime{$package}{version} = $result->{version};
|
|
| 9893 |
+ } |
|
| 9894 |
+ |
|
| 9895 |
+ } else {
|
|
| 9896 |
+ # no alt package found with a version, but we have a prime |
|
| 9897 |
+ # package so we use it whether it has a version or not |
|
| 9898 |
+ } |
|
| 9899 |
+ |
|
| 9900 |
+ } else { # No primary package was selected, use the best alternative
|
|
| 9901 |
+ |
|
| 9902 |
+ if ( $result->{err} ) {
|
|
| 9903 |
+ log_info {
|
|
| 9904 |
+ "Found conflicting versions for package '$package'\n" . |
|
| 9905 |
+ $result->{err}
|
|
| 9906 |
+ }; |
|
| 9907 |
+ } |
|
| 9908 |
+ |
|
| 9909 |
+ # Despite possible conflicting versions, we choose to record |
|
| 9910 |
+ # something rather than nothing |
|
| 9911 |
+ $prime{$package}{file} = $result->{file};
|
|
| 9912 |
+ $prime{$package}{version} = $result->{version}
|
|
| 9913 |
+ if defined( $result->{version} );
|
|
| 9914 |
+ } |
|
| 9915 |
+ } |
|
| 9916 |
+ |
|
| 9917 |
+ # Normalize versions. Can't use exists() here because of bug in YAML::Node. |
|
| 9918 |
+ # XXX "bug in YAML::Node" comment seems irrelvant -- dagolden, 2009-05-18 |
|
| 9919 |
+ for (grep defined $_->{version}, values %prime) {
|
|
| 9920 |
+ $_->{version} = $normalize_version->( $_->{version} );
|
|
| 9921 |
+ } |
|
| 9922 |
+ |
|
| 9923 |
+ return \%prime; |
|
| 9924 |
+ } |
|
| 9925 |
+ } |
|
| 9926 |
+ |
|
| 8999 | 9927 |
|
| 9000 |
- sub catch (&;@) {
|
|
| 9001 |
- my ( $block, @rest ) = @_; |
|
| 9928 |
+ sub _init {
|
|
| 9929 |
+ my $class = shift; |
|
| 9930 |
+ my $module = shift; |
|
| 9931 |
+ my $filename = shift; |
|
| 9932 |
+ my %props = @_; |
|
| 9002 | 9933 |
|
| 9003 |
- return ( |
|
| 9004 |
- bless(\$block, 'Try::Tiny::Catch'), |
|
| 9005 |
- @rest, |
|
| 9006 |
- ); |
|
| 9007 |
- } |
|
| 9934 |
+ my $handle = delete $props{handle};
|
|
| 9935 |
+ my( %valid_props, @valid_props ); |
|
| 9936 |
+ @valid_props = qw( collect_pod inc ); |
|
| 9937 |
+ @valid_props{@valid_props} = delete( @props{@valid_props} );
|
|
| 9938 |
+ warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
|
|
| 9008 | 9939 |
|
| 9009 |
- sub finally (&;@) {
|
|
| 9010 |
- my ( $block, @rest ) = @_; |
|
| 9940 |
+ my %data = ( |
|
| 9941 |
+ module => $module, |
|
| 9942 |
+ filename => $filename, |
|
| 9943 |
+ version => undef, |
|
| 9944 |
+ packages => [], |
|
| 9945 |
+ versions => {},
|
|
| 9946 |
+ pod => {},
|
|
| 9947 |
+ pod_headings => [], |
|
| 9948 |
+ collect_pod => 0, |
|
| 9011 | 9949 |
|
| 9012 |
- return ( |
|
| 9013 |
- bless(\$block, 'Try::Tiny::Finally'), |
|
| 9014 |
- @rest, |
|
| 9015 |
- ); |
|
| 9016 |
- } |
|
| 9950 |
+ %valid_props, |
|
| 9951 |
+ ); |
|
| 9017 | 9952 |
|
| 9018 |
- {
|
|
| 9019 |
- package # hide from PAUSE |
|
| 9020 |
- Try::Tiny::ScopeGuard; |
|
| 9953 |
+ my $self = bless(\%data, $class); |
|
| 9021 | 9954 |
|
| 9022 |
- sub _new {
|
|
| 9023 |
- shift; |
|
| 9024 |
- bless [ @_ ]; |
|
| 9955 |
+ if ( $handle ) {
|
|
| 9956 |
+ $self->_parse_fh($handle); |
|
| 9025 | 9957 |
} |
| 9026 |
- |
|
| 9027 |
- sub DESTROY {
|
|
| 9028 |
- my @guts = @{ shift() };
|
|
| 9029 |
- my $code = shift @guts; |
|
| 9030 |
- $code->(@guts); |
|
| 9958 |
+ else {
|
|
| 9959 |
+ $self->_parse_file(); |
|
| 9031 | 9960 |
} |
| 9032 |
- } |
|
| 9033 |
- |
|
| 9034 |
- __PACKAGE__ |
|
| 9035 |
- |
|
| 9036 |
- __END__ |
|
| 9037 |
- |
|
| 9038 |
-TRY_TINY |
|
| 9039 |
- |
|
| 9040 |
-$fatpacked{"lib/core/only.pm"} = <<'LIB_CORE_ONLY';
|
|
| 9041 |
- package lib::core::only; |
|
| 9042 |
- |
|
| 9043 |
- use strict; |
|
| 9044 |
- use warnings FATAL => 'all'; |
|
| 9045 |
- use Config; |
|
| 9046 |
- |
|
| 9047 |
- sub import {
|
|
| 9048 |
- @INC = @Config{qw(privlibexp archlibexp)};
|
|
| 9049 |
- return |
|
| 9050 |
- } |
|
| 9051 |
- |
|
| 9052 |
- 1; |
|
| 9053 |
-LIB_CORE_ONLY |
|
| 9054 |
- |
|
| 9055 |
-$fatpacked{"local/lib.pm"} = <<'LOCAL_LIB';
|
|
| 9056 |
- use strict; |
|
| 9057 |
- use warnings; |
|
| 9058 |
- |
|
| 9059 |
- package local::lib; |
|
| 9060 |
- |
|
| 9061 |
- use 5.008001; # probably works with earlier versions but I'm not supporting them |
|
| 9062 |
- # (patches would, of course, be welcome) |
|
| 9063 |
- |
|
| 9064 |
- use File::Spec (); |
|
| 9065 |
- use File::Path (); |
|
| 9066 |
- use Carp (); |
|
| 9067 |
- use Config; |
|
| 9068 |
- |
|
| 9069 |
- our $VERSION = '1.008001'; # 1.8.1 |
|
| 9070 |
- |
|
| 9071 |
- our @KNOWN_FLAGS = qw(--self-contained); |
|
| 9072 |
- |
|
| 9073 |
- sub import {
|
|
| 9074 |
- my ($class, @args) = @_; |
|
| 9075 |
- |
|
| 9076 |
- # Remember what PERL5LIB was when we started |
|
| 9077 |
- my $perl5lib = $ENV{PERL5LIB} || '';
|
|
| 9078 | 9961 |
|
| 9079 |
- my %arg_store; |
|
| 9080 |
- for my $arg (@args) {
|
|
| 9081 |
- # check for lethal dash first to stop processing before causing problems |
|
| 9082 |
- if ($arg =~ /−/) {
|
|
| 9083 |
- die <<'DEATH'; |
|
| 9084 |
- WHOA THERE! It looks like you've got some fancy dashes in your commandline! |
|
| 9085 |
- These are *not* the traditional -- dashes that software recognizes. You |
|
| 9086 |
- probably got these by copy-pasting from the perldoc for this module as |
|
| 9087 |
- rendered by a UTF8-capable formatter. This most typically happens on an OS X |
|
| 9088 |
- terminal, but can happen elsewhere too. Please try again after replacing the |
|
| 9089 |
- dashes with normal minus signs. |
|
| 9090 |
- DEATH |
|
| 9091 |
- } |
|
| 9092 |
- elsif(grep { $arg eq $_ } @KNOWN_FLAGS) {
|
|
| 9093 |
- (my $flag = $arg) =~ s/--//; |
|
| 9094 |
- $arg_store{$flag} = 1;
|
|
| 9095 |
- } |
|
| 9096 |
- elsif($arg =~ /^--/) {
|
|
| 9097 |
- die "Unknown import argument: $arg"; |
|
| 9962 |
+ unless($self->{module} and length($self->{module})) {
|
|
| 9963 |
+ my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
|
|
| 9964 |
+ if($f =~ /\.pm$/) {
|
|
| 9965 |
+ $f =~ s/\..+$//; |
|
| 9966 |
+ my @candidates = grep /$f$/, @{$self->{packages}};
|
|
| 9967 |
+ $self->{module} = shift(@candidates); # punt
|
|
| 9098 | 9968 |
} |
| 9099 | 9969 |
else {
|
| 9100 |
- # assume that what's left is a path |
|
| 9101 |
- $arg_store{path} = $arg;
|
|
| 9970 |
+ if(grep /main/, @{$self->{packages}}) {
|
|
| 9971 |
+ $self->{module} = 'main';
|
|
| 9972 |
+ } |
|
| 9973 |
+ else {
|
|
| 9974 |
+ $self->{module} = $self->{packages}[0] || '';
|
|
| 9975 |
+ } |
|
| 9102 | 9976 |
} |
| 9103 | 9977 |
} |
| 9104 | 9978 |
|
| 9105 |
- if($arg_store{'self-contained'}) {
|
|
| 9106 |
- die "FATAL: The local::lib --self-contained flag has never worked reliably and the original author, Mark Stosberg, was unable or unwilling to maintain it. As such, this flag has been removed from the local::lib codebase in order to prevent misunderstandings and potentially broken builds. The local::lib authors recommend that you look at the lib::core::only module shipped with this distribution in order to create a more robust environment that is equivalent to what --self-contained provided (although quite possibly not what you originally thought it provided due to the poor quality of the documentation, for which we apologise).\n"; |
|
| 9107 |
- } |
|
| 9108 |
- |
|
| 9109 |
- $arg_store{path} = $class->resolve_path($arg_store{path});
|
|
| 9110 |
- $class->setup_local_lib_for($arg_store{path});
|
|
| 9111 |
- |
|
| 9112 |
- for (@INC) { # Untaint @INC
|
|
| 9113 |
- next if ref; # Skip entry if it is an ARRAY, CODE, blessed, etc. |
|
| 9114 |
- m/(.*)/ and $_ = $1; |
|
| 9115 |
- } |
|
| 9979 |
+ $self->{version} = $self->{versions}{$self->{module}}
|
|
| 9980 |
+ if defined( $self->{module} );
|
|
| 9981 |
+ |
|
| 9982 |
+ return $self; |
|
| 9116 | 9983 |
} |
| 9117 | 9984 |
|
| 9118 |
- sub pipeline; |
|
| 9985 |
+ # class method |
|
| 9986 |
+ sub _do_find_module {
|
|
| 9987 |
+ my $class = shift; |
|
| 9988 |
+ my $module = shift || croak 'find_module_by_name() requires a package name'; |
|
| 9989 |
+ my $dirs = shift || \@INC; |
|
| 9119 | 9990 |
|
| 9120 |
- sub pipeline {
|
|
| 9121 |
- my @methods = @_; |
|
| 9122 |
- my $last = pop(@methods); |
|
| 9123 |
- if (@methods) {
|
|
| 9124 |
- \sub {
|
|
| 9125 |
- my ($obj, @args) = @_; |
|
| 9126 |
- $obj->${pipeline @methods}(
|
|
| 9127 |
- $obj->$last(@args) |
|
| 9128 |
- ); |
|
| 9129 |
- }; |
|
| 9130 |
- } else {
|
|
| 9131 |
- \sub {
|
|
| 9132 |
- shift->$last(@_); |
|
| 9133 |
- }; |
|
| 9991 |
+ my $file = File::Spec->catfile(split( /::/, $module)); |
|
| 9992 |
+ foreach my $dir ( @$dirs ) {
|
|
| 9993 |
+ my $testfile = File::Spec->catfile($dir, $file); |
|
| 9994 |
+ return [ File::Spec->rel2abs( $testfile ), $dir ] |
|
| 9995 |
+ if -e $testfile and !-d _; # For stuff like ExtUtils::xsubpp |
|
| 9996 |
+ return [ File::Spec->rel2abs( "$testfile.pm" ), $dir ] |
|
| 9997 |
+ if -e "$testfile.pm"; |
|
| 9134 | 9998 |
} |
| 9999 |
+ return; |
|
| 9135 | 10000 |
} |
| 9136 | 10001 |
|
| 9137 |
- sub _uniq {
|
|
| 9138 |
- my %seen; |
|
| 9139 |
- grep { ! $seen{$_}++ } @_;
|
|
| 10002 |
+ # class method |
|
| 10003 |
+ sub find_module_by_name {
|
|
| 10004 |
+ my $found = shift()->_do_find_module(@_) or return; |
|
| 10005 |
+ return $found->[0]; |
|
| 9140 | 10006 |
} |
| 9141 | 10007 |
|
| 9142 |
- sub resolve_path {
|
|
| 9143 |
- my ($class, $path) = @_; |
|
| 9144 |
- $class->${pipeline qw(
|
|
| 9145 |
- resolve_relative_path |
|
| 9146 |
- resolve_home_path |
|
| 9147 |
- resolve_empty_path |
|
| 9148 |
- )}($path); |
|
| 10008 |
+ # class method |
|
| 10009 |
+ sub find_module_dir_by_name {
|
|
| 10010 |
+ my $found = shift()->_do_find_module(@_) or return; |
|
| 10011 |
+ return $found->[1]; |
|
| 9149 | 10012 |
} |
| 9150 | 10013 |
|
| 9151 |
- sub resolve_empty_path {
|
|
| 9152 |
- my ($class, $path) = @_; |
|
| 9153 |
- if (defined $path) {
|
|
| 9154 |
- $path; |
|
| 9155 |
- } else {
|
|
| 9156 |
- '~/perl5'; |
|
| 9157 |
- } |
|
| 9158 |
- } |
|
| 9159 | 10014 |
|
| 9160 |
- sub resolve_home_path {
|
|
| 9161 |
- my ($class, $path) = @_; |
|
| 9162 |
- return $path unless ($path =~ /^~/); |
|
| 9163 |
- my ($user) = ($path =~ /^~([^\/]+)/); # can assume ^~ so undef for 'us' |
|
| 9164 |
- my $tried_file_homedir; |
|
| 9165 |
- my $homedir = do {
|
|
| 9166 |
- if (eval { require File::HomeDir } && $File::HomeDir::VERSION >= 0.65) {
|
|
| 9167 |
- $tried_file_homedir = 1; |
|
| 9168 |
- if (defined $user) {
|
|
| 9169 |
- File::HomeDir->users_home($user); |
|
| 9170 |
- } else {
|
|
| 9171 |
- File::HomeDir->my_home; |
|
| 9172 |
- } |
|
| 9173 |
- } else {
|
|
| 9174 |
- if (defined $user) {
|
|
| 9175 |
- (getpwnam $user)[7]; |
|
| 9176 |
- } else {
|
|
| 9177 |
- if (defined $ENV{HOME}) {
|
|
| 9178 |
- $ENV{HOME};
|
|
| 9179 |
- } else {
|
|
| 9180 |
- (getpwuid $<)[7]; |
|
| 9181 |
- } |
|
| 9182 |
- } |
|
| 10015 |
+ # given a line of perl code, attempt to parse it if it looks like a |
|
| 10016 |
+ # $VERSION assignment, returning sigil, full name, & package name |
|
| 10017 |
+ sub _parse_version_expression {
|
|
| 10018 |
+ my $self = shift; |
|
| 10019 |
+ my $line = shift; |
|
| 10020 |
+ |
|
| 10021 |
+ my( $sig, $var, $pkg ); |
|
| 10022 |
+ if ( $line =~ /$VERS_REGEXP/o ) {
|
|
| 10023 |
+ ( $sig, $var, $pkg ) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 ); |
|
| 10024 |
+ if ( $pkg ) {
|
|
| 10025 |
+ $pkg = ($pkg eq '::') ? 'main' : $pkg; |
|
| 10026 |
+ $pkg =~ s/::$//; |
|
| 9183 | 10027 |
} |
| 9184 |
- }; |
|
| 9185 |
- unless (defined $homedir) {
|
|
| 9186 |
- Carp::croak( |
|
| 9187 |
- "Couldn't resolve homedir for " |
|
| 9188 |
- .(defined $user ? $user : 'current user') |
|
| 9189 |
- .($tried_file_homedir ? '' : ' - consider installing File::HomeDir') |
|
| 9190 |
- ); |
|
| 9191 | 10028 |
} |
| 9192 |
- $path =~ s/^~[^\/]*/$homedir/; |
|
| 9193 |
- $path; |
|
| 10029 |
+ |
|
| 10030 |
+ return ( $sig, $var, $pkg ); |
|
| 9194 | 10031 |
} |
| 9195 | 10032 |
|
| 9196 |
- sub resolve_relative_path {
|
|
| 9197 |
- my ($class, $path) = @_; |
|
| 9198 |
- $path = File::Spec->rel2abs($path); |
|
| 10033 |
+ sub _parse_file {
|
|
| 10034 |
+ my $self = shift; |
|
| 10035 |
+ |
|
| 10036 |
+ my $filename = $self->{filename};
|
|
| 10037 |
+ my $fh = IO::File->new( $filename ) |
|
| 10038 |
+ or croak( "Can't open '$filename': $!" ); |
|
| 10039 |
+ |
|
| 10040 |
+ $self->_handle_bom($fh, $filename); |
|
| 10041 |
+ |
|
| 10042 |
+ $self->_parse_fh($fh); |
|
| 9199 | 10043 |
} |
| 9200 | 10044 |
|
| 9201 |
- sub setup_local_lib_for {
|
|
| 9202 |
- my ($class, $path) = @_; |
|
| 9203 |
- $path = $class->ensure_dir_structure_for($path); |
|
| 9204 |
- if ($0 eq '-') {
|
|
| 9205 |
- $class->print_environment_vars_for($path); |
|
| 9206 |
- exit 0; |
|
| 10045 |
+ # Look for a UTF-8/UTF-16BE/UTF-16LE BOM at the beginning of the stream. |
|
| 10046 |
+ # If there's one, then skip it and set the :encoding layer appropriately. |
|
| 10047 |
+ sub _handle_bom {
|
|
| 10048 |
+ my ($self, $fh, $filename) = @_; |
|
| 10049 |
+ |
|
| 10050 |
+ my $pos = $fh->getpos; |
|
| 10051 |
+ return unless defined $pos; |
|
| 10052 |
+ |
|
| 10053 |
+ my $buf = ' ' x 2; |
|
| 10054 |
+ my $count = $fh->read( $buf, length $buf ); |
|
| 10055 |
+ return unless defined $count and $count >= 2; |
|
| 10056 |
+ |
|
| 10057 |
+ my $encoding; |
|
| 10058 |
+ if ( $buf eq "\x{FE}\x{FF}" ) {
|
|
| 10059 |
+ $encoding = 'UTF-16BE'; |
|
| 10060 |
+ } elsif ( $buf eq "\x{FF}\x{FE}" ) {
|
|
| 10061 |
+ $encoding = 'UTF-16LE'; |
|
| 10062 |
+ } elsif ( $buf eq "\x{EF}\x{BB}" ) {
|
|
| 10063 |
+ $buf = ' '; |
|
| 10064 |
+ $count = $fh->read( $buf, length $buf ); |
|
| 10065 |
+ if ( defined $count and $count >= 1 and $buf eq "\x{BF}" ) {
|
|
| 10066 |
+ $encoding = 'UTF-8'; |
|
| 10067 |
+ } |
|
| 10068 |
+ } |
|
| 10069 |
+ |
|
| 10070 |
+ if ( defined $encoding ) {
|
|
| 10071 |
+ if ( "$]" >= 5.008 ) {
|
|
| 10072 |
+ # $fh->binmode requires perl 5.10 |
|
| 10073 |
+ binmode( $fh, ":encoding($encoding)" ); |
|
| 10074 |
+ } |
|
| 9207 | 10075 |
} else {
|
| 9208 |
- $class->setup_env_hash_for($path); |
|
| 9209 |
- @INC = _uniq(split($Config{path_sep}, $ENV{PERL5LIB}), @INC);
|
|
| 10076 |
+ $fh->setpos($pos) |
|
| 10077 |
+ or croak( sprintf "Can't reset position to the top of '$filename'" ); |
|
| 9210 | 10078 |
} |
| 9211 |
- } |
|
| 9212 | 10079 |
|
| 9213 |
- sub install_base_bin_path {
|
|
| 9214 |
- my ($class, $path) = @_; |
|
| 9215 |
- File::Spec->catdir($path, 'bin'); |
|
| 10080 |
+ return $encoding; |
|
| 9216 | 10081 |
} |
| 9217 | 10082 |
|
| 9218 |
- sub install_base_perl_path {
|
|
| 9219 |
- my ($class, $path) = @_; |
|
| 9220 |
- File::Spec->catdir($path, 'lib', 'perl5'); |
|
| 9221 |
- } |
|
| 10083 |
+ sub _parse_fh {
|
|
| 10084 |
+ my ($self, $fh) = @_; |
|
| 9222 | 10085 |
|
| 9223 |
- sub install_base_arch_path {
|
|
| 9224 |
- my ($class, $path) = @_; |
|
| 9225 |
- File::Spec->catdir($class->install_base_perl_path($path), $Config{archname});
|
|
| 9226 |
- } |
|
| 10086 |
+ my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 ); |
|
| 10087 |
+ my( @pkgs, %vers, %pod, @pod ); |
|
| 10088 |
+ my $pkg = 'main'; |
|
| 10089 |
+ my $pod_sect = ''; |
|
| 10090 |
+ my $pod_data = ''; |
|
| 9227 | 10091 |
|
| 9228 |
- sub ensure_dir_structure_for {
|
|
| 9229 |
- my ($class, $path) = @_; |
|
| 9230 |
- unless (-d $path) {
|
|
| 9231 |
- warn "Attempting to create directory ${path}\n";
|
|
| 9232 |
- } |
|
| 9233 |
- File::Path::mkpath($path); |
|
| 9234 |
- # Need to have the path exist to make a short name for it, so |
|
| 9235 |
- # converting to a short name here. |
|
| 9236 |
- $path = Win32::GetShortPathName($path) if $^O eq 'MSWin32'; |
|
| 10092 |
+ while (defined( my $line = <$fh> )) {
|
|
| 10093 |
+ my $line_num = $.; |
|
| 9237 | 10094 |
|
| 9238 |
- return $path; |
|
| 9239 |
- } |
|
| 10095 |
+ chomp( $line ); |
|
| 9240 | 10096 |
|
| 9241 |
- sub INTERPOLATE_ENV () { 1 }
|
|
| 9242 |
- sub LITERAL_ENV () { 0 }
|
|
| 10097 |
+ # From toke.c : any line that begins by "=X", where X is an alphabetic |
|
| 10098 |
+ # character, introduces a POD segment. |
|
| 10099 |
+ my $is_cut; |
|
| 10100 |
+ if ( $line =~ /^=([a-zA-Z].*)/ ) {
|
|
| 10101 |
+ my $cmd = $1; |
|
| 10102 |
+ # Then it goes back to Perl code for "=cutX" where X is a non-alphabetic |
|
| 10103 |
+ # character (which includes the newline, but here we chomped it away). |
|
| 10104 |
+ $is_cut = $cmd =~ /^cut(?:[^a-zA-Z]|$)/; |
|
| 10105 |
+ $in_pod = !$is_cut; |
|
| 10106 |
+ } |
|
| 10107 |
+ |
|
| 10108 |
+ if ( $in_pod ) {
|
|
| 10109 |
+ |
|
| 10110 |
+ if ( $line =~ /^=head[1-4]\s+(.+)\s*$/ ) {
|
|
| 10111 |
+ push( @pod, $1 ); |
|
| 10112 |
+ if ( $self->{collect_pod} && length( $pod_data ) ) {
|
|
| 10113 |
+ $pod{$pod_sect} = $pod_data;
|
|
| 10114 |
+ $pod_data = ''; |
|
| 10115 |
+ } |
|
| 10116 |
+ $pod_sect = $1; |
|
| 10117 |
+ |
|
| 10118 |
+ } elsif ( $self->{collect_pod} ) {
|
|
| 10119 |
+ $pod_data .= "$line\n"; |
|
| 9243 | 10120 |
|
| 9244 |
- sub guess_shelltype {
|
|
| 9245 |
- my $shellbin = 'sh'; |
|
| 9246 |
- if(defined $ENV{'SHELL'}) {
|
|
| 9247 |
- my @shell_bin_path_parts = File::Spec->splitpath($ENV{'SHELL'});
|
|
| 9248 |
- $shellbin = $shell_bin_path_parts[-1]; |
|
| 9249 |
- } |
|
| 9250 |
- my $shelltype = do {
|
|
| 9251 |
- local $_ = $shellbin; |
|
| 9252 |
- if(/csh/) {
|
|
| 9253 |
- 'csh' |
|
| 9254 |
- } else {
|
|
| 9255 |
- 'bourne' |
|
| 9256 | 10121 |
} |
| 9257 |
- }; |
|
| 9258 | 10122 |
|
| 9259 |
- # Both Win32 and Cygwin have $ENV{COMSPEC} set.
|
|
| 9260 |
- if (defined $ENV{'COMSPEC'} && $^O ne 'cygwin') {
|
|
| 9261 |
- my @shell_bin_path_parts = File::Spec->splitpath($ENV{'COMSPEC'});
|
|
| 9262 |
- $shellbin = $shell_bin_path_parts[-1]; |
|
| 9263 |
- $shelltype = do {
|
|
| 9264 |
- local $_ = $shellbin; |
|
| 9265 |
- if(/command\.com/) {
|
|
| 9266 |
- 'win32' |
|
| 9267 |
- } elsif(/cmd\.exe/) {
|
|
| 9268 |
- 'win32' |
|
| 9269 |
- } elsif(/4nt\.exe/) {
|
|
| 9270 |
- 'win32' |
|
| 9271 |
- } else {
|
|
| 9272 |
- $shelltype |
|
| 9273 |
- } |
|
| 9274 |
- }; |
|
| 9275 |
- } |
|
| 9276 |
- return $shelltype; |
|
| 9277 |
- } |
|
| 10123 |
+ } elsif ( $is_cut ) {
|
|
| 9278 | 10124 |
|
| 9279 |
- sub print_environment_vars_for {
|
|
| 9280 |
- my ($class, $path) = @_; |
|
| 9281 |
- print $class->environment_vars_string_for($path); |
|
| 9282 |
- } |
|
| 10125 |
+ if ( $self->{collect_pod} && length( $pod_data ) ) {
|
|
| 10126 |
+ $pod{$pod_sect} = $pod_data;
|
|
| 10127 |
+ $pod_data = ''; |
|
| 10128 |
+ } |
|
| 10129 |
+ $pod_sect = ''; |
|
| 9283 | 10130 |
|
| 9284 |
- sub environment_vars_string_for {
|
|
| 9285 |
- my ($class, $path) = @_; |
|
| 9286 |
- my @envs = $class->build_environment_vars_for($path, LITERAL_ENV); |
|
| 9287 |
- my $out = ''; |
|
| 10131 |
+ } else {
|
|
| 9288 | 10132 |
|
| 9289 |
- # rather basic csh detection, goes on the assumption that something won't |
|
| 9290 |
- # call itself csh unless it really is. also, default to bourne in the |
|
| 9291 |
- # pathological situation where a user doesn't have $ENV{SHELL} defined.
|
|
| 9292 |
- # note also that shells with funny names, like zoid, are assumed to be |
|
| 9293 |
- # bourne. |
|
| 10133 |
+ # Skip comments in code |
|
| 10134 |
+ next if $line =~ /^\s*#/; |
|
| 9294 | 10135 |
|
| 9295 |
- my $shelltype = $class->guess_shelltype; |
|
| 10136 |
+ # Would be nice if we could also check $in_string or something too |
|
| 10137 |
+ last if $line =~ /^__(?:DATA|END)__$/; |
|
| 9296 | 10138 |
|
| 9297 |
- while (@envs) {
|
|
| 9298 |
- my ($name, $value) = (shift(@envs), shift(@envs)); |
|
| 9299 |
- $value =~ s/(\\")/\\$1/g; |
|
| 9300 |
- $out .= $class->${\"build_${shelltype}_env_declaration"}($name, $value);
|
|
| 9301 |
- } |
|
| 9302 |
- return $out; |
|
| 9303 |
- } |
|
| 10139 |
+ # parse $line to see if it's a $VERSION declaration |
|
| 10140 |
+ my( $vers_sig, $vers_fullname, $vers_pkg ) = |
|
| 10141 |
+ ($line =~ /VERSION/) |
|
| 10142 |
+ ? $self->_parse_version_expression( $line ) |
|
| 10143 |
+ : (); |
|
| 9304 | 10144 |
|
| 9305 |
- # simple routines that take two arguments: an %ENV key and a value. return |
|
| 9306 |
- # strings that are suitable for passing directly to the relevant shell to set |
|
| 9307 |
- # said key to said value. |
|
| 9308 |
- sub build_bourne_env_declaration {
|
|
| 9309 |
- my $class = shift; |
|
| 9310 |
- my($name, $value) = @_; |
|
| 9311 |
- return qq{export ${name}="${value}"\n};
|
|
| 9312 |
- } |
|
| 10145 |
+ if ( $line =~ /$PKG_REGEXP/o ) {
|
|
| 10146 |
+ $pkg = $1; |
|
| 10147 |
+ push( @pkgs, $pkg ) unless grep( $pkg eq $_, @pkgs ); |
|
| 10148 |
+ $vers{$pkg} = $2 unless exists( $vers{$pkg} );
|
|
| 10149 |
+ $need_vers = defined $2 ? 0 : 1; |
|
| 9313 | 10150 |
|
| 9314 |
- sub build_csh_env_declaration {
|
|
| 9315 |
- my $class = shift; |
|
| 9316 |
- my($name, $value) = @_; |
|
| 9317 |
- return qq{setenv ${name} "${value}"\n};
|
|
| 9318 |
- } |
|
| 10151 |
+ # VERSION defined with full package spec, i.e. $Module::VERSION |
|
| 10152 |
+ } elsif ( $vers_fullname && $vers_pkg ) {
|
|
| 10153 |
+ push( @pkgs, $vers_pkg ) unless grep( $vers_pkg eq $_, @pkgs ); |
|
| 10154 |
+ $need_vers = 0 if $vers_pkg eq $pkg; |
|
| 9319 | 10155 |
|
| 9320 |
- sub build_win32_env_declaration {
|
|
| 9321 |
- my $class = shift; |
|
| 9322 |
- my($name, $value) = @_; |
|
| 9323 |
- return qq{set ${name}=${value}\n};
|
|
| 9324 |
- } |
|
| 10156 |
+ unless ( defined $vers{$vers_pkg} && length $vers{$vers_pkg} ) {
|
|
| 10157 |
+ $vers{$vers_pkg} =
|
|
| 10158 |
+ $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line ); |
|
| 10159 |
+ } |
|
| 9325 | 10160 |
|
| 9326 |
- sub setup_env_hash_for {
|
|
| 9327 |
- my ($class, $path) = @_; |
|
| 9328 |
- my %envs = $class->build_environment_vars_for($path, INTERPOLATE_ENV); |
|
| 9329 |
- @ENV{keys %envs} = values %envs;
|
|
| 9330 |
- } |
|
| 10161 |
+ # first non-comment line in undeclared package main is VERSION |
|
| 10162 |
+ } elsif ( !exists($vers{main}) && $pkg eq 'main' && $vers_fullname ) {
|
|
| 10163 |
+ $need_vers = 0; |
|
| 10164 |
+ my $v = |
|
| 10165 |
+ $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line ); |
|
| 10166 |
+ $vers{$pkg} = $v;
|
|
| 10167 |
+ push( @pkgs, 'main' ); |
|
| 9331 | 10168 |
|
| 9332 |
- sub build_environment_vars_for {
|
|
| 9333 |
- my ($class, $path, $interpolate) = @_; |
|
| 9334 |
- return ( |
|
| 9335 |
- PERL_LOCAL_LIB_ROOT => $path, |
|
| 9336 |
- PERL_MB_OPT => "--install_base ${path}",
|
|
| 9337 |
- PERL_MM_OPT => "INSTALL_BASE=${path}",
|
|
| 9338 |
- PERL5LIB => join($Config{path_sep},
|
|
| 9339 |
- $class->install_base_arch_path($path), |
|
| 9340 |
- $class->install_base_perl_path($path), |
|
| 9341 |
- (($ENV{PERL5LIB}||()) ?
|
|
| 9342 |
- ($interpolate == INTERPOLATE_ENV |
|
| 9343 |
- ? ($ENV{PERL5LIB})
|
|
| 9344 |
- : (($^O ne 'MSWin32') ? '$PERL5LIB' : '%PERL5LIB%' )) |
|
| 9345 |
- : ()) |
|
| 9346 |
- ), |
|
| 9347 |
- PATH => join($Config{path_sep},
|
|
| 9348 |
- $class->install_base_bin_path($path), |
|
| 9349 |
- ($interpolate == INTERPOLATE_ENV |
|
| 9350 |
- ? ($ENV{PATH}||())
|
|
| 9351 |
- : (($^O ne 'MSWin32') ? '$PATH' : '%PATH%' )) |
|
| 9352 |
- ), |
|
| 9353 |
- ) |
|
| 9354 |
- } |
|
| 10169 |
+ # first non-comment line in undeclared package defines package main |
|
| 10170 |
+ } elsif ( !exists($vers{main}) && $pkg eq 'main' && $line =~ /\w+/ ) {
|
|
| 10171 |
+ $need_vers = 1; |
|
| 10172 |
+ $vers{main} = '';
|
|
| 10173 |
+ push( @pkgs, 'main' ); |
|
| 9355 | 10174 |
|
| 9356 |
- 1; |
|
| 9357 |
-LOCAL_LIB |
|
| 9358 |
- |
|
| 9359 |
-$fatpacked{"parent.pm"} = <<'PARENT';
|
|
| 9360 |
- package parent; |
|
| 9361 |
- use strict; |
|
| 9362 |
- use vars qw($VERSION); |
|
| 9363 |
- $VERSION = '0.225'; |
|
| 10175 |
+ # only keep if this is the first $VERSION seen |
|
| 10176 |
+ } elsif ( $vers_fullname && $need_vers ) {
|
|
| 10177 |
+ $need_vers = 0; |
|
| 10178 |
+ my $v = |
|
| 10179 |
+ $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line ); |
|
| 9364 | 10180 |
|
| 9365 |
- sub import {
|
|
| 9366 |
- my $class = shift; |
|
| 9367 | 10181 |
|
| 9368 |
- my $inheritor = caller(0); |
|
| 10182 |
+ unless ( defined $vers{$pkg} && length $vers{$pkg} ) {
|
|
| 10183 |
+ $vers{$pkg} = $v;
|
|
| 10184 |
+ } |
|
| 9369 | 10185 |
|
| 9370 |
- if ( @_ and $_[0] eq '-norequire' ) {
|
|
| 9371 |
- shift @_; |
|
| 9372 |
- } else {
|
|
| 9373 |
- for ( my @filename = @_ ) {
|
|
| 9374 |
- if ( $_ eq $inheritor ) {
|
|
| 9375 |
- warn "Class '$inheritor' tried to inherit from itself\n"; |
|
| 9376 |
- }; |
|
| 10186 |
+ } |
|
| 9377 | 10187 |
|
| 9378 |
- s{::|'}{/}g;
|
|
| 9379 |
- require "$_.pm"; # dies if the file is not found |
|
| 9380 |
- } |
|
| 9381 | 10188 |
} |
| 9382 | 10189 |
|
| 9383 |
- {
|
|
| 9384 |
- no strict 'refs'; |
|
| 9385 |
- push @{"$inheritor\::ISA"}, @_;
|
|
| 9386 |
- }; |
|
| 9387 |
- }; |
|
| 10190 |
+ } |
|
| 9388 | 10191 |
|
| 9389 |
- "All your base are belong to us" |
|
| 10192 |
+ if ( $self->{collect_pod} && length($pod_data) ) {
|
|
| 10193 |
+ $pod{$pod_sect} = $pod_data;
|
|
| 10194 |
+ } |
|
| 9390 | 10195 |
|
| 9391 |
- __END__ |
|
| 10196 |
+ $self->{versions} = \%vers;
|
|
| 10197 |
+ $self->{packages} = \@pkgs;
|
|
| 10198 |
+ $self->{pod} = \%pod;
|
|
| 10199 |
+ $self->{pod_headings} = \@pod;
|
|
| 10200 |
+ } |
|
| 9392 | 10201 |
|
| 9393 |
-PARENT |
|
| 9394 |
- |
|
| 9395 |
-$fatpacked{"version.pm"} = <<'VERSION';
|
|
| 9396 |
- #!perl -w |
|
| 9397 |
- package version; |
|
| 10202 |
+ {
|
|
| 10203 |
+ my $pn = 0; |
|
| 10204 |
+ sub _evaluate_version_line {
|
|
| 10205 |
+ my $self = shift; |
|
| 10206 |
+ my( $sigil, $var, $line ) = @_; |
|
| 9398 | 10207 |
|
| 9399 |
- use 5.005_04; |
|
| 9400 |
- use strict; |
|
| 10208 |
+ # Some of this code came from the ExtUtils:: hierarchy. |
|
| 9401 | 10209 |
|
| 9402 |
- use vars qw(@ISA $VERSION $CLASS $STRICT $LAX *declare *qv); |
|
| 10210 |
+ # We compile into $vsub because 'use version' would cause |
|
| 10211 |
+ # compiletime/runtime issues with local() |
|
| 10212 |
+ my $vsub; |
|
| 10213 |
+ $pn++; # everybody gets their own package |
|
| 10214 |
+ my $eval = qq{BEGIN { q# Hide from _packages_inside()
|
|
| 10215 |
+ #; package Module::Metadata::_version::p$pn; |
|
| 10216 |
+ use version; |
|
| 10217 |
+ no strict; |
|
| 9403 | 10218 |
|
| 9404 |
- $VERSION = 0.88; |
|
| 10219 |
+ \$vsub = sub {
|
|
| 10220 |
+ local $sigil$var; |
|
| 10221 |
+ \$$var=undef; |
|
| 10222 |
+ $line; |
|
| 10223 |
+ \$$var |
|
| 10224 |
+ }; |
|
| 10225 |
+ }}; |
|
| 9405 | 10226 |
|
| 9406 |
- $CLASS = 'version'; |
|
| 10227 |
+ local $^W; |
|
| 10228 |
+ # Try to get the $VERSION |
|
| 10229 |
+ eval $eval; |
|
| 10230 |
+ # some modules say $VERSION = $Foo::Bar::VERSION, but Foo::Bar isn't |
|
| 10231 |
+ # installed, so we need to hunt in ./lib for it |
|
| 10232 |
+ if ( $@ =~ /Can't locate/ && -d 'lib' ) {
|
|
| 10233 |
+ local @INC = ('lib',@INC);
|
|
| 10234 |
+ eval $eval; |
|
| 10235 |
+ } |
|
| 10236 |
+ warn "Error evaling version line '$eval' in $self->{filename}: $@\n"
|
|
| 10237 |
+ if $@; |
|
| 10238 |
+ (ref($vsub) eq 'CODE') or |
|
| 10239 |
+ croak "failed to build version sub for $self->{filename}";
|
|
| 10240 |
+ my $result = eval { $vsub->() };
|
|
| 10241 |
+ croak "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n"
|
|
| 10242 |
+ if $@; |
|
| 9407 | 10243 |
|
| 9408 |
- #--------------------------------------------------------------------------# |
|
| 9409 |
- # Version regexp components |
|
| 9410 |
- #--------------------------------------------------------------------------# |
|
| 10244 |
+ # Upgrade it into a version object |
|
| 10245 |
+ my $version = eval { _dwim_version($result) };
|
|
| 9411 | 10246 |
|
| 9412 |
- # Fraction part of a decimal version number. This is a common part of |
|
| 9413 |
- # both strict and lax decimal versions |
|
| 10247 |
+ croak "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n"
|
|
| 10248 |
+ unless defined $version; # "0" is OK! |
|
| 9414 | 10249 |
|
| 9415 |
- my $FRACTION_PART = qr/\.[0-9]+/; |
|
| 10250 |
+ return $version; |
|
| 10251 |
+ } |
|
| 10252 |
+ } |
|
| 9416 | 10253 |
|
| 9417 |
- # First part of either decimal or dotted-decimal strict version number. |
|
| 9418 |
- # Unsigned integer with no leading zeroes (except for zero itself) to |
|
| 9419 |
- # avoid confusion with octal. |
|
| 10254 |
+ # Try to DWIM when things fail the lax version test in obvious ways |
|
| 10255 |
+ {
|
|
| 10256 |
+ my @version_prep = ( |
|
| 10257 |
+ # Best case, it just works |
|
| 10258 |
+ sub { return shift },
|
|
| 9420 | 10259 |
|
| 9421 |
- my $STRICT_INTEGER_PART = qr/0|[1-9][0-9]*/; |
|
| 10260 |
+ # If we still don't have a version, try stripping any |
|
| 10261 |
+ # trailing junk that is prohibited by lax rules |
|
| 10262 |
+ sub {
|
|
| 10263 |
+ my $v = shift; |
|
| 10264 |
+ $v =~ s{([0-9])[a-z-].*$}{$1}i; # 1.23-alpha or 1.23b
|
|
| 10265 |
+ return $v; |
|
| 10266 |
+ }, |
|
| 9422 | 10267 |
|
| 9423 |
- # First part of either decimal or dotted-decimal lax version number. |
|
| 9424 |
- # Unsigned integer, but allowing leading zeros. Always interpreted |
|
| 9425 |
- # as decimal. However, some forms of the resulting syntax give odd |
|
| 9426 |
- # results if used as ordinary Perl expressions, due to how perl treats |
|
| 9427 |
- # octals. E.g. |
|
| 9428 |
- # version->new("010" ) == 10
|
|
| 9429 |
- # version->new( 010 ) == 8 |
|
| 9430 |
- # version->new( 010.2) == 82 # "8" . "2" |
|
| 10268 |
+ # Activestate apparently creates custom versions like '1.23_45_01', which |
|
| 10269 |
+ # cause version.pm to think it's an invalid alpha. So check for that |
|
| 10270 |
+ # and strip them |
|
| 10271 |
+ sub {
|
|
| 10272 |
+ my $v = shift; |
|
| 10273 |
+ my $num_dots = () = $v =~ m{(\.)}g;
|
|
| 10274 |
+ my $num_unders = () = $v =~ m{(_)}g;
|
|
| 10275 |
+ my $leading_v = substr($v,0,1) eq 'v'; |
|
| 10276 |
+ if ( ! $leading_v && $num_dots < 2 && $num_unders > 1 ) {
|
|
| 10277 |
+ $v =~ s{_}{}g;
|
|
| 10278 |
+ $num_unders = () = $v =~ m{(_)}g;
|
|
| 10279 |
+ } |
|
| 10280 |
+ return $v; |
|
| 10281 |
+ }, |
|
| 9431 | 10282 |
|
| 9432 |
- my $LAX_INTEGER_PART = qr/[0-9]+/; |
|
| 10283 |
+ # Worst case, try numifying it like we would have before version objects |
|
| 10284 |
+ sub {
|
|
| 10285 |
+ my $v = shift; |
|
| 10286 |
+ no warnings 'numeric'; |
|
| 10287 |
+ return 0 + $v; |
|
| 10288 |
+ }, |
|
| 9433 | 10289 |
|
| 9434 |
- # Second and subsequent part of a strict dotted-decimal version number. |
|
| 9435 |
- # Leading zeroes are permitted, and the number is always decimal. |
|
| 9436 |
- # Limited to three digits to avoid overflow when converting to decimal |
|
| 9437 |
- # form and also avoid problematic style with excessive leading zeroes. |
|
| 10290 |
+ ); |
|
| 9438 | 10291 |
|
| 9439 |
- my $STRICT_DOTTED_DECIMAL_PART = qr/\.[0-9]{1,3}/;
|
|
| 10292 |
+ sub _dwim_version {
|
|
| 10293 |
+ my ($result) = shift; |
|
| 9440 | 10294 |
|
| 9441 |
- # Second and subsequent part of a lax dotted-decimal version number. |
|
| 9442 |
- # Leading zeroes are permitted, and the number is always decimal. No |
|
| 9443 |
- # limit on the numerical value or number of digits, so there is the |
|
| 9444 |
- # possibility of overflow when converting to decimal form. |
|
| 10295 |
+ return $result if ref($result) eq 'version'; |
|
| 9445 | 10296 |
|
| 9446 |
- my $LAX_DOTTED_DECIMAL_PART = qr/\.[0-9]+/; |
|
| 10297 |
+ my ($version, $error); |
|
| 10298 |
+ for my $f (@version_prep) {
|
|
| 10299 |
+ $result = $f->($result); |
|
| 10300 |
+ $version = eval { version->new($result) };
|
|
| 10301 |
+ $error ||= $@ if $@; # capture first failure |
|
| 10302 |
+ last if defined $version; |
|
| 10303 |
+ } |
|
| 9447 | 10304 |
|
| 9448 |
- # Alpha suffix part of lax version number syntax. Acts like a |
|
| 9449 |
- # dotted-decimal part. |
|
| 10305 |
+ croak $error unless defined $version; |
|
| 9450 | 10306 |
|
| 9451 |
- my $LAX_ALPHA_PART = qr/_[0-9]+/; |
|
| 10307 |
+ return $version; |
|
| 10308 |
+ } |
|
| 10309 |
+ } |
|
| 9452 | 10310 |
|
| 9453 |
- #--------------------------------------------------------------------------# |
|
| 9454 |
- # Strict version regexp definitions |
|
| 9455 |
- #--------------------------------------------------------------------------# |
|
| 10311 |
+ ############################################################ |
|
| 9456 | 10312 |
|
| 9457 |
- # Strict decimal version number. |
|
| 10313 |
+ # accessors |
|
| 10314 |
+ sub name { $_[0]->{module} }
|
|
| 9458 | 10315 |
|
| 9459 |
- my $STRICT_DECIMAL_VERSION = |
|
| 9460 |
- qr/ $STRICT_INTEGER_PART $FRACTION_PART? /x; |
|
| 10316 |
+ sub filename { $_[0]->{filename} }
|
|
| 10317 |
+ sub packages_inside { @{$_[0]->{packages}} }
|
|
| 10318 |
+ sub pod_inside { @{$_[0]->{pod_headings}} }
|
|
| 10319 |
+ sub contains_pod { $#{$_[0]->{pod_headings}} }
|
|
| 9461 | 10320 |
|
| 9462 |
- # Strict dotted-decimal version number. Must have both leading "v" and |
|
| 9463 |
- # at least three parts, to avoid confusion with decimal syntax. |
|
| 10321 |
+ sub version {
|
|
| 10322 |
+ my $self = shift; |
|
| 10323 |
+ my $mod = shift || $self->{module};
|
|
| 10324 |
+ my $vers; |
|
| 10325 |
+ if ( defined( $mod ) && length( $mod ) && |
|
| 10326 |
+ exists( $self->{versions}{$mod} ) ) {
|
|
| 10327 |
+ return $self->{versions}{$mod};
|
|
| 10328 |
+ } else {
|
|
| 10329 |
+ return undef; |
|
| 10330 |
+ } |
|
| 10331 |
+ } |
|
| 9464 | 10332 |
|
| 9465 |
- my $STRICT_DOTTED_DECIMAL_VERSION = |
|
| 9466 |
- qr/ v $STRICT_INTEGER_PART $STRICT_DOTTED_DECIMAL_PART{2,} /x;
|
|
| 10333 |
+ sub pod {
|
|
| 10334 |
+ my $self = shift; |
|
| 10335 |
+ my $sect = shift; |
|
| 10336 |
+ if ( defined( $sect ) && length( $sect ) && |
|
| 10337 |
+ exists( $self->{pod}{$sect} ) ) {
|
|
| 10338 |
+ return $self->{pod}{$sect};
|
|
| 10339 |
+ } else {
|
|
| 10340 |
+ return undef; |
|
| 10341 |
+ } |
|
| 10342 |
+ } |
|
| 9467 | 10343 |
|
| 9468 |
- # Complete strict version number syntax -- should generally be used |
|
| 9469 |
- # anchored: qr/ \A $STRICT \z /x |
|
| 10344 |
+ 1; |
|
| 9470 | 10345 |
|
| 9471 |
- $STRICT = |
|
| 9472 |
- qr/ $STRICT_DECIMAL_VERSION | $STRICT_DOTTED_DECIMAL_VERSION /x; |
|
| 10346 |
+MODULE_METADATA |
|
| 10347 |
+ |
|
| 10348 |
+$fatpacked{"Parse/CPAN/Meta.pm"} = <<'PARSE_CPAN_META';
|
|
| 10349 |
+ package Parse::CPAN::Meta; |
|
| 9473 | 10350 |
|
| 9474 |
- #--------------------------------------------------------------------------# |
|
| 9475 |
- # Lax version regexp definitions |
|
| 9476 |
- #--------------------------------------------------------------------------# |
|
| 10351 |
+ use strict; |
|
| 10352 |
+ use Carp 'croak'; |
|
| 9477 | 10353 |
|
| 9478 |
- # Lax decimal version number. Just like the strict one except for |
|
| 9479 |
- # allowing an alpha suffix or allowing a leading or trailing |
|
| 9480 |
- # decimal-point |
|
| 10354 |
+ # UTF Support? |
|
| 10355 |
+ sub HAVE_UTF8 () { $] >= 5.007003 }
|
|
| 10356 |
+ sub IO_LAYER () { $] >= 5.008001 ? ":utf8" : "" }
|
|
| 9481 | 10357 |
|
| 9482 |
- my $LAX_DECIMAL_VERSION = |
|
| 9483 |
- qr/ $LAX_INTEGER_PART (?: \. | $FRACTION_PART $LAX_ALPHA_PART? )? |
|
| 9484 |
- | |
|
| 9485 |
- $FRACTION_PART $LAX_ALPHA_PART? |
|
| 9486 |
- /x; |
|
| 10358 |
+ BEGIN {
|
|
| 10359 |
+ if ( HAVE_UTF8 ) {
|
|
| 10360 |
+ # The string eval helps hide this from Test::MinimumVersion |
|
| 10361 |
+ eval "require utf8;"; |
|
| 10362 |
+ die "Failed to load UTF-8 support" if $@; |
|
| 10363 |
+ } |
|
| 9487 | 10364 |
|
| 9488 |
- # Lax dotted-decimal version number. Distinguished by having either |
|
| 9489 |
- # leading "v" or at least three non-alpha parts. Alpha part is only |
|
| 9490 |
- # permitted if there are at least two non-alpha parts. Strangely |
|
| 9491 |
- # enough, without the leading "v", Perl takes .1.2 to mean v0.1.2, |
|
| 9492 |
- # so when there is no "v", the leading part is optional |
|
| 10365 |
+ # Class structure |
|
| 10366 |
+ require 5.004; |
|
| 10367 |
+ require Exporter; |
|
| 10368 |
+ $Parse::CPAN::Meta::VERSION = '1.4404'; |
|
| 10369 |
+ @Parse::CPAN::Meta::ISA = qw{ Exporter };
|
|
| 10370 |
+ @Parse::CPAN::Meta::EXPORT_OK = qw{ Load LoadFile };
|
|
| 10371 |
+ } |
|
| 9493 | 10372 |
|
| 9494 |
- my $LAX_DOTTED_DECIMAL_VERSION = |
|
| 9495 |
- qr/ |
|
| 9496 |
- v $LAX_INTEGER_PART (?: $LAX_DOTTED_DECIMAL_PART+ $LAX_ALPHA_PART? )? |
|
| 9497 |
- | |
|
| 9498 |
- $LAX_INTEGER_PART? $LAX_DOTTED_DECIMAL_PART{2,} $LAX_ALPHA_PART?
|
|
| 9499 |
- /x; |
|
| 10373 |
+ sub load_file {
|
|
| 10374 |
+ my ($class, $filename) = @_; |
|
| 9500 | 10375 |
|
| 9501 |
- # Complete lax version number syntax -- should generally be used |
|
| 9502 |
- # anchored: qr/ \A $LAX \z /x |
|
| 9503 |
- # |
|
| 9504 |
- # The string 'undef' is a special case to make for easier handling |
|
| 9505 |
- # of return values from ExtUtils::MM->parse_version |
|
| 10376 |
+ if ($filename =~ /\.ya?ml$/) {
|
|
| 10377 |
+ return $class->load_yaml_string(_slurp($filename)); |
|
| 10378 |
+ } |
|
| 9506 | 10379 |
|
| 9507 |
- $LAX = |
|
| 9508 |
- qr/ undef | $LAX_DECIMAL_VERSION | $LAX_DOTTED_DECIMAL_VERSION /x; |
|
| 10380 |
+ if ($filename =~ /\.json$/) {
|
|
| 10381 |
+ return $class->load_json_string(_slurp($filename)); |
|
| 10382 |
+ } |
|
| 9509 | 10383 |
|
| 9510 |
- #--------------------------------------------------------------------------# |
|
| 10384 |
+ croak("file type cannot be determined by filename");
|
|
| 10385 |
+ } |
|
| 9511 | 10386 |
|
| 9512 |
- eval "use version::vxs $VERSION"; |
|
| 9513 |
- if ( $@ ) { # don't have the XS version installed
|
|
| 9514 |
- eval "use version::vpp $VERSION"; # don't tempt fate |
|
| 9515 |
- die "$@" if ( $@ ); |
|
| 9516 |
- push @ISA, "version::vpp"; |
|
| 9517 |
- local $^W; |
|
| 9518 |
- *version::qv = \&version::vpp::qv; |
|
| 9519 |
- *version::declare = \&version::vpp::declare; |
|
| 9520 |
- *version::_VERSION = \&version::vpp::_VERSION; |
|
| 9521 |
- if ($] >= 5.009000 && $] < 5.011004) {
|
|
| 9522 |
- no strict 'refs'; |
|
| 9523 |
- *version::stringify = \&version::vpp::stringify; |
|
| 9524 |
- *{'version::(""'} = \&version::vpp::stringify;
|
|
| 9525 |
- *version::new = \&version::vpp::new; |
|
| 9526 |
- *version::parse = \&version::vpp::parse; |
|
| 9527 |
- } |
|
| 9528 |
- } |
|
| 9529 |
- else { # use XS module
|
|
| 9530 |
- push @ISA, "version::vxs"; |
|
| 9531 |
- local $^W; |
|
| 9532 |
- *version::declare = \&version::vxs::declare; |
|
| 9533 |
- *version::qv = \&version::vxs::qv; |
|
| 9534 |
- *version::_VERSION = \&version::vxs::_VERSION; |
|
| 9535 |
- *version::vcmp = \&version::vxs::VCMP; |
|
| 9536 |
- if ($] >= 5.009000 && $] < 5.011004) {
|
|
| 9537 |
- no strict 'refs'; |
|
| 9538 |
- *version::stringify = \&version::vxs::stringify; |
|
| 9539 |
- *{'version::(""'} = \&version::vxs::stringify;
|
|
| 9540 |
- *version::new = \&version::vxs::new; |
|
| 9541 |
- *version::parse = \&version::vxs::parse; |
|
| 9542 |
- } |
|
| 10387 |
+ sub load_yaml_string {
|
|
| 10388 |
+ my ($class, $string) = @_; |
|
| 10389 |
+ my $backend = $class->yaml_backend(); |
|
| 10390 |
+ my $data = eval { no strict 'refs'; &{"$backend\::Load"}($string) };
|
|
| 10391 |
+ if ( $@ ) {
|
|
| 10392 |
+ croak $backend->can('errstr') ? $backend->errstr : $@
|
|
| 10393 |
+ } |
|
| 10394 |
+ return $data || {}; # in case document was valid but empty
|
|
| 10395 |
+ } |
|
| 9543 | 10396 |
|
| 10397 |
+ sub load_json_string {
|
|
| 10398 |
+ my ($class, $string) = @_; |
|
| 10399 |
+ return $class->json_backend()->new->decode($string); |
|
| 9544 | 10400 |
} |
| 9545 | 10401 |
|
| 9546 |
- # Preloaded methods go here. |
|
| 9547 |
- sub import {
|
|
| 9548 |
- no strict 'refs'; |
|
| 9549 |
- my ($class) = shift; |
|
| 10402 |
+ sub yaml_backend {
|
|
| 10403 |
+ local $Module::Load::Conditional::CHECK_INC_HASH = 1; |
|
| 10404 |
+ if (! defined $ENV{PERL_YAML_BACKEND} ) {
|
|
| 10405 |
+ _can_load( 'CPAN::Meta::YAML', 0.002 ) |
|
| 10406 |
+ or croak "CPAN::Meta::YAML 0.002 is not available\n"; |
|
| 10407 |
+ return "CPAN::Meta::YAML"; |
|
| 10408 |
+ } |
|
| 10409 |
+ else {
|
|
| 10410 |
+ my $backend = $ENV{PERL_YAML_BACKEND};
|
|
| 10411 |
+ _can_load( $backend ) |
|
| 10412 |
+ or croak "Could not load PERL_YAML_BACKEND '$backend'\n"; |
|
| 10413 |
+ $backend->can("Load")
|
|
| 10414 |
+ or croak "PERL_YAML_BACKEND '$backend' does not implement Load()\n"; |
|
| 10415 |
+ return $backend; |
|
| 10416 |
+ } |
|
| 10417 |
+ } |
|
| 9550 | 10418 |
|
| 9551 |
- # Set up any derived class |
|
| 9552 |
- unless ($class eq 'version') {
|
|
| 9553 |
- local $^W; |
|
| 9554 |
- *{$class.'::declare'} = \&version::declare;
|
|
| 9555 |
- *{$class.'::qv'} = \&version::qv;
|
|
| 9556 |
- } |
|
| 10419 |
+ sub json_backend {
|
|
| 10420 |
+ local $Module::Load::Conditional::CHECK_INC_HASH = 1; |
|
| 10421 |
+ if (! $ENV{PERL_JSON_BACKEND} or $ENV{PERL_JSON_BACKEND} eq 'JSON::PP') {
|
|
| 10422 |
+ _can_load( 'JSON::PP' => 2.27103 ) |
|
| 10423 |
+ or croak "JSON::PP 2.27103 is not available\n"; |
|
| 10424 |
+ return 'JSON::PP'; |
|
| 10425 |
+ } |
|
| 10426 |
+ else {
|
|
| 10427 |
+ _can_load( 'JSON' => 2.5 ) |
|
| 10428 |
+ or croak "JSON 2.5 is required for " . |
|
| 10429 |
+ "\$ENV{PERL_JSON_BACKEND} = '$ENV{PERL_JSON_BACKEND}'\n";
|
|
| 10430 |
+ return "JSON"; |
|
| 10431 |
+ } |
|
| 10432 |
+ } |
|
| 9557 | 10433 |
|
| 9558 |
- my %args; |
|
| 9559 |
- if (@_) { # any remaining terms are arguments
|
|
| 9560 |
- map { $args{$_} = 1 } @_
|
|
| 9561 |
- } |
|
| 9562 |
- else { # no parameters at all on use line
|
|
| 9563 |
- %args = |
|
| 9564 |
- ( |
|
| 9565 |
- qv => 1, |
|
| 9566 |
- 'UNIVERSAL::VERSION' => 1, |
|
| 9567 |
- ); |
|
| 9568 |
- } |
|
| 10434 |
+ sub _slurp {
|
|
| 10435 |
+ open my $fh, "<" . IO_LAYER, "$_[0]" |
|
| 10436 |
+ or die "can't open $_[0] for reading: $!"; |
|
| 10437 |
+ return do { local $/; <$fh> };
|
|
| 10438 |
+ } |
|
| 10439 |
+ |
|
| 10440 |
+ sub _can_load {
|
|
| 10441 |
+ my ($module, $version) = @_; |
|
| 10442 |
+ (my $file = $module) =~ s{::}{/}g;
|
|
| 10443 |
+ $file .= ".pm"; |
|
| 10444 |
+ return 1 if $INC{$file};
|
|
| 10445 |
+ return 0 if exists $INC{$file}; # prior load failed
|
|
| 10446 |
+ eval { require $file; 1 }
|
|
| 10447 |
+ or return 0; |
|
| 10448 |
+ if ( defined $version ) {
|
|
| 10449 |
+ eval { $module->VERSION($version); 1 }
|
|
| 10450 |
+ or return 0; |
|
| 10451 |
+ } |
|
| 10452 |
+ return 1; |
|
| 10453 |
+ } |
|
| 9569 | 10454 |
|
| 9570 |
- my $callpkg = caller(); |
|
| 9571 |
- |
|
| 9572 |
- if (exists($args{declare})) {
|
|
| 9573 |
- *{$callpkg.'::declare'} =
|
|
| 9574 |
- sub {return $class->declare(shift) }
|
|
| 9575 |
- unless defined(&{$callpkg.'::declare'});
|
|
| 9576 |
- } |
|
| 10455 |
+ # Kept for backwards compatibility only |
|
| 10456 |
+ # Create an object from a file |
|
| 10457 |
+ sub LoadFile ($) {
|
|
| 10458 |
+ require CPAN::Meta::YAML; |
|
| 10459 |
+ return CPAN::Meta::YAML::LoadFile(shift) |
|
| 10460 |
+ or die CPAN::Meta::YAML->errstr; |
|
| 10461 |
+ } |
|
| 9577 | 10462 |
|
| 9578 |
- if (exists($args{qv})) {
|
|
| 9579 |
- *{$callpkg.'::qv'} =
|
|
| 9580 |
- sub {return $class->qv(shift) }
|
|
| 9581 |
- unless defined(&{$callpkg.'::qv'});
|
|
| 9582 |
- } |
|
| 10463 |
+ # Parse a document from a string. |
|
| 10464 |
+ sub Load ($) {
|
|
| 10465 |
+ require CPAN::Meta::YAML; |
|
| 10466 |
+ return CPAN::Meta::YAML::Load(shift) |
|
| 10467 |
+ or die CPAN::Meta::YAML->errstr; |
|
| 10468 |
+ } |
|
| 9583 | 10469 |
|
| 9584 |
- if (exists($args{'UNIVERSAL::VERSION'})) {
|
|
| 9585 |
- local $^W; |
|
| 9586 |
- *UNIVERSAL::VERSION |
|
| 9587 |
- = \&version::_VERSION; |
|
| 9588 |
- } |
|
| 10470 |
+ 1; |
|
| 9589 | 10471 |
|
| 9590 |
- if (exists($args{'VERSION'})) {
|
|
| 9591 |
- *{$callpkg.'::VERSION'} = \&version::_VERSION;
|
|
| 9592 |
- } |
|
| 10472 |
+ __END__ |
|
| 9593 | 10473 |
|
| 9594 |
- if (exists($args{'is_strict'})) {
|
|
| 9595 |
- *{$callpkg.'::is_strict'} = \&version::is_strict
|
|
| 9596 |
- unless defined(&{$callpkg.'::is_strict'});
|
|
| 9597 |
- } |
|
| 10474 |
+PARSE_CPAN_META |
|
| 10475 |
+ |
|
| 10476 |
+$fatpacked{"lib/core/only.pm"} = <<'LIB_CORE_ONLY';
|
|
| 10477 |
+ package lib::core::only; |
|
| 9598 | 10478 |
|
| 9599 |
- if (exists($args{'is_lax'})) {
|
|
| 9600 |
- *{$callpkg.'::is_lax'} = \&version::is_lax
|
|
| 9601 |
- unless defined(&{$callpkg.'::is_lax'});
|
|
| 9602 |
- } |
|
| 9603 |
- } |
|
| 10479 |
+ use strict; |
|
| 10480 |
+ use warnings FATAL => 'all'; |
|
| 10481 |
+ use Config; |
|
| 9604 | 10482 |
|
| 9605 |
- sub is_strict { defined $_[0] && $_[0] =~ qr/ \A $STRICT \z /x }
|
|
| 9606 |
- sub is_lax { defined $_[0] && $_[0] =~ qr/ \A $LAX \z /x }
|
|
| 10483 |
+ sub import {
|
|
| 10484 |
+ @INC = @Config{qw(privlibexp archlibexp)};
|
|
| 10485 |
+ return |
|
| 10486 |
+ } |
|
| 9607 | 10487 |
|
| 9608 | 10488 |
1; |
| 9609 |
-VERSION |
|
| 10489 |
+LIB_CORE_ONLY |
|
| 9610 | 10490 |
|
| 9611 |
-$fatpacked{"Version/Requirements.pm"} = <<'VERSION_REQUIREMENTS';
|
|
| 10491 |
+$fatpacked{"local/lib.pm"} = <<'LOCAL_LIB';
|
|
| 9612 | 10492 |
use strict; |
| 9613 | 10493 |
use warnings; |
| 9614 |
- package Version::Requirements; |
|
| 9615 |
- BEGIN {
|
|
| 9616 |
- $Version::Requirements::VERSION = '0.101020'; |
|
| 9617 |
- } |
|
| 9618 |
- # ABSTRACT: a set of version requirements for a CPAN dist |
|
| 9619 | 10494 |
|
| 10495 |
+ package local::lib; |
|
| 9620 | 10496 |
|
| 9621 |
- use Carp (); |
|
| 9622 |
- use Scalar::Util (); |
|
| 9623 |
- use version 0.77 (); # the ->parse method |
|
| 10497 |
+ use 5.008001; # probably works with earlier versions but I'm not supporting them |
|
| 10498 |
+ # (patches would, of course, be welcome) |
|
| 9624 | 10499 |
|
| 10500 |
+ use File::Spec (); |
|
| 10501 |
+ use File::Path (); |
|
| 10502 |
+ use Config; |
|
| 9625 | 10503 |
|
| 9626 |
- sub new {
|
|
| 9627 |
- my ($class) = @_; |
|
| 9628 |
- return bless {} => $class;
|
|
| 9629 |
- } |
|
| 10504 |
+ our $VERSION = '1.008009'; # 1.8.9 |
|
| 9630 | 10505 |
|
| 9631 |
- sub _version_object {
|
|
| 9632 |
- my ($self, $version) = @_; |
|
| 10506 |
+ our @KNOWN_FLAGS = qw(--self-contained --deactivate --deactivate-all); |
|
| 9633 | 10507 |
|
| 9634 |
- $version = (! defined $version) ? version->parse(0) |
|
| 9635 |
- : (! Scalar::Util::blessed($version)) ? version->parse($version) |
|
| 9636 |
- : $version; |
|
| 10508 |
+ sub DEACTIVATE_ONE () { 1 }
|
|
| 10509 |
+ sub DEACTIVATE_ALL () { 2 }
|
|
| 9637 | 10510 |
|
| 9638 |
- return $version; |
|
| 9639 |
- } |
|
| 10511 |
+ sub INTERPOLATE_ENV () { 1 }
|
|
| 10512 |
+ sub LITERAL_ENV () { 0 }
|
|
| 9640 | 10513 |
|
| 10514 |
+ sub import {
|
|
| 10515 |
+ my ($class, @args) = @_; |
|
| 9641 | 10516 |
|
| 9642 |
- BEGIN {
|
|
| 9643 |
- for my $type (qw(minimum maximum exclusion exact_version)) {
|
|
| 9644 |
- my $method = "with_$type"; |
|
| 9645 |
- my $to_add = $type eq 'exact_version' ? $type : "add_$type"; |
|
| 10517 |
+ # Remember what PERL5LIB was when we started |
|
| 10518 |
+ my $perl5lib = $ENV{PERL5LIB} || '';
|
|
| 9646 | 10519 |
|
| 9647 |
- my $code = sub {
|
|
| 9648 |
- my ($self, $name, $version) = @_; |
|
| 10520 |
+ my %arg_store; |
|
| 10521 |
+ for my $arg (@args) {
|
|
| 10522 |
+ # check for lethal dash first to stop processing before causing problems |
|
| 10523 |
+ if ($arg =~ /−/) {
|
|
| 10524 |
+ die <<'DEATH'; |
|
| 10525 |
+ WHOA THERE! It looks like you've got some fancy dashes in your commandline! |
|
| 10526 |
+ These are *not* the traditional -- dashes that software recognizes. You |
|
| 10527 |
+ probably got these by copy-pasting from the perldoc for this module as |
|
| 10528 |
+ rendered by a UTF8-capable formatter. This most typically happens on an OS X |
|
| 10529 |
+ terminal, but can happen elsewhere too. Please try again after replacing the |
|
| 10530 |
+ dashes with normal minus signs. |
|
| 10531 |
+ DEATH |
|
| 10532 |
+ } |
|
| 10533 |
+ elsif(grep { $arg eq $_ } @KNOWN_FLAGS) {
|
|
| 10534 |
+ (my $flag = $arg) =~ s/--//; |
|
| 10535 |
+ $arg_store{$flag} = 1;
|
|
| 10536 |
+ } |
|
| 10537 |
+ elsif($arg =~ /^--/) {
|
|
| 10538 |
+ die "Unknown import argument: $arg"; |
|
| 10539 |
+ } |
|
| 10540 |
+ else {
|
|
| 10541 |
+ # assume that what's left is a path |
|
| 10542 |
+ $arg_store{path} = $arg;
|
|
| 10543 |
+ } |
|
| 10544 |
+ } |
|
| 9649 | 10545 |
|
| 9650 |
- $version = $self->_version_object( $version ); |
|
| 10546 |
+ if($arg_store{'self-contained'}) {
|
|
| 10547 |
+ die "FATAL: The local::lib --self-contained flag has never worked reliably and the original author, Mark Stosberg, was unable or unwilling to maintain it. As such, this flag has been removed from the local::lib codebase in order to prevent misunderstandings and potentially broken builds. The local::lib authors recommend that you look at the lib::core::only module shipped with this distribution in order to create a more robust environment that is equivalent to what --self-contained provided (although quite possibly not what you originally thought it provided due to the poor quality of the documentation, for which we apologise).\n"; |
|
| 10548 |
+ } |
|
| 9651 | 10549 |
|
| 9652 |
- $self->__modify_entry_for($name, $method, $version); |
|
| 10550 |
+ my $deactivating = 0; |
|
| 10551 |
+ if ($arg_store{deactivate}) {
|
|
| 10552 |
+ $deactivating = DEACTIVATE_ONE; |
|
| 10553 |
+ } |
|
| 10554 |
+ if ($arg_store{'deactivate-all'}) {
|
|
| 10555 |
+ $deactivating = DEACTIVATE_ALL; |
|
| 10556 |
+ } |
|
| 9653 | 10557 |
|
| 9654 |
- return $self; |
|
| 9655 |
- }; |
|
| 9656 |
- |
|
| 9657 |
- no strict 'refs'; |
|
| 9658 |
- *$to_add = $code; |
|
| 10558 |
+ $arg_store{path} = $class->resolve_path($arg_store{path});
|
|
| 10559 |
+ $class->setup_local_lib_for($arg_store{path}, $deactivating);
|
|
| 10560 |
+ |
|
| 10561 |
+ for (@INC) { # Untaint @INC
|
|
| 10562 |
+ next if ref; # Skip entry if it is an ARRAY, CODE, blessed, etc. |
|
| 10563 |
+ m/(.*)/ and $_ = $1; |
|
| 9659 | 10564 |
} |
| 9660 | 10565 |
} |
| 9661 | 10566 |
|
| 10567 |
+ sub pipeline; |
|
| 9662 | 10568 |
|
| 9663 |
- sub add_requirements {
|
|
| 9664 |
- my ($self, $req) = @_; |
|
| 9665 |
- |
|
| 9666 |
- for my $module ($req->required_modules) {
|
|
| 9667 |
- my $modifiers = $req->__entry_for($module)->as_modifiers; |
|
| 9668 |
- for my $modifier (@$modifiers) {
|
|
| 9669 |
- my ($method, @args) = @$modifier; |
|
| 9670 |
- $self->$method($module => @args); |
|
| 10569 |
+ sub pipeline {
|
|
| 10570 |
+ my @methods = @_; |
|
| 10571 |
+ my $last = pop(@methods); |
|
| 10572 |
+ if (@methods) {
|
|
| 10573 |
+ \sub {
|
|
| 10574 |
+ my ($obj, @args) = @_; |
|
| 10575 |
+ $obj->${pipeline @methods}(
|
|
| 10576 |
+ $obj->$last(@args) |
|
| 10577 |
+ ); |
|
| 10578 |
+ }; |
|
| 10579 |
+ } else {
|
|
| 10580 |
+ \sub {
|
|
| 10581 |
+ shift->$last(@_); |
|
| 9671 | 10582 |
}; |
| 9672 | 10583 |
} |
| 10584 |
+ } |
|
| 9673 | 10585 |
|
| 9674 |
- return $self; |
|
| 10586 |
+ sub _uniq {
|
|
| 10587 |
+ my %seen; |
|
| 10588 |
+ grep { ! $seen{$_}++ } @_;
|
|
| 9675 | 10589 |
} |
| 9676 | 10590 |
|
| 10591 |
+ sub resolve_path {
|
|
| 10592 |
+ my ($class, $path) = @_; |
|
| 10593 |
+ $class->${pipeline qw(
|
|
| 10594 |
+ resolve_relative_path |
|
| 10595 |
+ resolve_home_path |
|
| 10596 |
+ resolve_empty_path |
|
| 10597 |
+ )}($path); |
|
| 10598 |
+ } |
|
| 9677 | 10599 |
|
| 9678 |
- sub accepts_module {
|
|
| 9679 |
- my ($self, $module, $version) = @_; |
|
| 10600 |
+ sub resolve_empty_path {
|
|
| 10601 |
+ my ($class, $path) = @_; |
|
| 10602 |
+ if (defined $path) {
|
|
| 10603 |
+ $path; |
|
| 10604 |
+ } else {
|
|
| 10605 |
+ '~/perl5'; |
|
| 10606 |
+ } |
|
| 10607 |
+ } |
|
| 9680 | 10608 |
|
| 9681 |
- $version = $self->_version_object( $version ); |
|
| 10609 |
+ sub resolve_home_path {
|
|
| 10610 |
+ my ($class, $path) = @_; |
|
| 10611 |
+ return $path unless ($path =~ /^~/); |
|
| 10612 |
+ my ($user) = ($path =~ /^~([^\/]+)/); # can assume ^~ so undef for 'us' |
|
| 10613 |
+ my $tried_file_homedir; |
|
| 10614 |
+ my $homedir = do {
|
|
| 10615 |
+ if (eval { require File::HomeDir } && $File::HomeDir::VERSION >= 0.65) {
|
|
| 10616 |
+ $tried_file_homedir = 1; |
|
| 10617 |
+ if (defined $user) {
|
|
| 10618 |
+ File::HomeDir->users_home($user); |
|
| 10619 |
+ } else {
|
|
| 10620 |
+ File::HomeDir->my_home; |
|
| 10621 |
+ } |
|
| 10622 |
+ } else {
|
|
| 10623 |
+ if (defined $user) {
|
|
| 10624 |
+ (getpwnam $user)[7]; |
|
| 10625 |
+ } else {
|
|
| 10626 |
+ if (defined $ENV{HOME}) {
|
|
| 10627 |
+ $ENV{HOME};
|
|
| 10628 |
+ } else {
|
|
| 10629 |
+ (getpwuid $<)[7]; |
|
| 10630 |
+ } |
|
| 10631 |
+ } |
|
| 10632 |
+ } |
|
| 10633 |
+ }; |
|
| 10634 |
+ unless (defined $homedir) {
|
|
| 10635 |
+ require Carp; |
|
| 10636 |
+ Carp::croak( |
|
| 10637 |
+ "Couldn't resolve homedir for " |
|
| 10638 |
+ .(defined $user ? $user : 'current user') |
|
| 10639 |
+ .($tried_file_homedir ? '' : ' - consider installing File::HomeDir') |
|
| 10640 |
+ ); |
|
| 10641 |
+ } |
|
| 10642 |
+ $path =~ s/^~[^\/]*/$homedir/; |
|
| 10643 |
+ $path; |
|
| 10644 |
+ } |
|
| 9682 | 10645 |
|
| 9683 |
- return 1 unless my $range = $self->__entry_for($module); |
|
| 9684 |
- return $range->_accepts($version); |
|
| 10646 |
+ sub resolve_relative_path {
|
|
| 10647 |
+ my ($class, $path) = @_; |
|
| 10648 |
+ $path = File::Spec->rel2abs($path); |
|
| 9685 | 10649 |
} |
| 9686 | 10650 |
|
| 10651 |
+ sub setup_local_lib_for {
|
|
| 10652 |
+ my ($class, $path, $deactivating) = @_; |
|
| 9687 | 10653 |
|
| 9688 |
- sub clear_requirement {
|
|
| 9689 |
- my ($self, $module) = @_; |
|
| 10654 |
+ my $interpolate = LITERAL_ENV; |
|
| 10655 |
+ my @active_lls = $class->active_paths; |
|
| 9690 | 10656 |
|
| 9691 |
- return $self unless $self->__entry_for($module); |
|
| 10657 |
+ $class->ensure_dir_structure_for($path); |
|
| 9692 | 10658 |
|
| 9693 |
- Carp::confess("can't clear requirements on finalized requirements")
|
|
| 9694 |
- if $self->is_finalized; |
|
| 10659 |
+ # On Win32 directories often contain spaces. But some parts of the CPAN |
|
| 10660 |
+ # toolchain don't like that. To avoid this, GetShortPathName() gives us |
|
| 10661 |
+ # an alternate representation that has none. |
|
| 10662 |
+ # This only works if the directory already exists. |
|
| 10663 |
+ $path = Win32::GetShortPathName($path) if $^O eq 'MSWin32'; |
|
| 9695 | 10664 |
|
| 9696 |
- delete $self->{requirements}{ $module };
|
|
| 10665 |
+ if (! $deactivating) {
|
|
| 10666 |
+ if (@active_lls && $active_lls[-1] eq $path) {
|
|
| 10667 |
+ exit 0 if $0 eq '-'; |
|
| 10668 |
+ return; # Asked to add what's already at the top of the stack |
|
| 10669 |
+ } elsif (grep { $_ eq $path} @active_lls) {
|
|
| 10670 |
+ # Asked to add a dir that's lower in the stack -- so we remove it from |
|
| 10671 |
+ # where it is, and then add it back at the top. |
|
| 10672 |
+ $class->setup_env_hash_for($path, DEACTIVATE_ONE); |
|
| 10673 |
+ # Which means we can no longer output "PERL5LIB=...:$PERL5LIB" stuff |
|
| 10674 |
+ # anymore because we're taking something *out*. |
|
| 10675 |
+ $interpolate = INTERPOLATE_ENV; |
|
| 10676 |
+ } |
|
| 10677 |
+ } |
|
| 9697 | 10678 |
|
| 9698 |
- return $self; |
|
| 10679 |
+ if ($0 eq '-') {
|
|
| 10680 |
+ $class->print_environment_vars_for($path, $deactivating, $interpolate); |
|
| 10681 |
+ exit 0; |
|
| 10682 |
+ } else {
|
|
| 10683 |
+ $class->setup_env_hash_for($path, $deactivating); |
|
| 10684 |
+ my $arch_dir = $Config{archname};
|
|
| 10685 |
+ @INC = _uniq( |
|
| 10686 |
+ ( |
|
| 10687 |
+ # Inject $path/$archname for each path in PERL5LIB |
|
| 10688 |
+ map { ( File::Spec->catdir($_, $arch_dir), $_ ) }
|
|
| 10689 |
+ split($Config{path_sep}, $ENV{PERL5LIB})
|
|
| 10690 |
+ ), |
|
| 10691 |
+ @INC |
|
| 10692 |
+ ); |
|
| 10693 |
+ } |
|
| 9699 | 10694 |
} |
| 9700 | 10695 |
|
| 10696 |
+ sub install_base_bin_path {
|
|
| 10697 |
+ my ($class, $path) = @_; |
|
| 10698 |
+ File::Spec->catdir($path, 'bin'); |
|
| 10699 |
+ } |
|
| 9701 | 10700 |
|
| 9702 |
- sub required_modules { keys %{ $_[0]{requirements} } }
|
|
| 9703 |
- |
|
| 10701 |
+ sub install_base_perl_path {
|
|
| 10702 |
+ my ($class, $path) = @_; |
|
| 10703 |
+ File::Spec->catdir($path, 'lib', 'perl5'); |
|
| 10704 |
+ } |
|
| 9704 | 10705 |
|
| 9705 |
- sub clone {
|
|
| 9706 |
- my ($self) = @_; |
|
| 9707 |
- my $new = (ref $self)->new; |
|
| 10706 |
+ sub install_base_arch_path {
|
|
| 10707 |
+ my ($class, $path) = @_; |
|
| 10708 |
+ File::Spec->catdir($class->install_base_perl_path($path), $Config{archname});
|
|
| 10709 |
+ } |
|
| 9708 | 10710 |
|
| 9709 |
- return $new->add_requirements($self); |
|
| 10711 |
+ sub ensure_dir_structure_for {
|
|
| 10712 |
+ my ($class, $path) = @_; |
|
| 10713 |
+ unless (-d $path) {
|
|
| 10714 |
+ warn "Attempting to create directory ${path}\n";
|
|
| 10715 |
+ } |
|
| 10716 |
+ File::Path::mkpath($path); |
|
| 10717 |
+ return |
|
| 9710 | 10718 |
} |
| 9711 | 10719 |
|
| 9712 |
- sub __entry_for { $_[0]{requirements}{ $_[1] } }
|
|
| 10720 |
+ sub guess_shelltype {
|
|
| 10721 |
+ my $shellbin = 'sh'; |
|
| 10722 |
+ if(defined $ENV{'SHELL'}) {
|
|
| 10723 |
+ my @shell_bin_path_parts = File::Spec->splitpath($ENV{'SHELL'});
|
|
| 10724 |
+ $shellbin = $shell_bin_path_parts[-1]; |
|
| 10725 |
+ } |
|
| 10726 |
+ my $shelltype = do {
|
|
| 10727 |
+ local $_ = $shellbin; |
|
| 10728 |
+ if(/csh/) {
|
|
| 10729 |
+ 'csh' |
|
| 10730 |
+ } else {
|
|
| 10731 |
+ 'bourne' |
|
| 10732 |
+ } |
|
| 10733 |
+ }; |
|
| 9713 | 10734 |
|
| 9714 |
- sub __modify_entry_for {
|
|
| 9715 |
- my ($self, $name, $method, $version) = @_; |
|
| 10735 |
+ # Both Win32 and Cygwin have $ENV{COMSPEC} set.
|
|
| 10736 |
+ if (defined $ENV{'COMSPEC'} && $^O ne 'cygwin') {
|
|
| 10737 |
+ my @shell_bin_path_parts = File::Spec->splitpath($ENV{'COMSPEC'});
|
|
| 10738 |
+ $shellbin = $shell_bin_path_parts[-1]; |
|
| 10739 |
+ $shelltype = do {
|
|
| 10740 |
+ local $_ = $shellbin; |
|
| 10741 |
+ if(/command\.com/) {
|
|
| 10742 |
+ 'win32' |
|
| 10743 |
+ } elsif(/cmd\.exe/) {
|
|
| 10744 |
+ 'win32' |
|
| 10745 |
+ } elsif(/4nt\.exe/) {
|
|
| 10746 |
+ 'win32' |
|
| 10747 |
+ } else {
|
|
| 10748 |
+ $shelltype |
|
| 10749 |
+ } |
|
| 10750 |
+ }; |
|
| 10751 |
+ } |
|
| 10752 |
+ return $shelltype; |
|
| 10753 |
+ } |
|
| 9716 | 10754 |
|
| 9717 |
- my $fin = $self->is_finalized; |
|
| 9718 |
- my $old = $self->__entry_for($name); |
|
| 10755 |
+ sub print_environment_vars_for {
|
|
| 10756 |
+ my ($class, $path, $deactivating, $interpolate) = @_; |
|
| 10757 |
+ print $class->environment_vars_string_for($path, $deactivating, $interpolate); |
|
| 10758 |
+ } |
|
| 9719 | 10759 |
|
| 9720 |
- Carp::confess("can't add new requirements to finalized requirements")
|
|
| 9721 |
- if $fin and not $old; |
|
| 10760 |
+ sub environment_vars_string_for {
|
|
| 10761 |
+ my ($class, $path, $deactivating, $interpolate) = @_; |
|
| 10762 |
+ my @envs = $class->build_environment_vars_for($path, $deactivating, $interpolate); |
|
| 10763 |
+ my $out = ''; |
|
| 9722 | 10764 |
|
| 9723 |
- my $new = ($old || 'Version::Requirements::_Range::Range') |
|
| 9724 |
- ->$method($version); |
|
| 10765 |
+ # rather basic csh detection, goes on the assumption that something won't |
|
| 10766 |
+ # call itself csh unless it really is. also, default to bourne in the |
|
| 10767 |
+ # pathological situation where a user doesn't have $ENV{SHELL} defined.
|
|
| 10768 |
+ # note also that shells with funny names, like zoid, are assumed to be |
|
| 10769 |
+ # bourne. |
|
| 9725 | 10770 |
|
| 9726 |
- Carp::confess("can't modify finalized requirements")
|
|
| 9727 |
- if $fin and $old->as_string ne $new->as_string; |
|
| 10771 |
+ my $shelltype = $class->guess_shelltype; |
|
| 9728 | 10772 |
|
| 9729 |
- $self->{requirements}{ $name } = $new;
|
|
| 10773 |
+ while (@envs) {
|
|
| 10774 |
+ my ($name, $value) = (shift(@envs), shift(@envs)); |
|
| 10775 |
+ $value =~ s/(\\")/\\$1/g if defined $value; |
|
| 10776 |
+ $out .= $class->${\"build_${shelltype}_env_declaration"}($name, $value);
|
|
| 10777 |
+ } |
|
| 10778 |
+ return $out; |
|
| 9730 | 10779 |
} |
| 9731 | 10780 |
|
| 10781 |
+ # simple routines that take two arguments: an %ENV key and a value. return |
|
| 10782 |
+ # strings that are suitable for passing directly to the relevant shell to set |
|
| 10783 |
+ # said key to said value. |
|
| 10784 |
+ sub build_bourne_env_declaration {
|
|
| 10785 |
+ my $class = shift; |
|
| 10786 |
+ my($name, $value) = @_; |
|
| 10787 |
+ return defined($value) ? qq{export ${name}="${value}";\n} : qq{unset ${name};\n};
|
|
| 10788 |
+ } |
|
| 9732 | 10789 |
|
| 9733 |
- sub is_simple {
|
|
| 9734 |
- my ($self) = @_; |
|
| 9735 |
- for my $module ($self->required_modules) {
|
|
| 9736 |
- # XXX: This is a complete hack, but also entirely correct. |
|
| 9737 |
- return if $self->__entry_for($module)->as_string =~ /\s/; |
|
| 9738 |
- } |
|
| 9739 |
- |
|
| 9740 |
- return 1; |
|
| 10790 |
+ sub build_csh_env_declaration {
|
|
| 10791 |
+ my $class = shift; |
|
| 10792 |
+ my($name, $value) = @_; |
|
| 10793 |
+ return defined($value) ? qq{setenv ${name} "${value}"\n} : qq{unsetenv ${name}\n};
|
|
| 9741 | 10794 |
} |
| 9742 | 10795 |
|
| 10796 |
+ sub build_win32_env_declaration {
|
|
| 10797 |
+ my $class = shift; |
|
| 10798 |
+ my($name, $value) = @_; |
|
| 10799 |
+ return defined($value) ? qq{set ${name}=${value}\n} : qq{set ${name}=\n};
|
|
| 10800 |
+ } |
|
| 9743 | 10801 |
|
| 9744 |
- sub is_finalized { $_[0]{finalized} }
|
|
| 10802 |
+ sub setup_env_hash_for {
|
|
| 10803 |
+ my ($class, $path, $deactivating) = @_; |
|
| 10804 |
+ my %envs = $class->build_environment_vars_for($path, $deactivating, INTERPOLATE_ENV); |
|
| 10805 |
+ @ENV{keys %envs} = values %envs;
|
|
| 10806 |
+ } |
|
| 9745 | 10807 |
|
| 10808 |
+ sub build_environment_vars_for {
|
|
| 10809 |
+ my ($class, $path, $deactivating, $interpolate) = @_; |
|
| 9746 | 10810 |
|
| 9747 |
- sub finalize { $_[0]{finalized} = 1 }
|
|
| 10811 |
+ if ($deactivating == DEACTIVATE_ONE) {
|
|
| 10812 |
+ return $class->build_deactivate_environment_vars_for($path, $interpolate); |
|
| 10813 |
+ } elsif ($deactivating == DEACTIVATE_ALL) {
|
|
| 10814 |
+ return $class->build_deact_all_environment_vars_for($path, $interpolate); |
|
| 10815 |
+ } else {
|
|
| 10816 |
+ return $class->build_activate_environment_vars_for($path, $interpolate); |
|
| 10817 |
+ } |
|
| 10818 |
+ } |
|
| 9748 | 10819 |
|
| 10820 |
+ # Build an environment value for a variable like PATH from a list of paths. |
|
| 10821 |
+ # References to existing variables are given as references to the variable name. |
|
| 10822 |
+ # Duplicates are removed. |
|
| 10823 |
+ # |
|
| 10824 |
+ # options: |
|
| 10825 |
+ # - interpolate: INTERPOLATE_ENV/LITERAL_ENV |
|
| 10826 |
+ # - exists: paths are included only if they exist (default: interpolate == INTERPOLATE_ENV) |
|
| 10827 |
+ # - filter: function to apply to each path do decide if it must be included |
|
| 10828 |
+ # - empty: the value to return in the case of empty value |
|
| 10829 |
+ my %ENV_LIST_VALUE_DEFAULTS = ( |
|
| 10830 |
+ interpolate => INTERPOLATE_ENV, |
|
| 10831 |
+ exists => undef, |
|
| 10832 |
+ filter => sub { 1 },
|
|
| 10833 |
+ empty => undef, |
|
| 10834 |
+ ); |
|
| 10835 |
+ sub _env_list_value {
|
|
| 10836 |
+ my $options = shift; |
|
| 10837 |
+ die(sprintf "unknown option '$_' at %s line %u\n", (caller)[1..2]) |
|
| 10838 |
+ for grep { !exists $ENV_LIST_VALUE_DEFAULTS{$_} } keys %$options;
|
|
| 10839 |
+ my %options = (%ENV_LIST_VALUE_DEFAULTS, %{ $options });
|
|
| 10840 |
+ $options{exists} = $options{interpolate} == INTERPOLATE_ENV
|
|
| 10841 |
+ unless defined $options{exists};
|
|
| 10842 |
+ |
|
| 10843 |
+ my %seen; |
|
| 10844 |
+ |
|
| 10845 |
+ my $value = join($Config{path_sep}, map {
|
|
| 10846 |
+ ref $_ ? ($^O eq 'MSWin32' ? "%${$_}%" : "\$${$_}") : $_
|
|
| 10847 |
+ } grep {
|
|
| 10848 |
+ ref $_ || (defined $_ |
|
| 10849 |
+ && length($_) > 0 |
|
| 10850 |
+ && !$seen{$_}++
|
|
| 10851 |
+ && $options{filter}->($_)
|
|
| 10852 |
+ && (!$options{exists} || -e $_))
|
|
| 10853 |
+ } map {
|
|
| 10854 |
+ if (ref $_ eq 'SCALAR' && $options{interpolate} == INTERPOLATE_ENV) {
|
|
| 10855 |
+ exists $ENV{${$_}} ? (split /\Q$Config{path_sep}/, $ENV{${$_}}) : ()
|
|
| 10856 |
+ } else {
|
|
| 10857 |
+ $_ |
|
| 10858 |
+ } |
|
| 10859 |
+ } @_); |
|
| 10860 |
+ return length($value) ? $value : $options{empty};
|
|
| 10861 |
+ } |
|
| 9749 | 10862 |
|
| 9750 |
- sub as_string_hash {
|
|
| 9751 |
- my ($self) = @_; |
|
| 10863 |
+ sub build_activate_environment_vars_for {
|
|
| 10864 |
+ my ($class, $path, $interpolate) = @_; |
|
| 10865 |
+ return ( |
|
| 10866 |
+ PERL_LOCAL_LIB_ROOT => |
|
| 10867 |
+ _env_list_value( |
|
| 10868 |
+ { interpolate => $interpolate, exists => 0, empty => '' },
|
|
| 10869 |
+ \'PERL_LOCAL_LIB_ROOT', |
|
| 10870 |
+ $path, |
|
| 10871 |
+ ), |
|
| 10872 |
+ PERL_MB_OPT => "--install_base ${path}",
|
|
| 10873 |
+ PERL_MM_OPT => "INSTALL_BASE=${path}",
|
|
| 10874 |
+ PERL5LIB => |
|
| 10875 |
+ _env_list_value( |
|
| 10876 |
+ { interpolate => $interpolate, exists => 0, empty => '' },
|
|
| 10877 |
+ $class->install_base_perl_path($path), |
|
| 10878 |
+ \'PERL5LIB', |
|
| 10879 |
+ ), |
|
| 10880 |
+ PATH => _env_list_value( |
|
| 10881 |
+ { interpolate => $interpolate, exists => 0, empty => '' },
|
|
| 10882 |
+ $class->install_base_bin_path($path), |
|
| 10883 |
+ \'PATH', |
|
| 10884 |
+ ), |
|
| 10885 |
+ ) |
|
| 10886 |
+ } |
|
| 9752 | 10887 |
|
| 9753 |
- my %hash = map {; $_ => $self->{requirements}{$_}->as_string }
|
|
| 9754 |
- $self->required_modules; |
|
| 10888 |
+ sub active_paths {
|
|
| 10889 |
+ my ($class) = @_; |
|
| 9755 | 10890 |
|
| 9756 |
- return \%hash; |
|
| 10891 |
+ return () unless defined $ENV{PERL_LOCAL_LIB_ROOT};
|
|
| 10892 |
+ return grep { $_ ne '' } split /\Q$Config{path_sep}/, $ENV{PERL_LOCAL_LIB_ROOT};
|
|
| 9757 | 10893 |
} |
| 9758 | 10894 |
|
| 10895 |
+ sub build_deactivate_environment_vars_for {
|
|
| 10896 |
+ my ($class, $path, $interpolate) = @_; |
|
| 9759 | 10897 |
|
| 9760 |
- my %methods_for_op = ( |
|
| 9761 |
- '==' => [ qw(exact_version) ], |
|
| 9762 |
- '!=' => [ qw(add_exclusion) ], |
|
| 9763 |
- '>=' => [ qw(add_minimum) ], |
|
| 9764 |
- '<=' => [ qw(add_maximum) ], |
|
| 9765 |
- '>' => [ qw(add_minimum add_exclusion) ], |
|
| 9766 |
- '<' => [ qw(add_maximum add_exclusion) ], |
|
| 9767 |
- ); |
|
| 10898 |
+ my @active_lls = $class->active_paths; |
|
| 9768 | 10899 |
|
| 9769 |
- sub from_string_hash {
|
|
| 9770 |
- my ($class, $hash) = @_; |
|
| 10900 |
+ if (!grep { $_ eq $path } @active_lls) {
|
|
| 10901 |
+ warn "Tried to deactivate inactive local::lib '$path'\n"; |
|
| 10902 |
+ return (); |
|
| 10903 |
+ } |
|
| 9771 | 10904 |
|
| 9772 |
- my $self = $class->new; |
|
| 10905 |
+ my $perl_path = $class->install_base_perl_path($path); |
|
| 10906 |
+ my $arch_path = $class->install_base_arch_path($path); |
|
| 10907 |
+ my $bin_path = $class->install_base_bin_path($path); |
|
| 9773 | 10908 |
|
| 9774 |
- for my $module (keys %$hash) {
|
|
| 9775 |
- my @parts = split qr{\s*,\s*}, $hash->{ $module };
|
|
| 9776 |
- for my $part (@parts) {
|
|
| 9777 |
- my ($op, $ver) = split /\s+/, $part, 2; |
|
| 9778 | 10909 |
|
| 9779 |
- if (! defined $ver) {
|
|
| 9780 |
- $self->add_minimum($module => $op); |
|
| 9781 |
- } else {
|
|
| 9782 |
- Carp::confess("illegal requirement string: $hash->{ $module }")
|
|
| 9783 |
- unless my $methods = $methods_for_op{ $op };
|
|
| 10910 |
+ my %env = ( |
|
| 10911 |
+ PERL_LOCAL_LIB_ROOT => _env_list_value( |
|
| 10912 |
+ {
|
|
| 10913 |
+ exists => 0, |
|
| 10914 |
+ }, |
|
| 10915 |
+ grep { $_ ne $path } @active_lls
|
|
| 10916 |
+ ), |
|
| 10917 |
+ PERL5LIB => _env_list_value( |
|
| 10918 |
+ {
|
|
| 10919 |
+ exists => 0, |
|
| 10920 |
+ filter => sub {
|
|
| 10921 |
+ $_ ne $perl_path && $_ ne $arch_path |
|
| 10922 |
+ }, |
|
| 10923 |
+ }, |
|
| 10924 |
+ \'PERL5LIB', |
|
| 10925 |
+ ), |
|
| 10926 |
+ PATH => _env_list_value( |
|
| 10927 |
+ {
|
|
| 10928 |
+ exists => 0, |
|
| 10929 |
+ filter => sub { $_ ne $bin_path },
|
|
| 10930 |
+ }, |
|
| 10931 |
+ \'PATH', |
|
| 10932 |
+ ), |
|
| 10933 |
+ ); |
|
| 9784 | 10934 |
|
| 9785 |
- $self->$_($module => $ver) for @$methods; |
|
| 9786 |
- } |
|
| 9787 |
- } |
|
| 10935 |
+ # If removing ourselves from the "top of the stack", set install paths to |
|
| 10936 |
+ # correspond with the new top of stack. |
|
| 10937 |
+ if ($active_lls[-1] eq $path) {
|
|
| 10938 |
+ my $new_top = $active_lls[-2]; |
|
| 10939 |
+ $env{PERL_MB_OPT} = defined($new_top) ? "--install_base ${new_top}" : undef;
|
|
| 10940 |
+ $env{PERL_MM_OPT} = defined($new_top) ? "INSTALL_BASE=${new_top}" : undef;
|
|
| 9788 | 10941 |
} |
| 9789 | 10942 |
|
| 9790 |
- return $self; |
|
| 10943 |
+ return %env; |
|
| 9791 | 10944 |
} |
| 9792 | 10945 |
|
| 9793 |
- ############################################################## |
|
| 10946 |
+ sub build_deact_all_environment_vars_for {
|
|
| 10947 |
+ my ($class, $path, $interpolate) = @_; |
|
| 9794 | 10948 |
|
| 9795 |
- {
|
|
| 9796 |
- package |
|
| 9797 |
- Version::Requirements::_Range::Exact; |
|
| 9798 |
- BEGIN {
|
|
| 9799 |
- $Version::Requirements::_Range::Exact::VERSION = '0.101020'; |
|
| 10949 |
+ my @active_lls = $class->active_paths; |
|
| 10950 |
+ |
|
| 10951 |
+ my %perl_paths = map { (
|
|
| 10952 |
+ $class->install_base_perl_path($_) => 1, |
|
| 10953 |
+ $class->install_base_arch_path($_) => 1 |
|
| 10954 |
+ ) } @active_lls; |
|
| 10955 |
+ my %bin_paths = map { (
|
|
| 10956 |
+ $class->install_base_bin_path($_) => 1, |
|
| 10957 |
+ ) } @active_lls; |
|
| 10958 |
+ |
|
| 10959 |
+ my %env = ( |
|
| 10960 |
+ PERL_LOCAL_LIB_ROOT => undef, |
|
| 10961 |
+ PERL_MM_OPT => undef, |
|
| 10962 |
+ PERL_MB_OPT => undef, |
|
| 10963 |
+ PERL5LIB => _env_list_value( |
|
| 10964 |
+ {
|
|
| 10965 |
+ exists => 0, |
|
| 10966 |
+ filter => sub {
|
|
| 10967 |
+ ! scalar grep { exists $perl_paths{$_} } $_[0]
|
|
| 10968 |
+ }, |
|
| 10969 |
+ }, |
|
| 10970 |
+ \'PERL5LIB' |
|
| 10971 |
+ ), |
|
| 10972 |
+ PATH => _env_list_value( |
|
| 10973 |
+ {
|
|
| 10974 |
+ exists => 0, |
|
| 10975 |
+ filter => sub {
|
|
| 10976 |
+ ! scalar grep { exists $bin_paths{$_} } $_[0]
|
|
| 10977 |
+ }, |
|
| 10978 |
+ }, |
|
| 10979 |
+ \'PATH' |
|
| 10980 |
+ ), |
|
| 10981 |
+ ); |
|
| 10982 |
+ |
|
| 10983 |
+ return %env; |
|
| 9800 | 10984 |
} |
| 9801 |
- sub _new { bless { version => $_[1] } => $_[0] }
|
|
| 9802 | 10985 |
|
| 9803 |
- sub _accepts { return $_[0]{version} == $_[1] }
|
|
| 10986 |
+ 1; |
|
| 10987 |
+LOCAL_LIB |
|
| 10988 |
+ |
|
| 10989 |
+$fatpacked{"version.pm"} = <<'VERSION';
|
|
| 10990 |
+ #!perl -w |
|
| 10991 |
+ package version; |
|
| 9804 | 10992 |
|
| 9805 |
- sub as_string { return "== $_[0]{version}" }
|
|
| 10993 |
+ use 5.005_04; |
|
| 10994 |
+ use strict; |
|
| 9806 | 10995 |
|
| 9807 |
- sub as_modifiers { return [ [ exact_version => $_[0]{version} ] ] }
|
|
| 10996 |
+ use vars qw(@ISA $VERSION $CLASS $STRICT $LAX *declare *qv); |
|
| 9808 | 10997 |
|
| 9809 |
- sub _clone {
|
|
| 9810 |
- (ref $_[0])->_new( version->new( $_[0]{version} ) )
|
|
| 9811 |
- } |
|
| 10998 |
+ $VERSION = 0.9901; |
|
| 9812 | 10999 |
|
| 9813 |
- sub with_exact_version {
|
|
| 9814 |
- my ($self, $version) = @_; |
|
| 11000 |
+ $CLASS = 'version'; |
|
| 9815 | 11001 |
|
| 9816 |
- return $self->_clone if $self->_accepts($version); |
|
| 11002 |
+ #--------------------------------------------------------------------------# |
|
| 11003 |
+ # Version regexp components |
|
| 11004 |
+ #--------------------------------------------------------------------------# |
|
| 9817 | 11005 |
|
| 9818 |
- Carp::confess("illegal requirements: unequal exact version specified");
|
|
| 9819 |
- } |
|
| 11006 |
+ # Fraction part of a decimal version number. This is a common part of |
|
| 11007 |
+ # both strict and lax decimal versions |
|
| 9820 | 11008 |
|
| 9821 |
- sub with_minimum {
|
|
| 9822 |
- my ($self, $minimum) = @_; |
|
| 9823 |
- return $self->_clone if $self->{version} >= $minimum;
|
|
| 9824 |
- Carp::confess("illegal requirements: minimum above exact specification");
|
|
| 9825 |
- } |
|
| 11009 |
+ my $FRACTION_PART = qr/\.[0-9]+/; |
|
| 9826 | 11010 |
|
| 9827 |
- sub with_maximum {
|
|
| 9828 |
- my ($self, $maximum) = @_; |
|
| 9829 |
- return $self->_clone if $self->{version} <= $maximum;
|
|
| 9830 |
- Carp::confess("illegal requirements: maximum below exact specification");
|
|
| 9831 |
- } |
|
| 11011 |
+ # First part of either decimal or dotted-decimal strict version number. |
|
| 11012 |
+ # Unsigned integer with no leading zeroes (except for zero itself) to |
|
| 11013 |
+ # avoid confusion with octal. |
|
| 9832 | 11014 |
|
| 9833 |
- sub with_exclusion {
|
|
| 9834 |
- my ($self, $exclusion) = @_; |
|
| 9835 |
- return $self->_clone unless $exclusion == $self->{version};
|
|
| 9836 |
- Carp::confess("illegal requirements: excluded exact specification");
|
|
| 9837 |
- } |
|
| 9838 |
- } |
|
| 11015 |
+ my $STRICT_INTEGER_PART = qr/0|[1-9][0-9]*/; |
|
| 9839 | 11016 |
|
| 9840 |
- ############################################################## |
|
| 11017 |
+ # First part of either decimal or dotted-decimal lax version number. |
|
| 11018 |
+ # Unsigned integer, but allowing leading zeros. Always interpreted |
|
| 11019 |
+ # as decimal. However, some forms of the resulting syntax give odd |
|
| 11020 |
+ # results if used as ordinary Perl expressions, due to how perl treats |
|
| 11021 |
+ # octals. E.g. |
|
| 11022 |
+ # version->new("010" ) == 10
|
|
| 11023 |
+ # version->new( 010 ) == 8 |
|
| 11024 |
+ # version->new( 010.2) == 82 # "8" . "2" |
|
| 9841 | 11025 |
|
| 9842 |
- {
|
|
| 9843 |
- package |
|
| 9844 |
- Version::Requirements::_Range::Range; |
|
| 9845 |
- BEGIN {
|
|
| 9846 |
- $Version::Requirements::_Range::Range::VERSION = '0.101020'; |
|
| 9847 |
- } |
|
| 11026 |
+ my $LAX_INTEGER_PART = qr/[0-9]+/; |
|
| 11027 |
+ |
|
| 11028 |
+ # Second and subsequent part of a strict dotted-decimal version number. |
|
| 11029 |
+ # Leading zeroes are permitted, and the number is always decimal. |
|
| 11030 |
+ # Limited to three digits to avoid overflow when converting to decimal |
|
| 11031 |
+ # form and also avoid problematic style with excessive leading zeroes. |
|
| 9848 | 11032 |
|
| 9849 |
- sub _self { ref($_[0]) ? $_[0] : (bless { } => $_[0]) }
|
|
| 11033 |
+ my $STRICT_DOTTED_DECIMAL_PART = qr/\.[0-9]{1,3}/;
|
|
| 9850 | 11034 |
|
| 9851 |
- sub _clone {
|
|
| 9852 |
- return (bless { } => $_[0]) unless ref $_[0];
|
|
| 11035 |
+ # Second and subsequent part of a lax dotted-decimal version number. |
|
| 11036 |
+ # Leading zeroes are permitted, and the number is always decimal. No |
|
| 11037 |
+ # limit on the numerical value or number of digits, so there is the |
|
| 11038 |
+ # possibility of overflow when converting to decimal form. |
|
| 9853 | 11039 |
|
| 9854 |
- my ($s) = @_; |
|
| 9855 |
- my %guts = ( |
|
| 9856 |
- (exists $s->{minimum} ? (minimum => version->new($s->{minimum})) : ()),
|
|
| 9857 |
- (exists $s->{maximum} ? (maximum => version->new($s->{maximum})) : ()),
|
|
| 11040 |
+ my $LAX_DOTTED_DECIMAL_PART = qr/\.[0-9]+/; |
|
| 9858 | 11041 |
|
| 9859 |
- (exists $s->{exclusions}
|
|
| 9860 |
- ? (exclusions => [ map { version->new($_) } @{ $s->{exclusions} } ])
|
|
| 9861 |
- : ()), |
|
| 9862 |
- ); |
|
| 11042 |
+ # Alpha suffix part of lax version number syntax. Acts like a |
|
| 11043 |
+ # dotted-decimal part. |
|
| 9863 | 11044 |
|
| 9864 |
- bless \%guts => ref($s); |
|
| 9865 |
- } |
|
| 11045 |
+ my $LAX_ALPHA_PART = qr/_[0-9]+/; |
|
| 9866 | 11046 |
|
| 9867 |
- sub as_modifiers {
|
|
| 9868 |
- my ($self) = @_; |
|
| 9869 |
- my @mods; |
|
| 9870 |
- push @mods, [ add_minimum => $self->{minimum} ] if exists $self->{minimum};
|
|
| 9871 |
- push @mods, [ add_maximum => $self->{maximum} ] if exists $self->{maximum};
|
|
| 9872 |
- push @mods, map {; [ add_exclusion => $_ ] } @{$self->{exclusions} || []};
|
|
| 9873 |
- return \@mods; |
|
| 9874 |
- } |
|
| 11047 |
+ #--------------------------------------------------------------------------# |
|
| 11048 |
+ # Strict version regexp definitions |
|
| 11049 |
+ #--------------------------------------------------------------------------# |
|
| 9875 | 11050 |
|
| 9876 |
- sub as_string {
|
|
| 9877 |
- my ($self) = @_; |
|
| 11051 |
+ # Strict decimal version number. |
|
| 9878 | 11052 |
|
| 9879 |
- return 0 if ! keys %$self; |
|
| 11053 |
+ my $STRICT_DECIMAL_VERSION = |
|
| 11054 |
+ qr/ $STRICT_INTEGER_PART $FRACTION_PART? /x; |
|
| 9880 | 11055 |
|
| 9881 |
- return "$self->{minimum}" if (keys %$self) == 1 and exists $self->{minimum};
|
|
| 11056 |
+ # Strict dotted-decimal version number. Must have both leading "v" and |
|
| 11057 |
+ # at least three parts, to avoid confusion with decimal syntax. |
|
| 9882 | 11058 |
|
| 9883 |
- my @exclusions = @{ $self->{exclusions} || [] };
|
|
| 11059 |
+ my $STRICT_DOTTED_DECIMAL_VERSION = |
|
| 11060 |
+ qr/ v $STRICT_INTEGER_PART $STRICT_DOTTED_DECIMAL_PART{2,} /x;
|
|
| 9884 | 11061 |
|
| 9885 |
- my @parts; |
|
| 11062 |
+ # Complete strict version number syntax -- should generally be used |
|
| 11063 |
+ # anchored: qr/ \A $STRICT \z /x |
|
| 9886 | 11064 |
|
| 9887 |
- for my $pair ( |
|
| 9888 |
- [ qw( >= > minimum ) ], |
|
| 9889 |
- [ qw( <= < maximum ) ], |
|
| 9890 |
- ) {
|
|
| 9891 |
- my ($op, $e_op, $k) = @$pair; |
|
| 9892 |
- if (exists $self->{$k}) {
|
|
| 9893 |
- my @new_exclusions = grep { $_ != $self->{ $k } } @exclusions;
|
|
| 9894 |
- if (@new_exclusions == @exclusions) {
|
|
| 9895 |
- push @parts, "$op $self->{ $k }";
|
|
| 9896 |
- } else {
|
|
| 9897 |
- push @parts, "$e_op $self->{ $k }";
|
|
| 9898 |
- @exclusions = @new_exclusions; |
|
| 9899 |
- } |
|
| 9900 |
- } |
|
| 9901 |
- } |
|
| 11065 |
+ $STRICT = |
|
| 11066 |
+ qr/ $STRICT_DECIMAL_VERSION | $STRICT_DOTTED_DECIMAL_VERSION /x; |
|
| 9902 | 11067 |
|
| 9903 |
- push @parts, map {; "!= $_" } @exclusions;
|
|
| 11068 |
+ #--------------------------------------------------------------------------# |
|
| 11069 |
+ # Lax version regexp definitions |
|
| 11070 |
+ #--------------------------------------------------------------------------# |
|
| 9904 | 11071 |
|
| 9905 |
- return join q{, }, @parts;
|
|
| 9906 |
- } |
|
| 11072 |
+ # Lax decimal version number. Just like the strict one except for |
|
| 11073 |
+ # allowing an alpha suffix or allowing a leading or trailing |
|
| 11074 |
+ # decimal-point |
|
| 9907 | 11075 |
|
| 9908 |
- sub with_exact_version {
|
|
| 9909 |
- my ($self, $version) = @_; |
|
| 9910 |
- $self = $self->_clone; |
|
| 11076 |
+ my $LAX_DECIMAL_VERSION = |
|
| 11077 |
+ qr/ $LAX_INTEGER_PART (?: \. | $FRACTION_PART $LAX_ALPHA_PART? )? |
|
| 11078 |
+ | |
|
| 11079 |
+ $FRACTION_PART $LAX_ALPHA_PART? |
|
| 11080 |
+ /x; |
|
| 9911 | 11081 |
|
| 9912 |
- Carp::confess("illegal requirements: exact specification outside of range")
|
|
| 9913 |
- unless $self->_accepts($version); |
|
| 11082 |
+ # Lax dotted-decimal version number. Distinguished by having either |
|
| 11083 |
+ # leading "v" or at least three non-alpha parts. Alpha part is only |
|
| 11084 |
+ # permitted if there are at least two non-alpha parts. Strangely |
|
| 11085 |
+ # enough, without the leading "v", Perl takes .1.2 to mean v0.1.2, |
|
| 11086 |
+ # so when there is no "v", the leading part is optional |
|
| 9914 | 11087 |
|
| 9915 |
- return Version::Requirements::_Range::Exact->_new($version); |
|
| 9916 |
- } |
|
| 11088 |
+ my $LAX_DOTTED_DECIMAL_VERSION = |
|
| 11089 |
+ qr/ |
|
| 11090 |
+ v $LAX_INTEGER_PART (?: $LAX_DOTTED_DECIMAL_PART+ $LAX_ALPHA_PART? )? |
|
| 11091 |
+ | |
|
| 11092 |
+ $LAX_INTEGER_PART? $LAX_DOTTED_DECIMAL_PART{2,} $LAX_ALPHA_PART?
|
|
| 11093 |
+ /x; |
|
| 9917 | 11094 |
|
| 9918 |
- sub _simplify {
|
|
| 9919 |
- my ($self) = @_; |
|
| 11095 |
+ # Complete lax version number syntax -- should generally be used |
|
| 11096 |
+ # anchored: qr/ \A $LAX \z /x |
|
| 11097 |
+ # |
|
| 11098 |
+ # The string 'undef' is a special case to make for easier handling |
|
| 11099 |
+ # of return values from ExtUtils::MM->parse_version |
|
| 9920 | 11100 |
|
| 9921 |
- if (defined $self->{minimum} and defined $self->{maximum}) {
|
|
| 9922 |
- if ($self->{minimum} == $self->{maximum}) {
|
|
| 9923 |
- Carp::confess("illegal requirements: excluded all values")
|
|
| 9924 |
- if grep { $_ == $self->{minimum} } @{ $self->{exclusions} || [] };
|
|
| 11101 |
+ $LAX = |
|
| 11102 |
+ qr/ undef | $LAX_DECIMAL_VERSION | $LAX_DOTTED_DECIMAL_VERSION /x; |
|
| 9925 | 11103 |
|
| 9926 |
- return Version::Requirements::_Range::Exact->_new($self->{minimum})
|
|
| 9927 |
- } |
|
| 11104 |
+ #--------------------------------------------------------------------------# |
|
| 9928 | 11105 |
|
| 9929 |
- Carp::confess("illegal requirements: minimum exceeds maximum")
|
|
| 9930 |
- if $self->{minimum} > $self->{maximum};
|
|
| 11106 |
+ {
|
|
| 11107 |
+ local $SIG{'__DIE__'};
|
|
| 11108 |
+ eval "use version::vxs $VERSION"; |
|
| 11109 |
+ if ( $@ ) { # don't have the XS version installed
|
|
| 11110 |
+ eval "use version::vpp $VERSION"; # don't tempt fate |
|
| 11111 |
+ die "$@" if ( $@ ); |
|
| 11112 |
+ push @ISA, "version::vpp"; |
|
| 11113 |
+ local $^W; |
|
| 11114 |
+ *version::qv = \&version::vpp::qv; |
|
| 11115 |
+ *version::declare = \&version::vpp::declare; |
|
| 11116 |
+ *version::_VERSION = \&version::vpp::_VERSION; |
|
| 11117 |
+ *version::vcmp = \&version::vpp::vcmp; |
|
| 11118 |
+ if ($] >= 5.009000) {
|
|
| 11119 |
+ no strict 'refs'; |
|
| 11120 |
+ *version::stringify = \&version::vpp::stringify; |
|
| 11121 |
+ *{'version::(""'} = \&version::vpp::stringify;
|
|
| 11122 |
+ *{'version::(<=>'} = \&version::vpp::vcmp;
|
|
| 11123 |
+ *version::new = \&version::vpp::new; |
|
| 11124 |
+ *version::parse = \&version::vpp::parse; |
|
| 11125 |
+ } |
|
| 9931 | 11126 |
} |
| 11127 |
+ else { # use XS module
|
|
| 11128 |
+ push @ISA, "version::vxs"; |
|
| 11129 |
+ local $^W; |
|
| 11130 |
+ *version::declare = \&version::vxs::declare; |
|
| 11131 |
+ *version::qv = \&version::vxs::qv; |
|
| 11132 |
+ *version::_VERSION = \&version::vxs::_VERSION; |
|
| 11133 |
+ *version::vcmp = \&version::vxs::VCMP; |
|
| 11134 |
+ if ($] >= 5.009000) {
|
|
| 11135 |
+ no strict 'refs'; |
|
| 11136 |
+ *version::stringify = \&version::vxs::stringify; |
|
| 11137 |
+ *{'version::(""'} = \&version::vxs::stringify;
|
|
| 11138 |
+ *{'version::(<=>'} = \&version::vxs::VCMP;
|
|
| 11139 |
+ *version::new = \&version::vxs::new; |
|
| 11140 |
+ *version::parse = \&version::vxs::parse; |
|
| 11141 |
+ } |
|
| 9932 | 11142 |
|
| 9933 |
- # eliminate irrelevant exclusions |
|
| 9934 |
- if ($self->{exclusions}) {
|
|
| 9935 |
- my %seen; |
|
| 9936 |
- @{ $self->{exclusions} } = grep {
|
|
| 9937 |
- (! defined $self->{minimum} or $_ >= $self->{minimum})
|
|
| 9938 |
- and |
|
| 9939 |
- (! defined $self->{maximum} or $_ <= $self->{maximum})
|
|
| 9940 |
- and |
|
| 9941 |
- ! $seen{$_}++
|
|
| 9942 |
- } @{ $self->{exclusions} };
|
|
| 9943 | 11143 |
} |
| 11144 |
+ } |
|
| 9944 | 11145 |
|
| 9945 |
- return $self; |
|
| 9946 |
- } |
|
| 9947 |
- |
|
| 9948 |
- sub with_minimum {
|
|
| 9949 |
- my ($self, $minimum) = @_; |
|
| 9950 |
- $self = $self->_clone; |
|
| 11146 |
+ # Preloaded methods go here. |
|
| 11147 |
+ sub import {
|
|
| 11148 |
+ no strict 'refs'; |
|
| 11149 |
+ my ($class) = shift; |
|
| 9951 | 11150 |
|
| 9952 |
- if (defined (my $old_min = $self->{minimum})) {
|
|
| 9953 |
- $self->{minimum} = (sort { $b cmp $a } ($minimum, $old_min))[0];
|
|
| 9954 |
- } else {
|
|
| 9955 |
- $self->{minimum} = $minimum;
|
|
| 11151 |
+ # Set up any derived class |
|
| 11152 |
+ unless ($class eq 'version') {
|
|
| 11153 |
+ local $^W; |
|
| 11154 |
+ *{$class.'::declare'} = \&version::declare;
|
|
| 11155 |
+ *{$class.'::qv'} = \&version::qv;
|
|
| 9956 | 11156 |
} |
| 9957 | 11157 |
|
| 9958 |
- return $self->_simplify; |
|
| 9959 |
- } |
|
| 9960 |
- |
|
| 9961 |
- sub with_maximum {
|
|
| 9962 |
- my ($self, $maximum) = @_; |
|
| 9963 |
- $self = $self->_clone; |
|
| 9964 |
- |
|
| 9965 |
- if (defined (my $old_max = $self->{maximum})) {
|
|
| 9966 |
- $self->{maximum} = (sort { $a cmp $b } ($maximum, $old_max))[0];
|
|
| 9967 |
- } else {
|
|
| 9968 |
- $self->{maximum} = $maximum;
|
|
| 11158 |
+ my %args; |
|
| 11159 |
+ if (@_) { # any remaining terms are arguments
|
|
| 11160 |
+ map { $args{$_} = 1 } @_
|
|
| 11161 |
+ } |
|
| 11162 |
+ else { # no parameters at all on use line
|
|
| 11163 |
+ %args = |
|
| 11164 |
+ ( |
|
| 11165 |
+ qv => 1, |
|
| 11166 |
+ 'UNIVERSAL::VERSION' => 1, |
|
| 11167 |
+ ); |
|
| 9969 | 11168 |
} |
| 9970 | 11169 |
|
| 9971 |
- return $self->_simplify; |
|
| 9972 |
- } |
|
| 9973 |
- |
|
| 9974 |
- sub with_exclusion {
|
|
| 9975 |
- my ($self, $exclusion) = @_; |
|
| 9976 |
- $self = $self->_clone; |
|
| 11170 |
+ my $callpkg = caller(); |
|
| 11171 |
+ |
|
| 11172 |
+ if (exists($args{declare})) {
|
|
| 11173 |
+ *{$callpkg.'::declare'} =
|
|
| 11174 |
+ sub {return $class->declare(shift) }
|
|
| 11175 |
+ unless defined(&{$callpkg.'::declare'});
|
|
| 11176 |
+ } |
|
| 9977 | 11177 |
|
| 9978 |
- push @{ $self->{exclusions} ||= [] }, $exclusion;
|
|
| 11178 |
+ if (exists($args{qv})) {
|
|
| 11179 |
+ *{$callpkg.'::qv'} =
|
|
| 11180 |
+ sub {return $class->qv(shift) }
|
|
| 11181 |
+ unless defined(&{$callpkg.'::qv'});
|
|
| 11182 |
+ } |
|
| 9979 | 11183 |
|
| 9980 |
- return $self->_simplify; |
|
| 9981 |
- } |
|
| 11184 |
+ if (exists($args{'UNIVERSAL::VERSION'})) {
|
|
| 11185 |
+ local $^W; |
|
| 11186 |
+ *UNIVERSAL::VERSION |
|
| 11187 |
+ = \&version::_VERSION; |
|
| 11188 |
+ } |
|
| 9982 | 11189 |
|
| 9983 |
- sub _accepts {
|
|
| 9984 |
- my ($self, $version) = @_; |
|
| 11190 |
+ if (exists($args{'VERSION'})) {
|
|
| 11191 |
+ *{$callpkg.'::VERSION'} = \&version::_VERSION;
|
|
| 11192 |
+ } |
|
| 9985 | 11193 |
|
| 9986 |
- return if defined $self->{minimum} and $version < $self->{minimum};
|
|
| 9987 |
- return if defined $self->{maximum} and $version > $self->{maximum};
|
|
| 9988 |
- return if defined $self->{exclusions}
|
|
| 9989 |
- and grep { $version == $_ } @{ $self->{exclusions} };
|
|
| 11194 |
+ if (exists($args{'is_strict'})) {
|
|
| 11195 |
+ *{$callpkg.'::is_strict'} = \&version::is_strict
|
|
| 11196 |
+ unless defined(&{$callpkg.'::is_strict'});
|
|
| 11197 |
+ } |
|
| 9990 | 11198 |
|
| 9991 |
- return 1; |
|
| 9992 |
- } |
|
| 11199 |
+ if (exists($args{'is_lax'})) {
|
|
| 11200 |
+ *{$callpkg.'::is_lax'} = \&version::is_lax
|
|
| 11201 |
+ unless defined(&{$callpkg.'::is_lax'});
|
|
| 11202 |
+ } |
|
| 9993 | 11203 |
} |
| 9994 | 11204 |
|
| 9995 |
- 1; |
|
| 9996 |
- |
|
| 9997 |
- __END__ |
|
| 9998 |
- =pod |
|
| 11205 |
+ sub is_strict { defined $_[0] && $_[0] =~ qr/ \A $STRICT \z /x }
|
|
| 11206 |
+ sub is_lax { defined $_[0] && $_[0] =~ qr/ \A $LAX \z /x }
|
|
| 9999 | 11207 |
|
| 10000 |
-VERSION_REQUIREMENTS |
|
| 11208 |
+ 1; |
|
| 11209 |
+VERSION |
|
| 10001 | 11210 |
|
| 10002 | 11211 |
$fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10003 | 11212 |
package charstar; |
| ... | ... |
@@ -10123,7 +11332,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10123 | 11332 |
use POSIX qw/locale_h/; |
| 10124 | 11333 |
use locale; |
| 10125 | 11334 |
use vars qw ($VERSION @ISA @REGEXS); |
| 10126 |
- $VERSION = 0.88; |
|
| 11335 |
+ $VERSION = 0.9901; |
|
| 10127 | 11336 |
|
| 10128 | 11337 |
use overload ( |
| 10129 | 11338 |
'""' => \&stringify, |
| ... | ... |
@@ -10131,13 +11340,22 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10131 | 11340 |
'cmp' => \&vcmp, |
| 10132 | 11341 |
'<=>' => \&vcmp, |
| 10133 | 11342 |
'bool' => \&vbool, |
| 10134 |
- 'nomethod' => \&vnoop, |
|
| 11343 |
+ '+' => \&vnoop, |
|
| 11344 |
+ '-' => \&vnoop, |
|
| 11345 |
+ '*' => \&vnoop, |
|
| 11346 |
+ '/' => \&vnoop, |
|
| 11347 |
+ '+=' => \&vnoop, |
|
| 11348 |
+ '-=' => \&vnoop, |
|
| 11349 |
+ '*=' => \&vnoop, |
|
| 11350 |
+ '/=' => \&vnoop, |
|
| 11351 |
+ 'abs' => \&vnoop, |
|
| 10135 | 11352 |
); |
| 10136 | 11353 |
|
| 10137 | 11354 |
eval "use warnings"; |
| 10138 | 11355 |
if ($@) {
|
| 10139 | 11356 |
eval ' |
| 10140 |
- package warnings; |
|
| 11357 |
+ package |
|
| 11358 |
+ warnings; |
|
| 10141 | 11359 |
sub enabled {return $^W;}
|
| 10142 | 11360 |
1; |
| 10143 | 11361 |
'; |
| ... | ... |
@@ -10255,7 +11473,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10255 | 11473 |
} |
| 10256 | 11474 |
$j = 0; |
| 10257 | 11475 |
} |
| 10258 |
- |
|
| 11476 |
+ |
|
| 10259 | 11477 |
if ($strict && $i < 2) {
|
| 10260 | 11478 |
# requires v1.2.3 |
| 10261 | 11479 |
return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)"); |
| ... | ... |
@@ -10264,6 +11482,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10264 | 11482 |
} # end if dotted-decimal |
| 10265 | 11483 |
else |
| 10266 | 11484 |
{ # decimal versions
|
| 11485 |
+ my $j = 0; |
|
| 10267 | 11486 |
# special $strict case for leading '.' or '0' |
| 10268 | 11487 |
if ($strict) {
|
| 10269 | 11488 |
if ($d eq '.') {
|
| ... | ... |
@@ -10274,6 +11493,11 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10274 | 11493 |
} |
| 10275 | 11494 |
} |
| 10276 | 11495 |
|
| 11496 |
+ # and we never support negative version numbers |
|
| 11497 |
+ if ($d eq '-') {
|
|
| 11498 |
+ return BADVERSION($s,$errstr,"Invalid version format (negative version number)"); |
|
| 11499 |
+ } |
|
| 11500 |
+ |
|
| 10277 | 11501 |
# consume all of the integer part |
| 10278 | 11502 |
while (isDIGIT($d)) {
|
| 10279 | 11503 |
$d++; |
| ... | ... |
@@ -10321,7 +11545,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10321 | 11545 |
} |
| 10322 | 11546 |
|
| 10323 | 11547 |
while (isDIGIT($d)) {
|
| 10324 |
- $d++; |
|
| 11548 |
+ $d++; $j++; |
|
| 10325 | 11549 |
if ($d eq '.' && isDIGIT($d-1)) {
|
| 10326 | 11550 |
if ($alpha) {
|
| 10327 | 11551 |
return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)"); |
| ... | ... |
@@ -10343,6 +11567,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10343 | 11567 |
if ( ! isDIGIT($d+1) ) {
|
| 10344 | 11568 |
return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)"); |
| 10345 | 11569 |
} |
| 11570 |
+ $width = $j; |
|
| 10346 | 11571 |
$d++; |
| 10347 | 11572 |
$alpha = TRUE; |
| 10348 | 11573 |
} |
| ... | ... |
@@ -10418,7 +11643,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10418 | 11643 |
if ( !$qv && $width < 3 ) {
|
| 10419 | 11644 |
$$rv->{width} = $width;
|
| 10420 | 11645 |
} |
| 10421 |
- |
|
| 11646 |
+ |
|
| 10422 | 11647 |
while (isDIGIT($pos)) {
|
| 10423 | 11648 |
$pos++; |
| 10424 | 11649 |
} |
| ... | ... |
@@ -10443,7 +11668,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10443 | 11668 |
$orev = $rev; |
| 10444 | 11669 |
$rev += $s * $mult; |
| 10445 | 11670 |
$mult /= 10; |
| 10446 |
- if ( (abs($orev) > abs($rev)) |
|
| 11671 |
+ if ( (abs($orev) > abs($rev)) |
|
| 10447 | 11672 |
|| (abs($rev) > $VERSION_MAX )) {
|
| 10448 | 11673 |
warn("Integer overflow in version %d",
|
| 10449 | 11674 |
$VERSION_MAX); |
| ... | ... |
@@ -10462,7 +11687,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10462 | 11687 |
$orev = $rev; |
| 10463 | 11688 |
$rev += $end * $mult; |
| 10464 | 11689 |
$mult *= 10; |
| 10465 |
- if ( (abs($orev) > abs($rev)) |
|
| 11690 |
+ if ( (abs($orev) > abs($rev)) |
|
| 10466 | 11691 |
|| (abs($rev) > $VERSION_MAX )) {
|
| 10467 | 11692 |
warn("Integer overflow in version");
|
| 10468 | 11693 |
$end = $s - 1; |
| ... | ... |
@@ -10470,7 +11695,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10470 | 11695 |
$vinf = 1; |
| 10471 | 11696 |
} |
| 10472 | 11697 |
} |
| 10473 |
- } |
|
| 11698 |
+ } |
|
| 10474 | 11699 |
} |
| 10475 | 11700 |
|
| 10476 | 11701 |
# Append revision |
| ... | ... |
@@ -10519,7 +11744,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10519 | 11744 |
# gcc version 3.3 20030304 (Apple Computer, Inc. build 1640) |
| 10520 | 11745 |
# for ( len = 2 - len; len > 0; len-- ) |
| 10521 | 11746 |
# av_push(MUTABLE_AV(sv), newSViv(0)); |
| 10522 |
- # |
|
| 11747 |
+ # |
|
| 10523 | 11748 |
$len = 2 - $len; |
| 10524 | 11749 |
while ($len-- > 0) {
|
| 10525 | 11750 |
push @av, 0; |
| ... | ... |
@@ -10559,7 +11784,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10559 | 11784 |
my ($class, $value) = @_; |
| 10560 | 11785 |
my $self = bless ({}, ref ($class) || $class);
|
| 10561 | 11786 |
my $qv = FALSE; |
| 10562 |
- |
|
| 11787 |
+ |
|
| 10563 | 11788 |
if ( ref($value) && eval('$value->isa("version")') ) {
|
| 10564 | 11789 |
# Can copy the elements directly |
| 10565 | 11790 |
$self->{version} = [ @{$value->{version} } ];
|
| ... | ... |
@@ -10598,7 +11823,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10598 | 11823 |
$value = sprintf("%.9f",$value);
|
| 10599 | 11824 |
$value =~ s/(0+)$//; # trim trailing zeros |
| 10600 | 11825 |
} |
| 10601 |
- |
|
| 11826 |
+ |
|
| 10602 | 11827 |
my $s = scan_version($value, \$self, $qv); |
| 10603 | 11828 |
|
| 10604 | 11829 |
if ($s) { # must be something left over
|
| ... | ... |
@@ -10611,7 +11836,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10611 | 11836 |
|
| 10612 | 11837 |
*parse = \&new; |
| 10613 | 11838 |
|
| 10614 |
- sub numify |
|
| 11839 |
+ sub numify |
|
| 10615 | 11840 |
{
|
| 10616 | 11841 |
my ($self) = @_; |
| 10617 | 11842 |
unless (_verify($self)) {
|
| ... | ... |
@@ -10652,7 +11877,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10652 | 11877 |
return $string; |
| 10653 | 11878 |
} |
| 10654 | 11879 |
|
| 10655 |
- sub normal |
|
| 11880 |
+ sub normal |
|
| 10656 | 11881 |
{
|
| 10657 | 11882 |
my ($self) = @_; |
| 10658 | 11883 |
unless (_verify($self)) {
|
| ... | ... |
@@ -10695,9 +11920,9 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10695 | 11920 |
require Carp; |
| 10696 | 11921 |
Carp::croak("Invalid version object");
|
| 10697 | 11922 |
} |
| 10698 |
- return exists $self->{original}
|
|
| 10699 |
- ? $self->{original}
|
|
| 10700 |
- : exists $self->{qv}
|
|
| 11923 |
+ return exists $self->{original}
|
|
| 11924 |
+ ? $self->{original}
|
|
| 11925 |
+ : exists $self->{qv}
|
|
| 10701 | 11926 |
? $self->normal |
| 10702 | 11927 |
: $self->numify; |
| 10703 | 11928 |
} |
| ... | ... |
@@ -10720,7 +11945,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10720 | 11945 |
} |
| 10721 | 11946 |
unless (_verify($right)) {
|
| 10722 | 11947 |
require Carp; |
| 10723 |
- Carp::croak("Invalid version object");
|
|
| 11948 |
+ Carp::croak("Invalid version format");
|
|
| 10724 | 11949 |
} |
| 10725 | 11950 |
my $l = $#{$left->{version}};
|
| 10726 | 11951 |
my $r = $#{$right->{version}};
|
| ... | ... |
@@ -10735,8 +11960,8 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10735 | 11960 |
} |
| 10736 | 11961 |
|
| 10737 | 11962 |
# tiebreaker for alpha with identical terms |
| 10738 |
- if ( $retval == 0 |
|
| 10739 |
- && $l == $r |
|
| 11963 |
+ if ( $retval == 0 |
|
| 11964 |
+ && $l == $r |
|
| 10740 | 11965 |
&& $left->{version}[$m] == $right->{version}[$m]
|
| 10741 | 11966 |
&& ( $lalpha || $ralpha ) ) {
|
| 10742 | 11967 |
|
| ... | ... |
@@ -10768,7 +11993,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10768 | 11993 |
} |
| 10769 | 11994 |
} |
| 10770 | 11995 |
|
| 10771 |
- return $retval; |
|
| 11996 |
+ return $retval; |
|
| 10772 | 11997 |
} |
| 10773 | 11998 |
|
| 10774 | 11999 |
sub vbool {
|
| ... | ... |
@@ -10776,8 +12001,8 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10776 | 12001 |
return vcmp($self,$self->new("0"),1);
|
| 10777 | 12002 |
} |
| 10778 | 12003 |
|
| 10779 |
- sub vnoop {
|
|
| 10780 |
- require Carp; |
|
| 12004 |
+ sub vnoop {
|
|
| 12005 |
+ require Carp; |
|
| 10781 | 12006 |
Carp::croak("operation not supported with version object");
|
| 10782 | 12007 |
} |
| 10783 | 12008 |
|
| ... | ... |
@@ -10796,8 +12021,8 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10796 | 12021 |
|
| 10797 | 12022 |
$value = _un_vstring($value); |
| 10798 | 12023 |
$value = 'v'.$value unless $value =~ /(^v|\d+\.\d+\.\d)/; |
| 10799 |
- my $version = $class->new($value); |
|
| 10800 |
- return $version; |
|
| 12024 |
+ my $obj = version->new($value); |
|
| 12025 |
+ return bless $obj, $class; |
|
| 10801 | 12026 |
} |
| 10802 | 12027 |
|
| 10803 | 12028 |
*declare = \&qv; |
| ... | ... |
@@ -10835,7 +12060,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10835 | 12060 |
sub _un_vstring {
|
| 10836 | 12061 |
my $value = shift; |
| 10837 | 12062 |
# may be a v-string |
| 10838 |
- if ( length($value) >= 3 && $value !~ /[._]/ |
|
| 12063 |
+ if ( length($value) >= 3 && $value !~ /[._]/ |
|
| 10839 | 12064 |
&& _is_non_alphanumeric($value)) {
|
| 10840 | 12065 |
my $tvalue; |
| 10841 | 12066 |
if ( $] ge 5.008_001 ) {
|
| ... | ... |
@@ -10893,7 +12118,7 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10893 | 12118 |
if ( defined $req ) {
|
| 10894 | 12119 |
unless ( defined $version ) {
|
| 10895 | 12120 |
require Carp; |
| 10896 |
- my $msg = $] < 5.006 |
|
| 12121 |
+ my $msg = $] < 5.006 |
|
| 10897 | 12122 |
? "$class version $req required--this is only version " |
| 10898 | 12123 |
: "$class does not define \$$class\::VERSION" |
| 10899 | 12124 |
."--version check failed"; |
| ... | ... |
@@ -10911,14 +12136,14 @@ $fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
|
| 10911 | 12136 |
if ( $req > $version ) {
|
| 10912 | 12137 |
require Carp; |
| 10913 | 12138 |
if ( $req->is_qv ) {
|
| 10914 |
- Carp::croak( |
|
| 12139 |
+ Carp::croak( |
|
| 10915 | 12140 |
sprintf ("%s version %s required--".
|
| 10916 | 12141 |
"this is only version %s", $class, |
| 10917 | 12142 |
$req->normal, $version->normal) |
| 10918 | 12143 |
); |
| 10919 | 12144 |
} |
| 10920 | 12145 |
else {
|
| 10921 |
- Carp::croak( |
|
| 12146 |
+ Carp::croak( |
|
| 10922 | 12147 |
sprintf ("%s version %s required--".
|
| 10923 | 12148 |
"this is only version %s", $class, |
| 10924 | 12149 |
$req->stringify, $version->stringify) |
| ... | ... |
@@ -10937,6 +12162,14 @@ s/^ //mg for values %fatpacked; |
| 10937 | 12162 |
|
| 10938 | 12163 |
unshift @INC, sub {
|
| 10939 | 12164 |
if (my $fat = $fatpacked{$_[1]}) {
|
| 12165 |
+ if ($] < 5.008) {
|
|
| 12166 |
+ return sub {
|
|
| 12167 |
+ return 0 unless length $fat; |
|
| 12168 |
+ $fat =~ s/^([^\n]*\n?)//; |
|
| 12169 |
+ $_ = $1; |
|
| 12170 |
+ return 1; |
|
| 12171 |
+ }; |
|
| 12172 |
+ } |
|
| 10940 | 12173 |
open my $fh, '<', \$fat |
| 10941 | 12174 |
or die "FatPacker error loading $_[1] (could be a perl installation issue?)"; |
| 10942 | 12175 |
return $fh; |
| ... | ... |
@@ -10963,21 +12196,54 @@ cpanm - get, unpack build and install modules from CPAN |
| 10963 | 12196 |
|
| 10964 | 12197 |
=head1 SYNOPSIS |
| 10965 | 12198 |
|
| 10966 |
- cpanm Test::More # install Test::More |
|
| 10967 |
- cpanm MIYAGAWA/Plack-0.99_05.tar.gz # full distribution path |
|
| 10968 |
- cpanm http://example.org/LDS/CGI.pm-3.20.tar.gz # install from URL |
|
| 10969 |
- cpanm ~/dists/MyCompany-Enterprise-1.00.tar.gz # install from a local file |
|
| 10970 |
- cpanm --interactive Task::Kensho # Configure interactively |
|
| 10971 |
- cpanm . # install from local directory |
|
| 10972 |
- cpanm --installdeps . # install all the deps for the current directory |
|
| 10973 |
- cpanm -L extlib Plack # install Plack and all non-core deps into extlib |
|
| 10974 |
- cpanm --mirror http://cpan.cpantesters.org/ DBI # use the fast-syncing mirror |
|
| 10975 |
- cpanm --scandeps Moose # See what modules will be installed for Moose |
|
| 12199 |
+ cpanm Test::More # install Test::More |
|
| 12200 |
+ cpanm MIYAGAWA/Plack-0.99_05.tar.gz # full distribution path |
|
| 12201 |
+ cpanm http://example.org/LDS/CGI.pm-3.20.tar.gz # install from URL |
|
| 12202 |
+ cpanm ~/dists/MyCompany-Enterprise-1.00.tar.gz # install from a local file |
|
| 12203 |
+ cpanm --interactive Task::Kensho # Configure interactively |
|
| 12204 |
+ cpanm . # install from local directory |
|
| 12205 |
+ cpanm --installdeps . # install all the deps for the current directory |
|
| 12206 |
+ cpanm -L extlib Plack # install Plack and all non-core deps into extlib |
|
| 12207 |
+ cpanm --mirror http://cpan.cpantesters.org/ DBI # use the fast-syncing mirror |
|
| 12208 |
+ cpanm --scandeps Moose # See what modules will be installed for Moose |
|
| 10976 | 12209 |
|
| 10977 | 12210 |
=head1 COMMANDS |
| 10978 | 12211 |
|
| 10979 | 12212 |
=over 4 |
| 10980 | 12213 |
|
| 12214 |
+=item (arguments) |
|
| 12215 |
+ |
|
| 12216 |
+Command line arguments can be either a module name, distribution file, |
|
| 12217 |
+local file path, HTTP URL or git repository URL. Following commands |
|
| 12218 |
+will all work as you expect. |
|
| 12219 |
+ |
|
| 12220 |
+ cpanm Plack |
|
| 12221 |
+ cpanm Plack/Request.pm |
|
| 12222 |
+ cpanm MIYAGAWA/Plack-1.0000.tar.gz |
|
| 12223 |
+ cpanm /path/to/Plack-1.0000.tar.gz |
|
| 12224 |
+ cpanm http://cpan.metacpan.org/authors/id/M/MI/MIYAGAWA/Plack-0.9990.tar.gz |
|
| 12225 |
+ cpanm git://github.com/miyagawa/Plack.git |
|
| 12226 |
+ |
|
| 12227 |
+Additionally, you can use the notation using C<~> and C<@> to specify |
|
| 12228 |
+version for a given module. C<~> specifies the version requirement in |
|
| 12229 |
+the L<CPAN::Meta::Spec> format, while C<@> pins the exact version, and |
|
| 12230 |
+is a shortcut for C<~"== VERSION">. |
|
| 12231 |
+ |
|
| 12232 |
+ cpanm Plack~1.0000 # 1.0000 or later |
|
| 12233 |
+ cpanm Plack~">= 1.0000, < 2.0000" # latest of 1.xxxx |
|
| 12234 |
+ cpanm Plack@0.9990 # specific version. same as Plack~"== 0.9990" |
|
| 12235 |
+ |
|
| 12236 |
+The version query including specific version or range will be sent to |
|
| 12237 |
+L<MetaCPAN> to search for previous releases. The query will search for |
|
| 12238 |
+BackPAN archives by default, unless you specify C<--dev> option, in |
|
| 12239 |
+which case, archived versions will be filtered out. |
|
| 12240 |
+ |
|
| 12241 |
+For a git repository, you can specify a branch, tag, or commit SHA to |
|
| 12242 |
+build. The default is C<master> |
|
| 12243 |
+ |
|
| 12244 |
+ cpanm git://github.com/miyagawa/Plack.git@1.0000 # tag |
|
| 12245 |
+ cpanm git://github.com/miyagawa/Plack.git@devel # branch |
|
| 12246 |
+ |
|
| 10981 | 12247 |
=item -i, --install |
| 10982 | 12248 |
|
| 10983 | 12249 |
Installs the modules. This is a default behavior and this is just a |
| ... | ... |
@@ -11037,6 +12303,16 @@ architecture you've already tested to make sure it builds fine. |
| 11037 | 12303 |
Defaults to false, and you can say C<--no-notest> to override when it |
| 11038 | 12304 |
is set in the default options in C<PERL_CPANM_OPT>. |
| 11039 | 12305 |
|
| 12306 |
+=item --test-only |
|
| 12307 |
+ |
|
| 12308 |
+Run the tests only, and do not install the specified module or |
|
| 12309 |
+distributions. Handy if you want to verify the new (or even old) |
|
| 12310 |
+releases pass its unit tests without installing the module. |
|
| 12311 |
+ |
|
| 12312 |
+Note that if you specify this option with a module or distribution |
|
| 12313 |
+that has dependencies, these dependencies will be installed if you |
|
| 12314 |
+don't currently have them. |
|
| 12315 |
+ |
|
| 11040 | 12316 |
=item -S, --sudo |
| 11041 | 12317 |
|
| 11042 | 12318 |
Switch to the root user with C<sudo> when installing modules. Use this |
| ... | ... |
@@ -11084,6 +12360,14 @@ Specifies the base URL for the CPAN mirror to use, such as |
| 11084 | 12360 |
C<http://cpan.cpantesters.org/> (you can omit the trailing slash). You |
| 11085 | 12361 |
can specify multiple mirror URLs by repeating the command line option. |
| 11086 | 12362 |
|
| 12363 |
+You can use a local directory that has a CPAN mirror structure |
|
| 12364 |
+(created by tools such as L<OrePAN> or L<Pinto>) by using a special |
|
| 12365 |
+URL scheme C<file://>. If the given URL begins with `/` (without any |
|
| 12366 |
+scheme), it is considered as a file scheme as well. |
|
| 12367 |
+ |
|
| 12368 |
+ cpanm --mirror file:///path/to/mirror |
|
| 12369 |
+ cpanm --mirror ~/minicpan # Because shell expands ~ to /home/user |
|
| 12370 |
+ |
|
| 11087 | 12371 |
Defaults to C<http://search.cpan.org/CPAN> which is a geo location |
| 11088 | 12372 |
aware redirector. |
| 11089 | 12373 |
|
| ... | ... |
@@ -11106,11 +12390,6 @@ shell aliases, like: |
| 11106 | 12390 |
B<EXPERIMENTAL>: Specifies the file path to C<02packages.details.txt> |
| 11107 | 12391 |
for module search index. |
| 11108 | 12392 |
|
| 11109 |
-=item --metacpan |
|
| 11110 |
- |
|
| 11111 |
-B<EXPERIMENTAL>: Use L<http://api.metacpan.org/> API for module lookup instead of |
|
| 11112 |
-L<http://cpanmetadb.plackperl.org/>. |
|
| 11113 |
- |
|
| 11114 | 12393 |
=item --prompt |
| 11115 | 12394 |
|
| 11116 | 12395 |
Prompts when a test fails so that you can skip, force install, retry |
| ... | ... |
@@ -11120,6 +12399,10 @@ one of the dependency failed if you want to proceed the installation. |
| 11120 | 12399 |
Defaults to false, and you can say C<--no-prompt> to override if it's |
| 11121 | 12400 |
set in the default options in C<PERL_CPANM_OPT>. |
| 11122 | 12401 |
|
| 12402 |
+=item --dev |
|
| 12403 |
+ |
|
| 12404 |
+B<EXPERIMENTAL>: search for a newer developer release as well. Defaults to false. |
|
| 12405 |
+ |
|
| 11123 | 12406 |
=item --reinstall |
| 11124 | 12407 |
|
| 11125 | 12408 |
cpanm, when given a module name in the command line (i.e. C<cpanm |
| ... | ... |
@@ -11232,8 +12515,8 @@ library path. |
| 11232 | 12515 |
=item --cascade-search |
| 11233 | 12516 |
|
| 11234 | 12517 |
B<EXPERIMENTAL>: Specifies whether to cascade search when you specify |
| 11235 |
-multiple mirrors and a mirror has a lower version of the module than |
|
| 11236 |
-requested. Defaults to false. |
|
| 12518 |
+multiple mirrors and a mirror doesn't have a module or has a lower |
|
| 12519 |
+version of the module than requested. Defaults to false. |
|
| 11237 | 12520 |
|
| 11238 | 12521 |
=item --skip-installed |
| 11239 | 12522 |
|
| ... | ... |
@@ -11258,8 +12541,12 @@ C<--skip-installed> but while C<--skip-installed> checks if the |
| 11258 | 12541 |
I<latest> version of CPAN is installed, C<--skip-satisfied> checks if |
| 11259 | 12542 |
a requested version (or not, which means any version) is installed. |
| 11260 | 12543 |
|
| 11261 |
-Defaults to false for bare module names, but if you specify versions |
|
| 11262 |
-with C<~>, it will always skip satisfied requirements. |
|
| 12544 |
+Defaults to false. |
|
| 12545 |
+ |
|
| 12546 |
+=item --verify |
|
| 12547 |
+ |
|
| 12548 |
+Verify the integrity of distribution files retrieved from PAUSE using |
|
| 12549 |
+CHECKSUMS and SIGNATURES (if found). Defaults to false. |
|
| 11263 | 12550 |
|
| 11264 | 12551 |
=item --auto-cleanup |
| 11265 | 12552 |
|
| ... | ... |
@@ -7,3 +7,4 @@ |
| 7 | 7 |
<td><a href="/_admin/user">User</a></td> |
| 8 | 8 |
</tr> |
| 9 | 9 |
</table> |
| 10 |
+ |