... | ... |
@@ -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 |
+ |