xqkqfjdlqsjdqs odsqqlkdjqmsjd onelmlksqdjmlsqjd LWP/Protocol/https/hosts.pm000044400000001175151112047420011646 0ustar00package LWP::Protocol::https::hosts; use strict; use warnings; use parent 'LWP::Protocol::https'; use LWP::UserAgent::DNS::Hosts; sub _extra_sock_opts { my ($self, $host, $port) = @_; my @opts = $self->SUPER::_extra_sock_opts($host, $port); if (my $peer_addr = LWP::UserAgent::DNS::Hosts->_registered_peer_addr($host)) { push @opts, ( PeerAddr => $peer_addr, Host => $host, SSL_verifycn_name => $host, SSL_hostname => $host, # for SNI ); } return @opts; } sub socket_class { 'LWP::Protocol::https::Socket' } 1; __END__ LWP/Protocol/http/hosts.pm000044400000000747151112047420011467 0ustar00package LWP::Protocol::http::hosts; use strict; use warnings; use parent 'LWP::Protocol::http'; use LWP::UserAgent::DNS::Hosts; sub _extra_sock_opts { my ($self, $host, $port) = @_; my @opts = $self->SUPER::_extra_sock_opts($host, $port); if (my $peer_addr = LWP::UserAgent::DNS::Hosts->_registered_peer_addr($host)) { push @opts, (PeerAddr => $peer_addr, Host => $host); } return @opts; } sub socket_class { 'LWP::Protocol::http::Socket' } 1; __END__ LWP/Protocol/https.pm000044400000012645151112047420010512 0ustar00package LWP::Protocol::https; use strict; use warnings; our $VERSION = '6.14'; use parent qw(LWP::Protocol::http); require Net::HTTPS; sub socket_type { return "https"; } sub _extra_sock_opts { my $self = shift; my %ssl_opts = %{$self->{ua}{ssl_opts} || {}}; if (delete $ssl_opts{verify_hostname}) { $ssl_opts{SSL_verify_mode} ||= 1; $ssl_opts{SSL_verifycn_scheme} = 'www'; } else { if ( $Net::HTTPS::SSL_SOCKET_CLASS eq 'Net::SSL' ) { $ssl_opts{SSL_verifycn_scheme} = ''; } else { $ssl_opts{SSL_verifycn_scheme} = 'none'; } } if ($ssl_opts{SSL_verify_mode}) { unless (exists $ssl_opts{SSL_ca_file} || exists $ssl_opts{SSL_ca_path}) { if ($Net::HTTPS::SSL_SOCKET_CLASS eq 'IO::Socket::SSL' && defined &IO::Socket::SSL::default_ca && IO::Socket::SSL::default_ca() ) { # IO::Socket::SSL has a usable default CA } elsif ( my $cafile = eval { require Mozilla::CA; Mozilla::CA::SSL_ca_file() }) { # use Mozilla::CA $ssl_opts{SSL_ca_file} = $cafile; } else { die <<'EOT'; Can't verify SSL peers without knowing which Certificate Authorities to trust. This problem can be fixed by either setting the PERL_LWP_SSL_CA_FILE environment variable to the file where your trusted CA are, or by installing the Mozilla::CA module for set of commonly trusted CAs. To completely disable the verification that you talk to the correct SSL peer you can set SSL_verify_mode to 0 within ssl_opts. But, if you do this you can't be sure that you communicate with the expected peer. EOT } } } $self->{ssl_opts} = \%ssl_opts; return (%ssl_opts, MultiHomed => 1, $self->SUPER::_extra_sock_opts); } # This is a subclass of LWP::Protocol::http. # That parent class calls ->_check_sock() during the # request method. This allows us to hook in and run checks # sub _check_sock # { # my($self, $req, $sock) = @_; # } sub _get_sock_info { my $self = shift; $self->SUPER::_get_sock_info(@_); my($res, $sock) = @_; if ($sock->can('get_sslversion') and my $sslversion = $sock->get_sslversion) { $res->header("Client-SSL-Version" => $sslversion); } $res->header("Client-SSL-Cipher" => $sock->get_cipher); my $cert = $sock->get_peer_certificate; if ($cert) { $res->header("Client-SSL-Cert-Subject" => $cert->subject_name); $res->header("Client-SSL-Cert-Issuer" => $cert->issuer_name); } if (!$self->{ssl_opts}{SSL_verify_mode}) { $res->push_header("Client-SSL-Warning" => "Peer certificate not verified"); } elsif (!$self->{ssl_opts}{SSL_verifycn_scheme}) { $res->push_header("Client-SSL-Warning" => "Peer hostname match with certificate not verified"); } $res->header("Client-SSL-Socket-Class" => $Net::HTTPS::SSL_SOCKET_CLASS); } # upgrade plain socket to SSL, used for CONNECT tunnel when proxying https # will only work if the underlying socket class of Net::HTTPS is # IO::Socket::SSL, but code will only be called in this case if ( $Net::HTTPS::SSL_SOCKET_CLASS->can('start_SSL')) { *_upgrade_sock = sub { my ($self,$sock,$url) = @_; # SNI should be passed there only if it is not an IP address. # Details: https://github.com/libwww-perl/libwww-perl/issues/449#issuecomment-1896175509 my $host = $url->host() =~ m/:|^[\d.]+$/s ? undef : $url->host(); $sock = LWP::Protocol::https::Socket->start_SSL( $sock, SSL_verifycn_name => $url->host, SSL_hostname => $host, $self->_extra_sock_opts, ); $@ = LWP::Protocol::https::Socket->errstr if ! $sock; return $sock; } } #----------------------------------------------------------- package LWP::Protocol::https::Socket; use parent -norequire, qw(Net::HTTPS LWP::Protocol::http::SocketMethods); 1; __END__ =head1 NAME LWP::Protocol::https - Provide https support for LWP::UserAgent =head1 SYNOPSIS use LWP::UserAgent; $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 }); $res = $ua->get("https://www.example.com"); # specify a CA path $ua = LWP::UserAgent->new( ssl_opts => { SSL_ca_path => '/etc/ssl/certs', verify_hostname => 1, } ); =head1 DESCRIPTION The LWP::Protocol::https module provides support for using https schemed URLs with LWP. This module is a plug-in to the LWP protocol handling, so you don't use it directly. Once the module is installed LWP is able to access sites using HTTP over SSL/TLS. If hostname verification is requested by LWP::UserAgent's C, and neither C nor C is set, then C is implied to be the one provided by L. If the Mozilla::CA module isn't available SSL requests will fail. Either install this module, set up an alternative C or disable hostname verification. This module used to be bundled with the libwww-perl, but it was unbundled in v6.02 in order to be able to declare its dependencies properly for the CPAN tool-chain. Applications that need https support can just declare their dependency on LWP::Protocol::https and will no longer need to know what underlying modules to install. =head1 SEE ALSO L, L, L =head1 COPYRIGHT & LICENSE Copyright (c) 1997-2011 Gisle Aas. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut LWP/UserAgent/DNS/Hosts.pm000044400000011706151112047420011165 0ustar00package LWP::UserAgent::DNS::Hosts; use 5.008001; use strict; use warnings; use Carp; use LWP::Protocol; use Scope::Guard qw(guard); our $VERSION = '0.14'; $VERSION = eval $VERSION; our @Protocols = qw(http https); our %Implementors; our %Hosts; sub register_host { my ($class, $host, $peer_addr) = @_; $Hosts{$host} = $peer_addr; } sub register_hosts { my ($class, %pairs) = @_; while (my ($host, $peer_addr) = each %pairs) { $class->register_host($host, $peer_addr); } } sub clear_hosts { %Hosts = (); } sub read_hosts { my ($class, $source) = @_; if (ref $source eq 'GLOB') { $class->_read_hosts_from_handle($source); } elsif ($source !~ /[\x0D\x0A]/ && -f $source) { $class->_read_hosts_from_file($source); } else { $class->_read_hosts_from_string($source); } } sub _read_hosts_from_handle { my ($class, $handle) = @_; while (<$handle>) { chomp; s/^\s+//g; s/\s+$//g; next if !$_ || /^#/; my ($addr, @hosts) = split /\s+/; for my $host (@hosts) { $class->register_host($host, $addr); } } } sub _read_hosts_from_file { my ($class, $file) = @_; open my $fh, '<', $file or croak $!; $class->_read_hosts_from_handle($fh); close $fh; } sub _read_hosts_from_string { my ($class, $string) = @_; open my $fh, '<', \$string or croak $!; $class->_read_hosts_from_handle($fh); close $fh; } sub _registered_peer_addr { my ($class, $host) = @_; return $Hosts{$host}; } sub _implementor { my ($class, $proto) = @_; return sprintf 'LWP::Protocol::%s::hosts' => $proto; } sub enable_override { my $class = shift; for my $proto (@Protocols) { if (my $orig = LWP::Protocol::implementor($proto)) { my $impl = $class->_implementor($proto); if (eval "require $impl; 1") { LWP::Protocol::implementor($proto => $impl); $Implementors{$proto} = $orig; } } else { carp("LWP::Protocol::$proto is unavailable. Skip overriding it."); } } if (defined wantarray) { return guard { $class->disable_override }; } } sub disable_override { my $class = shift; for my $proto (@Protocols) { if (my $impl = $Implementors{$proto}) { LWP::Protocol::implementor($proto, $impl); } } } 1; =encoding utf-8 =for stopwords =head1 NAME LWP::UserAgent::DNS::Hosts - Override LWP HTTP/HTTPS request's host like /etc/hosts =head1 SYNOPSIS use LWP::UserAgent; use LWP::UserAgent::DNS::Hosts; # add entry LWP::UserAgent::DNS::Hosts->register_host( 'www.cpan.org' => '127.0.0.1', ); # add entries LWP::UserAgent::DNS::Hosts->register_hosts( 'search.cpan.org' => '192.168.0.100', 'pause.perl.org' => '192.168.0.101', ); # read hosts file LWP::UserAgent::DNS::Hosts->read_hosts('/path/to/my/hosts'); LWP::UserAgent::DNS::Hosts->enable_override; # override request hosts with peer addr defined above my $ua = LWP::UserAgent->new; my $res = $ua->get("http://www.cpan.org/"); print $res->content; # is same as "http://127.0.0.1/" content =head1 DESCRIPTION LWP::UserAgent::DNS::Hosts is a module to override HTTP/HTTPS request peer addresses that uses LWP::UserAgent. This module concept was got from L. =head1 METHODS =over 4 =item register_host($host, $peer_addr) LWP::UserAgent::DNS::Hosts->register_host($host, $peer_addr); Registers a pair of hostname and peer ip address. # /etc/hosts 127.0.0.1 example.com equals to: LWP::UserAgent::DNS::Hosts->register_hosts('example.com', '127.0.0.1'); =item register_hosts(%host_addr_pairs) LWP::UserAgent::DNS::Hosts->register_hosts( 'example.com' => '192.168.0.1', 'example.org' => '192.168.0.2', ... ); Registers pairs of hostname and peer ip address. =item read_hosts($file_or_string) LWP::UserAgent::DNS::Hosts->read_hosts('hosts.my'); LWP::UserAgent::DNS::Hosts->read_hosts(<<'__HOST__'); 127.0.0.1 example.com 192.168.0.1 example.net example.org __HOST__ Registers "/etc/hosts" syntax entries. =item clear_hosts Clears registered pairs. =item enable_override LWP::UserAgent::DNS::Hosts->enable_override; my $guard = LWP::UserAgent::DNS::Hosts->enable_override; Enables to override hook. If called in a non-void context, returns a L object that automatically resets the override when it goes out of context. =item disable_override LWP::UserAgent::DNS::Hosts->disable_override; Disables to override hook. If you use the guard interface described above, it will be automatically called for you. =back =head1 AUTHOR NAKAGAWA Masaki Emasaki@cpan.orgE =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L =cut cPanelUserConfig.pm000064400000001026151112047420010265 0ustar00# cpanel - cPanelUserConfig.pm Copyright(c) 2021 cPanel, L.L.C. # All Rights Reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited if ( $> != 0 ) { my $b__dir = ( getpwuid($>) )[7] . '/perl'; unshift @INC, $b__dir . '5/lib/perl5', $b__dir . '5/lib/perl5/x86_64-linux-thread-multi', map { $b__dir . $_ } grep {$_ ne '.'} @INC; } 1; HTTP/Tiny.pm000044400000237105151112047420006607 0ustar00# vim: ts=4 sts=4 sw=4 et: package HTTP::Tiny; use strict; use warnings; # ABSTRACT: A small, simple, correct HTTP/1.1 client our $VERSION = '0.090'; sub _croak { require Carp; Carp::croak(@_) } #pod =method new #pod #pod $http = HTTP::Tiny->new( %attributes ); #pod #pod This constructor returns a new HTTP::Tiny object. Valid attributes include: #pod #pod =for :list #pod * C — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If #pod C — ends in a space character, the default user-agent string is #pod appended. #pod * C — An instance of L — or equivalent class #pod that supports the C and C methods #pod * C — A hashref of default headers to apply to requests #pod * C — The local IP address to bind to #pod * C — Whether to reuse the last connection (if for the same #pod scheme, host and port) (defaults to 1) #pod * C — Maximum number of redirects allowed (defaults to 5) #pod * C — Maximum response size in bytes (only when not using a data #pod callback). If defined, requests with responses larger than this will return #pod a 599 status code. #pod * C — URL of a proxy server to use for HTTP connections #pod (default is C<$ENV{http_proxy}> — if set) #pod * C — URL of a proxy server to use for HTTPS connections #pod (default is C<$ENV{https_proxy}> — if set) #pod * C — URL of a generic proxy server for both HTTP and HTTPS #pod connections (default is C<$ENV{all_proxy}> — if set) #pod * C — List of domain suffixes that should not be proxied. Must #pod be a comma-separated string or an array reference. (default is #pod C<$ENV{no_proxy}> —) #pod * C — Request timeout in seconds (default is 60) If a socket open, #pod read or write takes longer than the timeout, the request response status code #pod will be 599. #pod * C — A boolean that indicates whether to validate the TLS/SSL #pod certificate of an C — connection (default is true). Changed from false #pod to true in version 0.083. #pod * C — A hashref of C — options to pass through to #pod L #pod * C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}> - Changes the default #pod certificate verification behavior to not check server identity if set to 1. #pod Only effective if C is not set. Added in version 0.083. #pod #pod #pod An accessor/mutator method exists for each attribute. #pod #pod Passing an explicit C for C, C or C will #pod prevent getting the corresponding proxies from the environment. #pod #pod Errors during request execution will result in a pseudo-HTTP status code of 599 #pod and a reason of "Internal Exception". The content field in the response will #pod contain the text of the error. #pod #pod The C parameter enables a persistent connection, but only to a #pod single destination scheme, host and port. If any connection-relevant #pod attributes are modified via accessor, or if the process ID or thread ID change, #pod the persistent connection will be dropped. If you want persistent connections #pod across multiple destinations, use multiple HTTP::Tiny objects. #pod #pod See L for more on the C and C #pod attributes. #pod #pod =cut my @attributes; BEGIN { @attributes = qw( cookie_jar default_headers http_proxy https_proxy keep_alive local_address max_redirect max_size proxy no_proxy SSL_options verify_SSL ); my %persist_ok = map {; $_ => 1 } qw( cookie_jar default_headers max_redirect max_size ); no strict 'refs'; no warnings 'uninitialized'; for my $accessor ( @attributes ) { *{$accessor} = sub { @_ > 1 ? do { delete $_[0]->{handle} if !$persist_ok{$accessor} && $_[1] ne $_[0]->{$accessor}; $_[0]->{$accessor} = $_[1] } : $_[0]->{$accessor}; }; } } sub agent { my($self, $agent) = @_; if( @_ > 1 ){ $self->{agent} = (defined $agent && $agent =~ / $/) ? $agent . $self->_agent : $agent; } return $self->{agent}; } sub timeout { my ($self, $timeout) = @_; if ( @_ > 1 ) { $self->{timeout} = $timeout; if ($self->{handle}) { $self->{handle}->timeout($timeout); } } return $self->{timeout}; } sub new { my($class, %args) = @_; # Support lower case verify_ssl argument, but only if verify_SSL is not # true. if ( exists $args{verify_ssl} ) { $args{verify_SSL} ||= $args{verify_ssl}; } my $self = { max_redirect => 5, timeout => defined $args{timeout} ? $args{timeout} : 60, keep_alive => 1, verify_SSL => defined $args{verify_SSL} ? $args{verify_SSL} : _verify_SSL_default(), no_proxy => $ENV{no_proxy}, }; bless $self, $class; $class->_validate_cookie_jar( $args{cookie_jar} ) if $args{cookie_jar}; for my $key ( @attributes ) { $self->{$key} = $args{$key} if exists $args{$key} } $self->agent( exists $args{agent} ? $args{agent} : $class->_agent ); $self->_set_proxies; return $self; } sub _verify_SSL_default { my ($self) = @_; # Check if insecure default certificate verification behaviour has been # changed by the user by setting PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=1 return (($ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} || '') eq '1') ? 0 : 1; } sub _set_proxies { my ($self) = @_; # get proxies from %ENV only if not provided; explicit undef will disable # getting proxies from the environment # generic proxy if (! exists $self->{proxy} ) { $self->{proxy} = $ENV{all_proxy} || $ENV{ALL_PROXY}; } if ( defined $self->{proxy} ) { $self->_split_proxy( 'generic proxy' => $self->{proxy} ); # validate } else { delete $self->{proxy}; } # http proxy if (! exists $self->{http_proxy} ) { # under CGI, bypass HTTP_PROXY as request sets it from Proxy header local $ENV{HTTP_PROXY} = ($ENV{CGI_HTTP_PROXY} || "") if $ENV{REQUEST_METHOD}; $self->{http_proxy} = $ENV{http_proxy} || $ENV{HTTP_PROXY} || $self->{proxy}; } if ( defined $self->{http_proxy} ) { $self->_split_proxy( http_proxy => $self->{http_proxy} ); # validate $self->{_has_proxy}{http} = 1; } else { delete $self->{http_proxy}; } # https proxy if (! exists $self->{https_proxy} ) { $self->{https_proxy} = $ENV{https_proxy} || $ENV{HTTPS_PROXY} || $self->{proxy}; } if ( $self->{https_proxy} ) { $self->_split_proxy( https_proxy => $self->{https_proxy} ); # validate $self->{_has_proxy}{https} = 1; } else { delete $self->{https_proxy}; } # Split no_proxy to array reference if not provided as such unless ( ref $self->{no_proxy} eq 'ARRAY' ) { $self->{no_proxy} = (defined $self->{no_proxy}) ? [ split /\s*,\s*/, $self->{no_proxy} ] : []; } return; } #pod =method get|head|put|post|patch|delete #pod #pod $response = $http->get($url); #pod $response = $http->get($url, \%options); #pod $response = $http->head($url); #pod #pod These methods are shorthand for calling C for the given method. The #pod URL must have unsafe characters escaped and international domain names encoded. #pod See C for valid options and a description of the response. #pod #pod The C field of the response will be true if the status code is 2XX. #pod #pod =cut for my $sub_name ( qw/get head put post patch delete/ ) { my $req_method = uc $sub_name; no strict 'refs'; eval <<"HERE"; ## no critic sub $sub_name { my (\$self, \$url, \$args) = \@_; \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH') or _croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n"); return \$self->request('$req_method', \$url, \$args || {}); } HERE } #pod =method post_form #pod #pod $response = $http->post_form($url, $form_data); #pod $response = $http->post_form($url, $form_data, \%options); #pod #pod This method executes a C request and sends the key/value pairs from a #pod form data hash or array reference to the given URL with a C of #pod C. If data is provided as an array #pod reference, the order is preserved; if provided as a hash reference, the terms #pod are sorted by key for consistency. See documentation for the #pod C method for details on the encoding. #pod #pod The URL must have unsafe characters escaped and international domain names #pod encoded. See C for valid options and a description of the response. #pod Any C header or content in the options hashref will be ignored. #pod #pod The C field of the response will be true if the status code is 2XX. #pod #pod =cut sub post_form { my ($self, $url, $data, $args) = @_; (@_ == 3 || @_ == 4 && ref $args eq 'HASH') or _croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n"); my $headers = {}; while ( my ($key, $value) = each %{$args->{headers} || {}} ) { $headers->{lc $key} = $value; } return $self->request('POST', $url, { # Any existing 'headers' key in $args will be overridden with a # normalized version below. %$args, content => $self->www_form_urlencode($data), headers => { %$headers, 'content-type' => 'application/x-www-form-urlencoded' }, } ); } #pod =method mirror #pod #pod $response = $http->mirror($url, $file, \%options) #pod if ( $response->{success} ) { #pod print "$file is up to date\n"; #pod } #pod #pod Executes a C request for the URL and saves the response body to the file #pod name provided. The URL must have unsafe characters escaped and international #pod domain names encoded. If the file already exists, the request will include an #pod C header with the modification timestamp of the file. You #pod may specify a different C header yourself in the C<< #pod $options->{headers} >> hash. #pod #pod The C field of the response will be true if the status code is 2XX #pod or if the status code is 304 (unmodified). #pod #pod If the file was modified and the server response includes a properly #pod formatted C header, the file modification time will #pod be updated accordingly. #pod #pod =cut sub mirror { my ($self, $url, $file, $args) = @_; @_ == 3 || (@_ == 4 && ref $args eq 'HASH') or _croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n"); if ( exists $args->{headers} ) { my $headers = {}; while ( my ($key, $value) = each %{$args->{headers} || {}} ) { $headers->{lc $key} = $value; } $args->{headers} = $headers; } if ( -e $file and my $mtime = (stat($file))[9] ) { $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime); } my $tempfile = $file . int(rand(2**31)); require Fcntl; sysopen my $fh, $tempfile, Fcntl::O_CREAT()|Fcntl::O_EXCL()|Fcntl::O_WRONLY() or _croak(qq/Error: Could not create temporary file $tempfile for downloading: $!\n/); binmode $fh; $args->{data_callback} = sub { print {$fh} $_[0] }; my $response = $self->request('GET', $url, $args); close $fh or _croak(qq/Error: Caught error closing temporary file $tempfile: $!\n/); if ( $response->{success} ) { rename $tempfile, $file or _croak(qq/Error replacing $file with $tempfile: $!\n/); my $lm = $response->{headers}{'last-modified'}; if ( $lm and my $mtime = $self->_parse_http_date($lm) ) { utime $mtime, $mtime, $file; } } $response->{success} ||= $response->{status} eq '304'; unlink $tempfile; return $response; } #pod =method request #pod #pod $response = $http->request($method, $url); #pod $response = $http->request($method, $url, \%options); #pod #pod Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST', #pod 'PUT', etc.) on the given URL. The URL must have unsafe characters escaped and #pod international domain names encoded. #pod #pod B: Method names are B per the HTTP/1.1 specification. #pod Don't use C when you really want C. See L for #pod how this applies to redirection. #pod #pod If the URL includes a "user:password" stanza, they will be used for Basic-style #pod authorization headers. (Authorization headers will not be included in a #pod redirected request.) For example: #pod #pod $http->request('GET', 'http://Aladdin:open sesame@example.com/'); #pod #pod If the "user:password" stanza contains reserved characters, they must #pod be percent-escaped: #pod #pod $http->request('GET', 'http://john%40example.com:password@example.com/'); #pod #pod A hashref of options may be appended to modify the request. #pod #pod Valid options are: #pod #pod =for :list #pod * C — #pod A hashref containing headers to include with the request. If the value for #pod a header is an array reference, the header will be output multiple times with #pod each value in the array. These headers over-write any default headers. #pod * C — #pod A scalar to include as the body of the request OR a code reference #pod that will be called iteratively to produce the body of the request #pod * C — #pod A code reference that will be called if it exists to provide a hashref #pod of trailing headers (only used with chunked transfer-encoding) #pod * C — #pod A code reference that will be called for each chunks of the response #pod body received. #pod * C — #pod Override host resolution and force all connections to go only to a #pod specific peer address, regardless of the URL of the request. This will #pod include any redirections! This options should be used with extreme #pod caution (e.g. debugging or very special circumstances). It can be given as #pod either a scalar or a code reference that will receive the hostname and #pod whose response will be taken as the address. #pod #pod The C header is generated from the URL in accordance with RFC 2616. It #pod is a fatal error to specify C in the C option. Other headers #pod may be ignored or overwritten if necessary for transport compliance. #pod #pod If the C option is a code reference, it will be called iteratively #pod to provide the content body of the request. It should return the empty #pod string or undef when the iterator is exhausted. #pod #pod If the C option is the empty string, no C or #pod C headers will be generated. #pod #pod If the C option is provided, it will be called iteratively until #pod the entire response body is received. The first argument will be a string #pod containing a chunk of the response body, the second argument will be the #pod in-progress response hash reference, as described below. (This allows #pod customizing the action of the callback based on the C or C #pod received prior to the content body.) #pod #pod Content data in the request/response is handled as "raw bytes". Any #pod encoding/decoding (with associated headers) are the responsibility of the #pod caller. #pod #pod The C method returns a hashref containing the response. The hashref #pod will have the following keys: #pod #pod =for :list #pod * C — #pod Boolean indicating whether the operation returned a 2XX status code #pod * C — #pod URL that provided the response. This is the URL of the request unless #pod there were redirections, in which case it is the last URL queried #pod in a redirection chain #pod * C — #pod The HTTP status code of the response #pod * C — #pod The response phrase returned by the server #pod * C — #pod The body of the response. If the response does not have any content #pod or if a data callback is provided to consume the response body, #pod this will be the empty string #pod * C — #pod A hashref of header fields. All header field names will be normalized #pod to be lower case. If a header is repeated, the value will be an arrayref; #pod it will otherwise be a scalar string containing the value #pod * C - #pod If this field exists, it is the protocol of the response #pod such as HTTP/1.0 or HTTP/1.1 #pod * C #pod If this field exists, it is an arrayref of response hash references from #pod redirects in the same order that redirections occurred. If it does #pod not exist, then no redirections occurred. #pod #pod On an error during the execution of the request, the C field will #pod contain 599, and the C field will contain the text of the error. #pod #pod =cut my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/; sub request { my ($self, $method, $url, $args) = @_; @_ == 3 || (@_ == 4 && ref $args eq 'HASH') or _croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n"); $args ||= {}; # we keep some state in this during _request # RFC 2616 Section 8.1.4 mandates a single retry on broken socket my $response; for ( 0 .. 1 ) { $response = eval { $self->_request($method, $url, $args) }; last unless $@ && $idempotent{$method} && $@ =~ m{^(?:Socket closed|Unexpected end|SSL read error)}; } if (my $e = $@) { # maybe we got a response hash thrown from somewhere deep if ( ref $e eq 'HASH' && exists $e->{status} ) { $e->{redirects} = delete $args->{_redirects} if @{ $args->{_redirects} || []}; return $e; } # otherwise, stringify it $e = "$e"; $response = { url => $url, success => q{}, status => 599, reason => 'Internal Exception', content => $e, headers => { 'content-type' => 'text/plain', 'content-length' => length $e, }, ( @{$args->{_redirects} || []} ? (redirects => delete $args->{_redirects}) : () ), }; } return $response; } #pod =method www_form_urlencode #pod #pod $params = $http->www_form_urlencode( $data ); #pod $response = $http->get("http://example.com/query?$params"); #pod #pod This method converts the key/value pairs from a data hash or array reference #pod into a C string. The keys and values from the data #pod reference will be UTF-8 encoded and escaped per RFC 3986. If a value is an #pod array reference, the key will be repeated with each of the values of the array #pod reference. If data is provided as a hash reference, the key/value pairs in the #pod resulting string will be sorted by key and value for consistent ordering. #pod #pod =cut sub www_form_urlencode { my ($self, $data) = @_; (@_ == 2 && ref $data) or _croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n"); (ref $data eq 'HASH' || ref $data eq 'ARRAY') or _croak("form data must be a hash or array reference\n"); my @params = ref $data eq 'HASH' ? map { ($_ => $data->{$_}) } sort keys %$data : @$data; @params % 2 == 0 or _croak("form data reference must have an even number of terms\n"); my @terms; while( @params ) { my ($key, $value) = splice(@params, 0, 2); _croak("form data keys must not be undef") if !defined($key); if ( ref $value eq 'ARRAY' ) { unshift @params, map { $key => $_ } @$value; } else { push @terms, join("=", map { $self->_uri_escape($_) } $key, $value); } } return join("&", @terms); } #pod =method can_ssl #pod #pod $ok = HTTP::Tiny->can_ssl; #pod ($ok, $why) = HTTP::Tiny->can_ssl; #pod ($ok, $why) = $http->can_ssl; #pod #pod Indicates if SSL support is available. When called as a class object, it #pod checks for the correct version of L and L. #pod When called as an object methods, if C is true or if C #pod is set in C, it checks that a CA file is available. #pod #pod In scalar context, returns a boolean indicating if SSL is available. #pod In list context, returns the boolean and a (possibly multi-line) string of #pod errors indicating why SSL isn't available. #pod #pod =cut sub can_ssl { my ($self) = @_; my($ok, $reason) = (1, ''); # Need IO::Socket::SSL 1.968 for default_ca() local @INC = @INC; pop @INC if $INC[-1] eq '.'; unless (eval {require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.968)}) { $ok = 0; $reason .= qq/IO::Socket::SSL 1.968 or later must be installed for https support\n/; } # Need Net::SSLeay 1.49 for MODE_AUTO_RETRY unless (eval {require Net::SSLeay; Net::SSLeay->VERSION(1.49)}) { $ok = 0; $reason .= qq/Net::SSLeay 1.49 or later must be installed for https support\n/; } # If an object, check that SSL config lets us get a CA if necessary if ( ref($self) && ( $self->{verify_SSL} || $self->{SSL_options}{SSL_verify_mode} ) ) { my $handle = HTTP::Tiny::Handle->new( SSL_options => $self->{SSL_options}, verify_SSL => $self->{verify_SSL}, ); unless ( eval { $handle->_find_CA; 1 } ) { $ok = 0; $reason .= "$@"; } } wantarray ? ($ok, $reason) : $ok; } #pod =method connected #pod #pod $host = $http->connected; #pod ($host, $port) = $http->connected; #pod #pod Indicates if a connection to a peer is being kept alive, per the C #pod option. #pod #pod In scalar context, returns the peer host and port, joined with a colon, or #pod C (if no peer is connected). #pod In list context, returns the peer host and port or an empty list (if no peer #pod is connected). #pod #pod B: This method cannot reliably be used to discover whether the remote #pod host has closed its end of the socket. #pod #pod =cut sub connected { my ($self) = @_; if ( $self->{handle} ) { return $self->{handle}->connected; } return; } #--------------------------------------------------------------------------# # private methods #--------------------------------------------------------------------------# my %DefaultPort = ( http => 80, https => 443, ); sub _agent { my $class = ref($_[0]) || $_[0]; (my $default_agent = $class) =~ s{::}{-}g; my $version = $class->VERSION; $default_agent .= "/$version" if defined $version; return $default_agent; } sub _request { my ($self, $method, $url, $args) = @_; my ($scheme, $host, $port, $path_query, $auth) = $self->_split_url($url); if ($scheme ne 'http' && $scheme ne 'https') { die(qq/Unsupported URL scheme '$scheme'\n/); } my $request = { method => $method, scheme => $scheme, host => $host, port => $port, host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"), uri => $path_query, headers => {}, }; my $peer = $args->{peer} || $host; # Allow 'peer' to be a coderef. if ('CODE' eq ref $peer) { $peer = $peer->($host); } # We remove the cached handle so it is not reused in the case of redirect. # If all is well, it will be recached at the end of _request. We only # reuse for the same scheme, host and port my $handle = delete $self->{handle}; if ( $handle ) { unless ( $handle->can_reuse( $scheme, $host, $port, $peer ) ) { $handle->close; undef $handle; } } $handle ||= $self->_open_handle( $request, $scheme, $host, $port, $peer ); $self->_prepare_headers_and_cb($request, $args, $url, $auth); $handle->write_request($request); my $response; do { $response = $handle->read_response_header } until (substr($response->{status},0,1) ne '1'); $self->_update_cookie_jar( $url, $response ) if $self->{cookie_jar}; my @redir_args = $self->_maybe_redirect($request, $response, $args); my $known_message_length; if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) { # response has no message body $known_message_length = 1; } else { # Ignore any data callbacks during redirection. my $cb_args = @redir_args ? +{} : $args; my $data_cb = $self->_prepare_data_cb($response, $cb_args); $known_message_length = $handle->read_body($data_cb, $response); } if ( $self->{keep_alive} && $handle->connected && $known_message_length && $response->{protocol} eq 'HTTP/1.1' && ($response->{headers}{connection} || '') ne 'close' ) { $self->{handle} = $handle; } else { $handle->close; } $response->{success} = substr( $response->{status}, 0, 1 ) eq '2'; $response->{url} = $url; # Push the current response onto the stack of redirects if redirecting. if (@redir_args) { push @{$args->{_redirects}}, $response; return $self->_request(@redir_args, $args); } # Copy the stack of redirects into the response before returning. $response->{redirects} = delete $args->{_redirects} if @{$args->{_redirects}}; return $response; } sub _open_handle { my ($self, $request, $scheme, $host, $port, $peer) = @_; my $handle = HTTP::Tiny::Handle->new( timeout => $self->{timeout}, SSL_options => $self->{SSL_options}, verify_SSL => $self->{verify_SSL}, local_address => $self->{local_address}, keep_alive => $self->{keep_alive} ); if ($self->{_has_proxy}{$scheme} && ! grep { $host =~ /\Q$_\E$/ } @{$self->{no_proxy}}) { return $self->_proxy_connect( $request, $handle ); } else { return $handle->connect($scheme, $host, $port, $peer); } } sub _proxy_connect { my ($self, $request, $handle) = @_; my @proxy_vars; if ( $request->{scheme} eq 'https' ) { _croak(qq{No https_proxy defined}) unless $self->{https_proxy}; @proxy_vars = $self->_split_proxy( https_proxy => $self->{https_proxy} ); if ( $proxy_vars[0] eq 'https' ) { _croak(qq{Can't proxy https over https: $request->{uri} via $self->{https_proxy}}); } } else { _croak(qq{No http_proxy defined}) unless $self->{http_proxy}; @proxy_vars = $self->_split_proxy( http_proxy => $self->{http_proxy} ); } my ($p_scheme, $p_host, $p_port, $p_auth) = @proxy_vars; if ( length $p_auth && ! defined $request->{headers}{'proxy-authorization'} ) { $self->_add_basic_auth_header( $request, 'proxy-authorization' => $p_auth ); } $handle->connect($p_scheme, $p_host, $p_port, $p_host); if ($request->{scheme} eq 'https') { $self->_create_proxy_tunnel( $request, $handle ); } else { # non-tunneled proxy requires absolute URI $request->{uri} = "$request->{scheme}://$request->{host_port}$request->{uri}"; } return $handle; } sub _split_proxy { my ($self, $type, $proxy) = @_; my ($scheme, $host, $port, $path_query, $auth) = eval { $self->_split_url($proxy) }; unless( defined($scheme) && length($scheme) && length($host) && length($port) && $path_query eq '/' ) { _croak(qq{$type URL must be in format http[s]://[auth@]:/\n}); } return ($scheme, $host, $port, $auth); } sub _create_proxy_tunnel { my ($self, $request, $handle) = @_; $handle->_assert_ssl; my $agent = exists($request->{headers}{'user-agent'}) ? $request->{headers}{'user-agent'} : $self->{agent}; my $connect_request = { method => 'CONNECT', uri => "$request->{host}:$request->{port}", headers => { host => "$request->{host}:$request->{port}", 'user-agent' => $agent, } }; if ( $request->{headers}{'proxy-authorization'} ) { $connect_request->{headers}{'proxy-authorization'} = delete $request->{headers}{'proxy-authorization'}; } $handle->write_request($connect_request); my $response; do { $response = $handle->read_response_header } until (substr($response->{status},0,1) ne '1'); # if CONNECT failed, throw the response so it will be # returned from the original request() method; unless (substr($response->{status},0,1) eq '2') { die $response; } # tunnel established, so start SSL handshake $handle->start_ssl( $request->{host} ); return; } sub _prepare_headers_and_cb { my ($self, $request, $args, $url, $auth) = @_; for ($self->{default_headers}, $args->{headers}) { next unless defined; while (my ($k, $v) = each %$_) { $request->{headers}{lc $k} = $v; $request->{header_case}{lc $k} = $k; } } if (exists $request->{headers}{'host'}) { die(qq/The 'Host' header must not be provided as header option\n/); } $request->{headers}{'host'} = $request->{host_port}; $request->{headers}{'user-agent'} ||= $self->{agent}; $request->{headers}{'connection'} = "close" unless $self->{keep_alive}; # Some servers error on an empty-body PUT/POST without a content-length if ( $request->{method} eq 'PUT' || $request->{method} eq 'POST' ) { if (!defined($args->{content}) || !length($args->{content}) ) { $request->{headers}{'content-length'} = 0; } } if ( defined $args->{content} ) { if ( ref $args->{content} eq 'CODE' ) { if ( exists $request->{'content-length'} && $request->{'content-length'} == 0 ) { $request->{cb} = sub { "" }; } else { $request->{headers}{'content-type'} ||= "application/octet-stream"; $request->{headers}{'transfer-encoding'} = 'chunked' unless exists $request->{headers}{'content-length'} || $request->{headers}{'transfer-encoding'}; $request->{cb} = $args->{content}; } } elsif ( length $args->{content} ) { my $content = $args->{content}; if ( $] ge '5.008' ) { utf8::downgrade($content, 1) or die(qq/Wide character in request message body\n/); } $request->{headers}{'content-type'} ||= "application/octet-stream"; $request->{headers}{'content-length'} = length $content unless $request->{headers}{'content-length'} || $request->{headers}{'transfer-encoding'}; $request->{cb} = sub { substr $content, 0, length $content, '' }; } $request->{trailer_cb} = $args->{trailer_callback} if ref $args->{trailer_callback} eq 'CODE'; } ### If we have a cookie jar, then maybe add relevant cookies if ( $self->{cookie_jar} ) { my $cookies = $self->cookie_jar->cookie_header( $url ); $request->{headers}{cookie} = $cookies if length $cookies; } # if we have Basic auth parameters, add them if ( length $auth && ! defined $request->{headers}{authorization} ) { $self->_add_basic_auth_header( $request, 'authorization' => $auth ); } return; } sub _add_basic_auth_header { my ($self, $request, $header, $auth) = @_; require MIME::Base64; $request->{headers}{$header} = "Basic " . MIME::Base64::encode_base64($auth, ""); return; } sub _prepare_data_cb { my ($self, $response, $args) = @_; my $data_cb = $args->{data_callback}; $response->{content} = ''; if (!$data_cb || $response->{status} !~ /^2/) { if (defined $self->{max_size}) { $data_cb = sub { $_[1]->{content} .= $_[0]; die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/) if length $_[1]->{content} > $self->{max_size}; }; } else { $data_cb = sub { $_[1]->{content} .= $_[0] }; } } return $data_cb; } sub _update_cookie_jar { my ($self, $url, $response) = @_; my $cookies = $response->{headers}->{'set-cookie'}; return unless defined $cookies; my @cookies = ref $cookies ? @$cookies : $cookies; $self->cookie_jar->add( $url, $_ ) for @cookies; return; } sub _validate_cookie_jar { my ($class, $jar) = @_; # duck typing for my $method ( qw/add cookie_header/ ) { _croak(qq/Cookie jar must provide the '$method' method\n/) unless ref($jar) && ref($jar)->can($method); } return; } sub _maybe_redirect { my ($self, $request, $response, $args) = @_; my $headers = $response->{headers}; my ($status, $method) = ($response->{status}, $request->{method}); $args->{_redirects} ||= []; if (($status eq '303' or ($status =~ /^30[1278]/ && $method =~ /^GET|HEAD$/)) and $headers->{location} and @{$args->{_redirects}} < $self->{max_redirect} ) { my $location = ($headers->{location} =~ /^\//) ? "$request->{scheme}://$request->{host_port}$headers->{location}" : $headers->{location} ; return (($status eq '303' ? 'GET' : $method), $location); } return; } sub _split_url { my $url = pop; # URI regex adapted from the URI module my ($scheme, $host, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)> or die(qq/Cannot parse URL: '$url'\n/); $scheme = lc $scheme; $path_query = "/$path_query" unless $path_query =~ m<\A/>; my $auth = ''; if ( (my $i = index $host, '@') != -1 ) { # user:pass@host $auth = substr $host, 0, $i, ''; # take up to the @ for auth substr $host, 0, 1, ''; # knock the @ off the host # userinfo might be percent escaped, so recover real auth info $auth =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; } my $port = $host =~ s/:(\d*)\z// && length $1 ? $1 : $scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef; return ($scheme, (length $host ? lc $host : "localhost") , $port, $path_query, $auth); } # Date conversions adapted from HTTP::Date my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat"; my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"; sub _http_date { my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]); return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT", substr($DoW,$wday*4,3), $mday, substr($MoY,$mon*4,3), $year+1900, $hour, $min, $sec ); } sub _parse_http_date { my ($self, $str) = @_; require Time::Local; my @tl_parts; if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) { @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); } elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) { @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); } elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) { @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6); } return eval { my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1; $t < 0 ? undef : $t; }; } # URI escaping adapted from URI::Escape # c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 # perl 5.6 ready UTF-8 encoding adapted from JSON::PP my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255; $escapes{' '}="+"; my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/; sub _uri_escape { my ($self, $str) = @_; return "" if !defined $str; if ( $] ge '5.008' ) { utf8::encode($str); } else { $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string if ( length $str == do { use bytes; length $str } ); $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag } $str =~ s/($unsafe_char)/$escapes{$1}/g; return $str; } package HTTP::Tiny::Handle; # hide from PAUSE/indexers use strict; use warnings; use Errno qw[EINTR EPIPE]; use IO::Socket qw[SOCK_STREAM]; use Socket qw[SOL_SOCKET SO_KEEPALIVE]; # PERL_HTTP_TINY_IPV4_ONLY is a private environment variable to force old # behavior if someone is unable to boostrap CPAN from a new perl install; it is # not intended for general, per-client use and may be removed in the future my $SOCKET_CLASS = $ENV{PERL_HTTP_TINY_IPV4_ONLY} ? 'IO::Socket::INET' : eval { require IO::Socket::IP; IO::Socket::IP->VERSION(0.32) } ? 'IO::Socket::IP' : 'IO::Socket::INET'; sub BUFSIZE () { 32768 } ## no critic my $Printable = sub { local $_ = shift; s/\r/\\r/g; s/\n/\\n/g; s/\t/\\t/g; s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge; $_; }; my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/; my $Field_Content = qr/[[:print:]]+ (?: [\x20\x09]+ [[:print:]]+ )*/x; sub new { my ($class, %args) = @_; return bless { rbuf => '', timeout => 60, max_line_size => 16384, max_header_lines => 64, verify_SSL => HTTP::Tiny::_verify_SSL_default(), SSL_options => {}, %args }, $class; } sub timeout { my ($self, $timeout) = @_; if ( @_ > 1 ) { $self->{timeout} = $timeout; if ( $self->{fh} && $self->{fh}->can('timeout') ) { $self->{fh}->timeout($timeout); } } return $self->{timeout}; } sub connect { @_ == 5 || die(q/Usage: $handle->connect(scheme, host, port, peer)/ . "\n"); my ($self, $scheme, $host, $port, $peer) = @_; if ( $scheme eq 'https' ) { $self->_assert_ssl; } $self->{fh} = $SOCKET_CLASS->new( PeerHost => $peer, PeerPort => $port, $self->{local_address} ? ( LocalAddr => $self->{local_address} ) : (), Proto => 'tcp', Type => SOCK_STREAM, Timeout => $self->{timeout}, ) or die(qq/Could not connect to '$host:$port': $@\n/); binmode($self->{fh}) or die(qq/Could not binmode() socket: '$!'\n/); if ( $self->{keep_alive} ) { unless ( defined( $self->{fh}->setsockopt( SOL_SOCKET, SO_KEEPALIVE, 1 ) ) ) { CORE::close($self->{fh}); die(qq/Could not set SO_KEEPALIVE on socket: '$!'\n/); } } $self->start_ssl($host) if $scheme eq 'https'; $self->{scheme} = $scheme; $self->{host} = $host; $self->{peer} = $peer; $self->{port} = $port; $self->{pid} = $$; $self->{tid} = _get_tid(); return $self; } sub connected { my ($self) = @_; if ( $self->{fh} && $self->{fh}->connected ) { return wantarray ? ( $self->{fh}->peerhost, $self->{fh}->peerport ) : join( ':', $self->{fh}->peerhost, $self->{fh}->peerport ); } return; } sub start_ssl { my ($self, $host) = @_; # As this might be used via CONNECT after an SSL session # to a proxy, we shut down any existing SSL before attempting # the handshake if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) { unless ( $self->{fh}->stop_SSL ) { my $ssl_err = IO::Socket::SSL->errstr; die(qq/Error halting prior SSL connection: $ssl_err/); } } my $ssl_args = $self->_ssl_args($host); IO::Socket::SSL->start_SSL( $self->{fh}, %$ssl_args, SSL_create_ctx_callback => sub { my $ctx = shift; Net::SSLeay::CTX_set_mode($ctx, Net::SSLeay::MODE_AUTO_RETRY()); }, ); unless ( ref($self->{fh}) eq 'IO::Socket::SSL' ) { my $ssl_err = IO::Socket::SSL->errstr; die(qq/SSL connection failed for $host: $ssl_err\n/); } } sub close { @_ == 1 || die(q/Usage: $handle->close()/ . "\n"); my ($self) = @_; CORE::close($self->{fh}) or die(qq/Could not close socket: '$!'\n/); } sub write { @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n"); my ($self, $buf) = @_; if ( $] ge '5.008' ) { utf8::downgrade($buf, 1) or die(qq/Wide character in write()\n/); } my $len = length $buf; my $off = 0; local $SIG{PIPE} = 'IGNORE'; while () { $self->can_write or die(qq/Timed out while waiting for socket to become ready for writing\n/); my $r = syswrite($self->{fh}, $buf, $len, $off); if (defined $r) { $len -= $r; $off += $r; last unless $len > 0; } elsif ($! == EPIPE) { die(qq/Socket closed by remote server: $!\n/); } elsif ($! != EINTR) { if ($self->{fh}->can('errstr')){ my $err = $self->{fh}->errstr(); die (qq/Could not write to SSL socket: '$err'\n /); } else { die(qq/Could not write to socket: '$!'\n/); } } } return $off; } sub read { @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n"); my ($self, $len, $allow_partial) = @_; my $buf = ''; my $got = length $self->{rbuf}; if ($got) { my $take = ($got < $len) ? $got : $len; $buf = substr($self->{rbuf}, 0, $take, ''); $len -= $take; } # Ignore SIGPIPE because SSL reads can result in writes that might error. # See "Expecting exactly the same behavior as plain sockets" in # https://metacpan.org/dist/IO-Socket-SSL/view/lib/IO/Socket/SSL.pod#Common-Usage-Errors local $SIG{PIPE} = 'IGNORE'; while ($len > 0) { $self->can_read or die(q/Timed out while waiting for socket to become ready for reading/ . "\n"); my $r = sysread($self->{fh}, $buf, $len, length $buf); if (defined $r) { last unless $r; $len -= $r; } elsif ($! != EINTR) { if ($self->{fh}->can('errstr')){ my $err = $self->{fh}->errstr(); die (qq/Could not read from SSL socket: '$err'\n /); } else { die(qq/Could not read from socket: '$!'\n/); } } } if ($len && !$allow_partial) { die(qq/Unexpected end of stream\n/); } return $buf; } sub readline { @_ == 1 || die(q/Usage: $handle->readline()/ . "\n"); my ($self) = @_; while () { if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) { return $1; } if (length $self->{rbuf} >= $self->{max_line_size}) { die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/); } $self->can_read or die(qq/Timed out while waiting for socket to become ready for reading\n/); my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf}); if (defined $r) { last unless $r; } elsif ($! != EINTR) { if ($self->{fh}->can('errstr')){ my $err = $self->{fh}->errstr(); die (qq/Could not read from SSL socket: '$err'\n /); } else { die(qq/Could not read from socket: '$!'\n/); } } } die(qq/Unexpected end of stream while looking for line\n/); } sub read_header_lines { @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n"); my ($self, $headers) = @_; $headers ||= {}; my $lines = 0; my $val; while () { my $line = $self->readline; if (++$lines >= $self->{max_header_lines}) { die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/); } elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) { my ($field_name) = lc $1; if (exists $headers->{$field_name}) { for ($headers->{$field_name}) { $_ = [$_] unless ref $_ eq "ARRAY"; push @$_, $2; $val = \$_->[-1]; } } else { $val = \($headers->{$field_name} = $2); } } elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) { $val or die(qq/Unexpected header continuation line\n/); next unless length $1; $$val .= ' ' if length $$val; $$val .= $1; } elsif ($line =~ /\A \x0D?\x0A \z/x) { last; } else { die(q/Malformed header line: / . $Printable->($line) . "\n"); } } return $headers; } sub write_request { @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n"); my($self, $request) = @_; $self->write_request_header(@{$request}{qw/method uri headers header_case/}); $self->write_body($request) if $request->{cb}; return; } # Standard request header names/case from HTTP/1.1 RFCs my @rfc_request_headers = qw( Accept Accept-Charset Accept-Encoding Accept-Language Authorization Cache-Control Connection Content-Length Expect From Host If-Match If-Modified-Since If-None-Match If-Range If-Unmodified-Since Max-Forwards Pragma Proxy-Authorization Range Referer TE Trailer Transfer-Encoding Upgrade User-Agent Via ); my @other_request_headers = qw( Content-Encoding Content-MD5 Content-Type Cookie DNT Date Origin X-XSS-Protection ); my %HeaderCase = map { lc($_) => $_ } @rfc_request_headers, @other_request_headers; # to avoid multiple small writes and hence nagle, you can pass the method line or anything else to # combine writes. sub write_header_lines { (@_ >= 2 && @_ <= 4 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers, [header_case, prefix])/ . "\n"); my($self, $headers, $header_case, $prefix_data) = @_; $header_case ||= {}; my $buf = (defined $prefix_data ? $prefix_data : ''); # Per RFC, control fields should be listed first my %seen; for my $k ( qw/host cache-control expect max-forwards pragma range te/ ) { next unless exists $headers->{$k}; $seen{$k}++; my $field_name = $HeaderCase{$k}; my $v = $headers->{$k}; for (ref $v eq 'ARRAY' ? @$v : $v) { $_ = '' unless defined $_; $buf .= "$field_name: $_\x0D\x0A"; } } # Other headers sent in arbitrary order while (my ($k, $v) = each %$headers) { my $field_name = lc $k; next if $seen{$field_name}; if (exists $HeaderCase{$field_name}) { $field_name = $HeaderCase{$field_name}; } else { if (exists $header_case->{$field_name}) { $field_name = $header_case->{$field_name}; } else { $field_name =~ s/\b(\w)/\u$1/g; } $field_name =~ /\A $Token+ \z/xo or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n"); $HeaderCase{lc $field_name} = $field_name; } for (ref $v eq 'ARRAY' ? @$v : $v) { # unwrap a field value if pre-wrapped by user s/\x0D?\x0A\s+/ /g; die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n") unless $_ eq '' || /\A $Field_Content \z/xo; $_ = '' unless defined $_; $buf .= "$field_name: $_\x0D\x0A"; } } $buf .= "\x0D\x0A"; return $self->write($buf); } # return value indicates whether message length was defined; this is generally # true unless there was no content-length header and we just read until EOF. # Other message length errors are thrown as exceptions sub read_body { @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n"); my ($self, $cb, $response) = @_; my $te = $response->{headers}{'transfer-encoding'} || ''; my $chunked = grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ; return $chunked ? $self->read_chunked_body($cb, $response) : $self->read_content_body($cb, $response); } sub write_body { @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n"); my ($self, $request) = @_; if (exists $request->{headers}{'content-length'}) { return unless $request->{headers}{'content-length'}; return $self->write_content_body($request); } else { return $self->write_chunked_body($request); } } sub read_content_body { @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n"); my ($self, $cb, $response, $content_length) = @_; $content_length ||= $response->{headers}{'content-length'}; if ( defined $content_length ) { my $len = $content_length; while ($len > 0) { my $read = ($len > BUFSIZE) ? BUFSIZE : $len; $cb->($self->read($read, 0), $response); $len -= $read; } return length($self->{rbuf}) == 0; } my $chunk; $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) ); return; } sub write_content_body { @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n"); my ($self, $request) = @_; my ($len, $content_length) = (0, $request->{headers}{'content-length'}); while () { my $data = $request->{cb}->(); defined $data && length $data or last; if ( $] ge '5.008' ) { utf8::downgrade($data, 1) or die(qq/Wide character in write_content()\n/); } $len += $self->write($data); } $len == $content_length or die(qq/Content-Length mismatch (got: $len expected: $content_length)\n/); return $len; } sub read_chunked_body { @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n"); my ($self, $cb, $response) = @_; while () { my $head = $self->readline; $head =~ /\A ([A-Fa-f0-9]+)/x or die(q/Malformed chunk head: / . $Printable->($head) . "\n"); my $len = hex($1) or last; $self->read_content_body($cb, $response, $len); $self->read(2) eq "\x0D\x0A" or die(qq/Malformed chunk: missing CRLF after chunk data\n/); } $self->read_header_lines($response->{headers}); return 1; } sub write_chunked_body { @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n"); my ($self, $request) = @_; my $len = 0; while () { my $data = $request->{cb}->(); defined $data && length $data or last; if ( $] ge '5.008' ) { utf8::downgrade($data, 1) or die(qq/Wide character in write_chunked_body()\n/); } $len += length $data; my $chunk = sprintf '%X', length $data; $chunk .= "\x0D\x0A"; $chunk .= $data; $chunk .= "\x0D\x0A"; $self->write($chunk); } $self->write("0\x0D\x0A"); if ( ref $request->{trailer_cb} eq 'CODE' ) { $self->write_header_lines($request->{trailer_cb}->()) } else { $self->write("\x0D\x0A"); } return $len; } sub read_response_header { @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n"); my ($self) = @_; my $line = $self->readline; $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) (?: [\x09\x20]+ ([^\x0D\x0A]*) )? \x0D?\x0A/x or die(q/Malformed Status-Line: / . $Printable->($line). "\n"); my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4); $reason = "" unless defined $reason; die (qq/Unsupported HTTP protocol: $protocol\n/) unless $version =~ /0*1\.0*[01]/; return { status => $status, reason => $reason, headers => $self->read_header_lines, protocol => $protocol, }; } sub write_request_header { @_ == 5 || die(q/Usage: $handle->write_request_header(method, request_uri, headers, header_case)/ . "\n"); my ($self, $method, $request_uri, $headers, $header_case) = @_; return $self->write_header_lines($headers, $header_case, "$method $request_uri HTTP/1.1\x0D\x0A"); } sub _do_timeout { my ($self, $type, $timeout) = @_; $timeout = $self->{timeout} unless defined $timeout && $timeout >= 0; my $fd = fileno $self->{fh}; defined $fd && $fd >= 0 or die(qq/select(2): 'Bad file descriptor'\n/); my $initial = time; my $pending = $timeout; my $nfound; vec(my $fdset = '', $fd, 1) = 1; while () { $nfound = ($type eq 'read') ? select($fdset, undef, undef, $pending) : select(undef, $fdset, undef, $pending) ; if ($nfound == -1) { $! == EINTR or die(qq/select(2): '$!'\n/); redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; $nfound = 0; } last; } $! = 0; return $nfound; } sub can_read { @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n"); my $self = shift; if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) { return 1 if $self->{fh}->pending; } return $self->_do_timeout('read', @_) } sub can_write { @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n"); my $self = shift; return $self->_do_timeout('write', @_) } sub _assert_ssl { my($ok, $reason) = HTTP::Tiny->can_ssl(); die $reason unless $ok; } sub can_reuse { my ($self,$scheme,$host,$port,$peer) = @_; return 0 if $self->{pid} != $$ || $self->{tid} != _get_tid() || length($self->{rbuf}) || $scheme ne $self->{scheme} || $host ne $self->{host} || $port ne $self->{port} || $peer ne $self->{peer} || eval { $self->can_read(0) } || $@ ; return 1; } sub _find_CA { my $self = shift; my $ca_file = $self->{SSL_options}->{SSL_ca_file}; if ( defined $ca_file ) { unless ( -r $ca_file ) { die qq/SSL_ca_file '$ca_file' not found or not readable\n/; } return ( SSL_ca_file => $ca_file ); } # Return default_ca() parameters from IO::Socket::SSL. It looks for the # default bundle and directory from Net::SSLeay, handles $ENV{SSL_CERT_FILE} # and $ENV{SSL_CERT_DIR}, and finally fails over to Mozilla::CA # my %default_ca = IO::Socket::SSL::default_ca(); return %default_ca if %default_ca; # If IO::Socket::SSL::default_ca() was unable to find a CA bundle, look for # one in well known locations as a last resort. Cert list copied from golang # src/crypto/x509/root_unix.go # foreach my $ca_bundle ( "/etc/ssl/certs/ca-certificates.crt", # Debian/Ubuntu/Gentoo etc. "/etc/pki/tls/certs/ca-bundle.crt", # Fedora/RHEL "/etc/ssl/ca-bundle.pem", # OpenSUSE "/etc/openssl/certs/ca-certificates.crt", # NetBSD "/etc/ssl/cert.pem", # OpenBSD "/usr/local/share/certs/ca-root-nss.crt", # FreeBSD/DragonFly "/etc/pki/tls/cacert.pem", # OpenELEC "/etc/certs/ca-certificates.crt", # Solaris 11.2+ ) { return ( SSL_ca_file => $ca_bundle ) if -e $ca_bundle; } die qq/Couldn't find a CA bundle with which to verify the SSL certificate.\n/ . qq/Try installing one from your OS vendor, or Mozilla::CA from CPAN\n/; } # not for internal use; backcompat shim only sub _find_CA_file { my $self = shift; my %res = $self->_find_CA(); return $res{SSL_ca_file}; } # for thread safety, we need to know thread id if threads are loaded sub _get_tid { no warnings 'reserved'; # for 'threads' return threads->can("tid") ? threads->tid : 0; } sub _ssl_args { my ($self, $host) = @_; my %ssl_args; # This test reimplements IO::Socket::SSL::can_client_sni(), which wasn't # added until IO::Socket::SSL 1.84 if ( Net::SSLeay::OPENSSL_VERSION_NUMBER() >= 0x01000000 ) { $ssl_args{SSL_hostname} = $host, # Sane SNI support } if ($self->{verify_SSL}) { $ssl_args{SSL_verifycn_scheme} = 'http'; # enable CN validation $ssl_args{SSL_verifycn_name} = $host; # set validation hostname $ssl_args{SSL_verify_mode} = 0x01; # enable cert validation %ssl_args = ( %ssl_args, $self->_find_CA ); } else { $ssl_args{SSL_verifycn_scheme} = 'none'; # disable CN validation $ssl_args{SSL_verify_mode} = 0x00; # disable cert validation } # user options override settings from verify_SSL for my $k ( keys %{$self->{SSL_options}} ) { $ssl_args{$k} = $self->{SSL_options}{$k} if $k =~ m/^SSL_/; } return \%ssl_args; } 1; __END__ =pod =encoding UTF-8 =head1 NAME HTTP::Tiny - A small, simple, correct HTTP/1.1 client =head1 VERSION version 0.090 =head1 SYNOPSIS use HTTP::Tiny; my $response = HTTP::Tiny->new->get('http://example.com/'); die "Failed!\n" unless $response->{success}; print "$response->{status} $response->{reason}\n"; while (my ($k, $v) = each %{$response->{headers}}) { for (ref $v eq 'ARRAY' ? @$v : $v) { print "$k: $_\n"; } } print $response->{content} if length $response->{content}; =head1 DESCRIPTION This is a very simple HTTP/1.1 client, designed for doing simple requests without the overhead of a large framework like L. It is more correct and more complete than L. It supports proxies and redirection. It also correctly resumes after EINTR. If L 0.25 or later is installed, HTTP::Tiny will use it instead of L for transparent support for both IPv4 and IPv6. Cookie support requires L or an equivalent class. =head1 METHODS =head2 new $http = HTTP::Tiny->new( %attributes ); This constructor returns a new HTTP::Tiny object. Valid attributes include: =over 4 =item * C — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If C — ends in a space character, the default user-agent string is appended. =item * C — An instance of L — or equivalent class that supports the C and C methods =item * C — A hashref of default headers to apply to requests =item * C — The local IP address to bind to =item * C — Whether to reuse the last connection (if for the same scheme, host and port) (defaults to 1) =item * C — Maximum number of redirects allowed (defaults to 5) =item * C — Maximum response size in bytes (only when not using a data callback). If defined, requests with responses larger than this will return a 599 status code. =item * C — URL of a proxy server to use for HTTP connections (default is C<$ENV{http_proxy}> — if set) =item * C — URL of a proxy server to use for HTTPS connections (default is C<$ENV{https_proxy}> — if set) =item * C — URL of a generic proxy server for both HTTP and HTTPS connections (default is C<$ENV{all_proxy}> — if set) =item * C — List of domain suffixes that should not be proxied. Must be a comma-separated string or an array reference. (default is C<$ENV{no_proxy}> —) =item * C — Request timeout in seconds (default is 60) If a socket open, read or write takes longer than the timeout, the request response status code will be 599. =item * C — A boolean that indicates whether to validate the TLS/SSL certificate of an C — connection (default is true). Changed from false to true in version 0.083. =item * C — A hashref of C — options to pass through to L =item * C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}> - Changes the default certificate verification behavior to not check server identity if set to 1. Only effective if C is not set. Added in version 0.083. =back An accessor/mutator method exists for each attribute. Passing an explicit C for C, C or C will prevent getting the corresponding proxies from the environment. Errors during request execution will result in a pseudo-HTTP status code of 599 and a reason of "Internal Exception". The content field in the response will contain the text of the error. The C parameter enables a persistent connection, but only to a single destination scheme, host and port. If any connection-relevant attributes are modified via accessor, or if the process ID or thread ID change, the persistent connection will be dropped. If you want persistent connections across multiple destinations, use multiple HTTP::Tiny objects. See L for more on the C and C attributes. =head2 get|head|put|post|patch|delete $response = $http->get($url); $response = $http->get($url, \%options); $response = $http->head($url); These methods are shorthand for calling C for the given method. The URL must have unsafe characters escaped and international domain names encoded. See C for valid options and a description of the response. The C field of the response will be true if the status code is 2XX. =head2 post_form $response = $http->post_form($url, $form_data); $response = $http->post_form($url, $form_data, \%options); This method executes a C request and sends the key/value pairs from a form data hash or array reference to the given URL with a C of C. If data is provided as an array reference, the order is preserved; if provided as a hash reference, the terms are sorted by key for consistency. See documentation for the C method for details on the encoding. The URL must have unsafe characters escaped and international domain names encoded. See C for valid options and a description of the response. Any C header or content in the options hashref will be ignored. The C field of the response will be true if the status code is 2XX. =head2 mirror $response = $http->mirror($url, $file, \%options) if ( $response->{success} ) { print "$file is up to date\n"; } Executes a C request for the URL and saves the response body to the file name provided. The URL must have unsafe characters escaped and international domain names encoded. If the file already exists, the request will include an C header with the modification timestamp of the file. You may specify a different C header yourself in the C<< $options->{headers} >> hash. The C field of the response will be true if the status code is 2XX or if the status code is 304 (unmodified). If the file was modified and the server response includes a properly formatted C header, the file modification time will be updated accordingly. =head2 request $response = $http->request($method, $url); $response = $http->request($method, $url, \%options); Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST', 'PUT', etc.) on the given URL. The URL must have unsafe characters escaped and international domain names encoded. B: Method names are B per the HTTP/1.1 specification. Don't use C when you really want C. See L for how this applies to redirection. If the URL includes a "user:password" stanza, they will be used for Basic-style authorization headers. (Authorization headers will not be included in a redirected request.) For example: $http->request('GET', 'http://Aladdin:open sesame@example.com/'); If the "user:password" stanza contains reserved characters, they must be percent-escaped: $http->request('GET', 'http://john%40example.com:password@example.com/'); A hashref of options may be appended to modify the request. Valid options are: =over 4 =item * C — A hashref containing headers to include with the request. If the value for a header is an array reference, the header will be output multiple times with each value in the array. These headers over-write any default headers. =item * C — A scalar to include as the body of the request OR a code reference that will be called iteratively to produce the body of the request =item * C — A code reference that will be called if it exists to provide a hashref of trailing headers (only used with chunked transfer-encoding) =item * C — A code reference that will be called for each chunks of the response body received. =item * C — Override host resolution and force all connections to go only to a specific peer address, regardless of the URL of the request. This will include any redirections! This options should be used with extreme caution (e.g. debugging or very special circumstances). It can be given as either a scalar or a code reference that will receive the hostname and whose response will be taken as the address. =back The C header is generated from the URL in accordance with RFC 2616. It is a fatal error to specify C in the C option. Other headers may be ignored or overwritten if necessary for transport compliance. If the C option is a code reference, it will be called iteratively to provide the content body of the request. It should return the empty string or undef when the iterator is exhausted. If the C option is the empty string, no C or C headers will be generated. If the C option is provided, it will be called iteratively until the entire response body is received. The first argument will be a string containing a chunk of the response body, the second argument will be the in-progress response hash reference, as described below. (This allows customizing the action of the callback based on the C or C received prior to the content body.) Content data in the request/response is handled as "raw bytes". Any encoding/decoding (with associated headers) are the responsibility of the caller. The C method returns a hashref containing the response. The hashref will have the following keys: =over 4 =item * C — Boolean indicating whether the operation returned a 2XX status code =item * C — URL that provided the response. This is the URL of the request unless there were redirections, in which case it is the last URL queried in a redirection chain =item * C — The HTTP status code of the response =item * C — The response phrase returned by the server =item * C — The body of the response. If the response does not have any content or if a data callback is provided to consume the response body, this will be the empty string =item * C — A hashref of header fields. All header field names will be normalized to be lower case. If a header is repeated, the value will be an arrayref; it will otherwise be a scalar string containing the value =item * C - If this field exists, it is the protocol of the response such as HTTP/1.0 or HTTP/1.1 =item * C If this field exists, it is an arrayref of response hash references from redirects in the same order that redirections occurred. If it does not exist, then no redirections occurred. =back On an error during the execution of the request, the C field will contain 599, and the C field will contain the text of the error. =head2 www_form_urlencode $params = $http->www_form_urlencode( $data ); $response = $http->get("http://example.com/query?$params"); This method converts the key/value pairs from a data hash or array reference into a C string. The keys and values from the data reference will be UTF-8 encoded and escaped per RFC 3986. If a value is an array reference, the key will be repeated with each of the values of the array reference. If data is provided as a hash reference, the key/value pairs in the resulting string will be sorted by key and value for consistent ordering. =head2 can_ssl $ok = HTTP::Tiny->can_ssl; ($ok, $why) = HTTP::Tiny->can_ssl; ($ok, $why) = $http->can_ssl; Indicates if SSL support is available. When called as a class object, it checks for the correct version of L and L. When called as an object methods, if C is true or if C is set in C, it checks that a CA file is available. In scalar context, returns a boolean indicating if SSL is available. In list context, returns the boolean and a (possibly multi-line) string of errors indicating why SSL isn't available. =head2 connected $host = $http->connected; ($host, $port) = $http->connected; Indicates if a connection to a peer is being kept alive, per the C option. In scalar context, returns the peer host and port, joined with a colon, or C (if no peer is connected). In list context, returns the peer host and port or an empty list (if no peer is connected). B: This method cannot reliably be used to discover whether the remote host has closed its end of the socket. =for Pod::Coverage SSL_options agent cookie_jar default_headers http_proxy https_proxy keep_alive local_address max_redirect max_size no_proxy proxy timeout verify_SSL =head1 TLS/SSL SUPPORT Direct C connections are supported only if L 1.56 or greater and L 1.49 or greater are installed. An error will occur if new enough versions of these modules are not installed or if the TLS encryption fails. You can also use C utility function that returns boolean to see if the required modules are installed. An C connection may be made via an C proxy that supports the CONNECT command (i.e. RFC 2817). You may not proxy C via a proxy that itself requires C to communicate. TLS/SSL provides two distinct capabilities: =over 4 =item * Encrypted communication channel =item * Verification of server identity =back B. This was changed in version 0.083 due to security concerns. The previous default behavior can be enabled by setting C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}> to 1. Verification is done by checking that that the TLS/SSL connection has a valid certificate corresponding to the host name of the connection and that the certificate has been verified by a CA. Assuming you trust the CA, this will protect against L. Certificate verification requires a file or directory containing trusted CA certificates. C is called to detect the default location of your CA certificates. This also supports the environment variables C and C, and will fail over to L if no certs are found. If C is not able to find usable CA certificates, HTTP::Tiny will search several well-known system-specific default locations for a CA certificate file as a last resort: =over 4 =item * /etc/ssl/certs/ca-certificates.crt =item * /etc/pki/tls/certs/ca-bundle.crt =item * /etc/ssl/ca-bundle.pem =item * /etc/openssl/certs/ca-certificates.crt =item * /etc/ssl/cert.pem =item * /usr/local/share/certs/ca-root-nss.crt =item * /etc/pki/tls/cacert.pem =item * /etc/certs/ca-certificates.crt =back An error will be occur if C is true and no CA certificate file is available. If you desire complete control over TLS/SSL connections, the C attribute lets you provide a hash reference that will be passed through to C, overriding any options set by HTTP::Tiny. For example, to provide your own trusted CA file: SSL_options => { SSL_ca_file => $file_path, } The C attribute could also be used for such things as providing a client certificate for authentication to a server or controlling the choice of cipher used for the TLS/SSL connection. See L documentation for details. =head1 PROXY SUPPORT HTTP::Tiny can proxy both C and C requests. Only Basic proxy authorization is supported and it must be provided as part of the proxy URL: C. HTTP::Tiny supports the following proxy environment variables: =over 4 =item * http_proxy or HTTP_PROXY =item * https_proxy or HTTPS_PROXY =item * all_proxy or ALL_PROXY =back If the C environment variable is set, then this might be a CGI process and C would be set from the C header, which is a security risk. If C is set, C (the upper case variant only) is ignored, but C is considered instead. Tunnelling C over an C proxy using the CONNECT method is supported. If your proxy uses C itself, you can not tunnel C over it. Be warned that proxying an C connection opens you to the risk of a man-in-the-middle attack by the proxy server. The C environment variable is supported in the format of a comma-separated list of domain extensions proxy should not be used for. Proxy arguments passed to C will override their corresponding environment variables. =head1 LIMITATIONS HTTP::Tiny is I with the L: =over 4 =item * "Message Syntax and Routing" [RFC7230] =item * "Semantics and Content" [RFC7231] =item * "Conditional Requests" [RFC7232] =item * "Range Requests" [RFC7233] =item * "Caching" [RFC7234] =item * "Authentication" [RFC7235] =back It attempts to meet all "MUST" requirements of the specification, but does not implement all "SHOULD" requirements. (Note: it was developed against the earlier RFC 2616 specification and may not yet meet the revised RFC 7230-7235 spec.) Additionally, HTTP::Tiny supports the C method of RFC 5789. Some particular limitations of note include: =over =item * HTTP::Tiny focuses on correct transport. Users are responsible for ensuring that user-defined headers and content are compliant with the HTTP/1.1 specification. =item * Users must ensure that URLs are properly escaped for unsafe characters and that international domain names are properly encoded to ASCII. See L, L and L. =item * Redirection is very strict against the specification. Redirection is only automatic for response codes 301, 302, 307 and 308 if the request method is 'GET' or 'HEAD'. Response code 303 is always converted into a 'GET' redirection, as mandated by the specification. There is no automatic support for status 305 ("Use proxy") redirections. =item * There is no provision for delaying a request body using an C header. Unexpected C<1XX> responses are silently ignored as per the specification. =item * Only 'chunked' C is supported. =item * There is no support for a Request-URI of '*' for the 'OPTIONS' request. =item * Headers mentioned in the RFCs and some other, well-known headers are generated with their canonical case. Other headers are sent in the case provided by the user. Except for control headers (which are sent first), headers are sent in arbitrary order. =back Despite the limitations listed above, HTTP::Tiny is considered feature-complete. New feature requests should be directed to L. =head1 SEE ALSO =over 4 =item * L - Higher level UA features for HTTP::Tiny =item * L - HTTP::Tiny wrapper with L/L compatibility =item * L - Wrap L instance in HTTP::Tiny compatible interface =item * L - Required for IPv6 support =item * L - Required for SSL support =item * L - If HTTP::Tiny isn't enough for you, this is the "standard" way to do things =item * L - Required for SSL support =back =for :stopwords cpan testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan =head1 SUPPORT =head2 Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at L. You will be notified automatically of any progress on your issue. =head2 Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. L git clone https://github.com/Perl-Toolchain-Gang/HTTP-Tiny.git =head1 AUTHORS =over 4 =item * Christian Hansen =item * David Golden =back =head1 CONTRIBUTORS =for stopwords Alan Gardner Alessandro Ghedini A. Sinan Unur Brad Gilbert brian m. carlson Chris Nehren Weyl Claes Jakobsson Clinton Gormley Craig Berry David Golden Mitchell Dean Pearce Edward Zborowski Felipe Gasper Graham Knop Greg Kennedy James E Keenan Raspass Jeremy Mates Jess Robinson Karen Etheridge Lukas Eklund Martin J. Evans Martin-Louis Bright Matthew Horsfall Michael R. Davis Mike Doherty Nicolas Rochelemagne Olaf Alders Olivier Mengué Petr Písař sanjay-cpu Serguei Trouchelle Shoichi Kaji SkyMarshal Sören Kornetzki Steve Grazzini Stig Palmquist Syohei YOSHIDA Tatsuhiko Miyagawa Tom Hukins Tony Cook Xavier Guimard =over 4 =item * Alan Gardner =item * Alessandro Ghedini =item * A. Sinan Unur =item * Brad Gilbert =item * brian m. carlson =item * Chris Nehren =item * Chris Weyl =item * Claes Jakobsson =item * Clinton Gormley =item * Craig A. Berry =item * David Golden =item * David Mitchell =item * Dean Pearce =item * Edward Zborowski =item * Felipe Gasper =item * Graham Knop =item * Greg Kennedy =item * James E Keenan =item * James Raspass =item * Jeremy Mates =item * Jess Robinson =item * Karen Etheridge =item * Lukas Eklund =item * Martin J. Evans =item * Martin-Louis Bright =item * Matthew Horsfall =item * Michael R. Davis =item * Mike Doherty =item * Nicolas Rochelemagne =item * Olaf Alders =item * Olivier Mengué =item * Petr Písař =item * sanjay-cpu =item * Serguei Trouchelle =item * Shoichi Kaji =item * SkyMarshal =item * Sören Kornetzki =item * Steve Grazzini =item * Stig Palmquist =item * Syohei YOSHIDA =item * Tatsuhiko Miyagawa =item * Tom Hukins =item * Tony Cook =item * Xavier Guimard =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Christian Hansen. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut CPAN/Distribution.pm000044400000545733151112047420010316 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Distribution; use strict; use Cwd qw(chdir); use CPAN::Distroprefs; use CPAN::InfoObj; use File::Path (); use POSIX ":sys_wait_h"; @CPAN::Distribution::ISA = qw(CPAN::InfoObj); use vars qw($VERSION); $VERSION = "2.34"; my $run_allow_installing_within_test = 1; # boolean; either in test or in install, there is no third option # no prepare, because prepare is not a command on the shell command line # TODO: clear instance cache on reload my %instance; for my $method (qw(get make test install)) { no strict 'refs'; for my $prefix (qw(pre post)) { my $hookname = sprintf "%s_%s", $prefix, $method; *$hookname = sub { my($self) = @_; for my $plugin (@{$CPAN::Config->{plugin_list}}) { my($plugin_proper,$args) = split /=/, $plugin, 2; $args = "" unless defined $args; if ($CPAN::META->has_inst($plugin_proper)){ my @args = split /,/, $args; $instance{$plugin} ||= $plugin_proper->new(@args); if ($instance{$plugin}->can($hookname)) { $instance{$plugin}->$hookname($self); } } else { $CPAN::Frontend->mydie("Plugin '$plugin_proper' not found for hook '$hookname'"); } } }; } } # Accessors sub cpan_comment { my $self = shift; my $ro = $self->ro or return; $ro->{CPAN_COMMENT} } #-> CPAN::Distribution::undelay sub undelay { my $self = shift; for my $delayer ( "configure_requires_later", "configure_requires_later_for", "later", "later_for", ) { delete $self->{$delayer}; } } #-> CPAN::Distribution::is_dot_dist sub is_dot_dist { my($self) = @_; return substr($self->id,-1,1) eq "."; } # add the A/AN/ stuff #-> CPAN::Distribution::normalize sub normalize { my($self,$s) = @_; $s = $self->id unless defined $s; if (substr($s,-1,1) eq ".") { # using a global because we are sometimes called as static method if (!$CPAN::META->{LOCK} && !$CPAN::Have_warned->{"$s is unlocked"}++ ) { $CPAN::Frontend->mywarn("You are visiting the local directory '$s' without lock, take care that concurrent processes do not do likewise.\n"); $CPAN::Frontend->mysleep(1); } if ($s eq ".") { $s = "$CPAN::iCwd/."; } elsif (File::Spec->file_name_is_absolute($s)) { } elsif (File::Spec->can("rel2abs")) { $s = File::Spec->rel2abs($s); } else { $CPAN::Frontend->mydie("Your File::Spec is too old, please upgrade File::Spec"); } CPAN->debug("s[$s]") if $CPAN::DEBUG; unless ($CPAN::META->exists("CPAN::Distribution", $s)) { for ($CPAN::META->instance("CPAN::Distribution", $s)) { $_->{build_dir} = $s; $_->{archived} = "local_directory"; $_->{unwrapped} = CPAN::Distrostatus->new("YES -- local_directory"); } } } elsif ( $s =~ tr|/|| == 1 or $s !~ m|[A-Z]/[A-Z-0-9]{2}/[A-Z-0-9]{2,}/| ) { return $s if $s =~ m:^N/A|^Contact Author: ; $s =~ s|^(.)(.)([^/]*/)(.+)$|$1/$1$2/$1$2$3$4|; CPAN->debug("s[$s]") if $CPAN::DEBUG; } $s; } #-> sub CPAN::Distribution::author ; sub author { my($self) = @_; my($authorid); if (substr($self->id,-1,1) eq ".") { $authorid = "LOCAL"; } else { ($authorid) = $self->pretty_id =~ /^([\w\-]+)/; } CPAN::Shell->expand("Author",$authorid); } # tries to get the yaml from CPAN instead of the distro itself: # EXPERIMENTAL, UNDOCUMENTED AND UNTESTED, for Tels sub fast_yaml { my($self) = @_; my $meta = $self->pretty_id; $meta =~ s/\.(tar.gz|tgz|zip|tar.bz2)/.meta/; my(@ls) = CPAN::Shell->globls($meta); my $norm = $self->normalize($meta); my($local_file); my($local_wanted) = File::Spec->catfile( $CPAN::Config->{keep_source_where}, "authors", "id", split(/\//,$norm) ); $self->debug("Doing localize") if $CPAN::DEBUG; unless ($local_file = CPAN::FTP->localize("authors/id/$norm", $local_wanted)) { $CPAN::Frontend->mydie("Giving up on downloading yaml file '$local_wanted'\n"); } my $yaml = CPAN->_yaml_loadfile($local_file)->[0]; } #-> sub CPAN::Distribution::cpan_userid sub cpan_userid { my $self = shift; if ($self->{ID} =~ m{[A-Z]/[A-Z\-]{2}/([A-Z\-]+)/}) { return $1; } return $self->SUPER::cpan_userid; } #-> sub CPAN::Distribution::pretty_id sub pretty_id { my $self = shift; my $id = $self->id; return $id unless $id =~ m|^./../|; substr($id,5); } #-> sub CPAN::Distribution::base_id sub base_id { my $self = shift; my $id = $self->pretty_id(); my $base_id = File::Basename::basename($id); $base_id =~ s{\.(?:tar\.(bz2|gz|Z)|t(?:gz|bz)|zip)$}{}i; return $base_id; } #-> sub CPAN::Distribution::tested_ok_but_not_installed sub tested_ok_but_not_installed { my $self = shift; return ( $self->{make_test} && $self->{build_dir} && (UNIVERSAL::can($self->{make_test},"failed") ? ! $self->{make_test}->failed : $self->{make_test} =~ /^YES/ ) && ( !$self->{install} || $self->{install}->failed ) ); } # mark as dirty/clean for the sake of recursion detection. $color=1 # means "in use", $color=0 means "not in use anymore". $color=2 means # we have determined prereqs now and thus insist on passing this # through (at least) once again. #-> sub CPAN::Distribution::color_cmd_tmps ; sub color_cmd_tmps { my($self) = shift; my($depth) = shift || 0; my($color) = shift || 0; my($ancestors) = shift || []; # a distribution needs to recurse into its prereq_pms $self->debug("color_cmd_tmps[$depth,$color,@$ancestors]") if $CPAN::DEBUG; return if exists $self->{incommandcolor} && $color==1 && $self->{incommandcolor}==$color; $CPAN::MAX_RECURSION||=0; # silence 'once' warnings if ($depth>=$CPAN::MAX_RECURSION) { my $e = CPAN::Exception::RecursiveDependency->new($ancestors); if ($e->is_resolvable) { return $self->{incommandcolor}=2; } else { die $e; } } # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; my $prereq_pm = $self->prereq_pm; if (defined $prereq_pm) { # XXX also optional_req & optional_breq? -- xdg, 2012-04-01 # A: no, optional deps may recurse -- ak, 2014-05-07 PREREQ: for my $pre (sort( keys %{$prereq_pm->{requires}||{}}, keys %{$prereq_pm->{build_requires}||{}}, )) { next PREREQ if $pre eq "perl"; my $premo; unless ($premo = CPAN::Shell->expand("Module",$pre)) { $CPAN::Frontend->mywarn("prerequisite module[$pre] not known\n"); $CPAN::Frontend->mysleep(0.2); next PREREQ; } $premo->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); } } if ($color==0) { delete $self->{sponsored_mods}; # as we are at the end of a command, we'll give up this # reminder of a broken test. Other commands may test this guy # again. Maybe 'badtestcnt' should be renamed to # 'make_test_failed_within_command'? delete $self->{badtestcnt}; } $self->{incommandcolor} = $color; } #-> sub CPAN::Distribution::as_string ; sub as_string { my $self = shift; $self->containsmods; $self->upload_date; $self->SUPER::as_string(@_); } #-> sub CPAN::Distribution::containsmods ; sub containsmods { my $self = shift; return sort keys %{$self->{CONTAINSMODS}} if exists $self->{CONTAINSMODS}; my $dist_id = $self->{ID}; for my $mod ($CPAN::META->all_objects("CPAN::Module")) { my $mod_file = $mod->cpan_file or next; my $mod_id = $mod->{ID} or next; # warn "mod_file[$mod_file] dist_id[$dist_id] mod_id[$mod_id]"; # sleep 1; if ($CPAN::Signal) { delete $self->{CONTAINSMODS}; return; } $self->{CONTAINSMODS}{$mod_id} = undef if $mod_file eq $dist_id; } sort keys %{$self->{CONTAINSMODS}||={}}; } #-> sub CPAN::Distribution::upload_date ; sub upload_date { my $self = shift; return $self->{UPLOAD_DATE} if exists $self->{UPLOAD_DATE}; my(@local_wanted) = split(/\//,$self->id); my $filename = pop @local_wanted; push @local_wanted, "CHECKSUMS"; my $author = CPAN::Shell->expand("Author",$self->cpan_userid); return unless $author; my @dl = $author->dir_listing(\@local_wanted,0,$CPAN::Config->{show_upload_date}); return unless @dl; my($dirent) = grep { $_->[2] eq $filename } @dl; # warn sprintf "dirent[%s]id[%s]", $dirent, $self->id; return unless $dirent->[1]; return $self->{UPLOAD_DATE} = $dirent->[1]; } #-> sub CPAN::Distribution::uptodate ; sub uptodate { my($self) = @_; my $c; foreach $c ($self->containsmods) { my $obj = CPAN::Shell->expandany($c); unless ($obj->uptodate) { my $id = $self->pretty_id; $self->debug("$id not uptodate due to $c") if $CPAN::DEBUG; return 0; } } return 1; } #-> sub CPAN::Distribution::called_for ; sub called_for { my($self,$id) = @_; $self->{CALLED_FOR} = $id if defined $id; return $self->{CALLED_FOR}; } #-> sub CPAN::Distribution::shortcut_get ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_get { my ($self) = @_; if (exists $self->{cleanup_after_install_done}) { if ($self->{force_update}) { delete $self->{cleanup_after_install_done}; } else { my $id = $self->{CALLED_FOR} || $self->pretty_id; return $self->success( "Has already been *installed and cleaned up in the staging area* within this session, will not work on it again; if you really want to start over, try something like `force get $id`" ); } } if (my $why = $self->check_disabled) { $self->{unwrapped} = CPAN::Distrostatus->new("NO $why"); # XXX why is this goodbye() instead of just print/warn? # Alternatively, should other print/warns here be goodbye()? # -- xdg, 2012-04-05 return $self->goodbye("[disabled] -- NA $why"); } $self->debug("checking already unwrapped[$self->{ID}]") if $CPAN::DEBUG; if (exists $self->{build_dir} && -d $self->{build_dir}) { # this deserves print, not warn: return $self->success("Has already been unwrapped into directory ". "$self->{build_dir}" ); } # XXX I'm not sure this should be here because it's not really # a test for whether get should continue or return; this is # a side effect -- xdg, 2012-04-05 $self->debug("checking missing build_dir[$self->{ID}]") if $CPAN::DEBUG; if (exists $self->{build_dir} && ! -d $self->{build_dir}){ # we have lost it. $self->fforce(""); # no method to reset all phases but not set force (dodge) return undef; # no shortcut } # although we talk about 'force' we shall not test on # force directly. New model of force tries to refrain from # direct checking of force. $self->debug("checking unwrapping error[$self->{ID}]") if $CPAN::DEBUG; if ( exists $self->{unwrapped} and ( UNIVERSAL::can($self->{unwrapped},"failed") ? $self->{unwrapped}->failed : $self->{unwrapped} =~ /^NO/ ) ) { return $self->goodbye("Unwrapping had some problem, won't try again without force"); } return undef; # no shortcut } #-> sub CPAN::Distribution::get ; sub get { my($self) = @_; $self->pre_get(); $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; if (my $goto = $self->prefs->{goto}) { $self->post_get(); return $self->goto($goto); } if ( defined( my $sc = $self->shortcut_get) ) { $self->post_get(); return $sc; } local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; # local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # get $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls my $sub_wd = CPAN::anycwd(); # for cleaning up as good as possible my($local_file); # XXX I don't think this check needs to be here, as it # is already checked in shortcut_get() -- xdg, 2012-04-05 unless ($self->{build_dir} && -d $self->{build_dir}) { $self->get_file_onto_local_disk; if ($CPAN::Signal){ $self->post_get(); return; } $self->check_integrity; if ($CPAN::Signal){ $self->post_get(); return; } (my $packagedir,$local_file) = $self->run_preps_on_packagedir; # XXX why is this check here? -- xdg, 2012-04-08 if (exists $self->{writemakefile} && ref $self->{writemakefile} && $self->{writemakefile}->can("failed") && $self->{writemakefile}->failed) { # $self->post_get(); return; } $packagedir ||= $self->{build_dir}; $self->{build_dir} = $packagedir; } # XXX should this move up to after run_preps_on_packagedir? # Otherwise, failing writemakefile can return without # a $CPAN::Signal check -- xdg, 2012-04-05 if ($CPAN::Signal) { $self->safe_chdir($sub_wd); $self->post_get(); return; } unless ($self->patch){ $self->post_get(); return; } $self->store_persistent_state; $self->post_get(); return 1; # success } #-> CPAN::Distribution::get_file_onto_local_disk sub get_file_onto_local_disk { my($self) = @_; return if $self->is_dot_dist; my($local_file); my($local_wanted) = File::Spec->catfile( $CPAN::Config->{keep_source_where}, "authors", "id", split(/\//,$self->id) ); $self->debug("Doing localize") if $CPAN::DEBUG; unless ($local_file = CPAN::FTP->localize("authors/id/$self->{ID}", $local_wanted)) { my $note = ""; if ($CPAN::Index::DATE_OF_02) { $note = "Note: Current database in memory was generated ". "on $CPAN::Index::DATE_OF_02\n"; } $CPAN::Frontend->mydie("Giving up on '$local_wanted'\n$note"); } $self->debug("local_wanted[$local_wanted]local_file[$local_file]") if $CPAN::DEBUG; $self->{localfile} = $local_file; } #-> CPAN::Distribution::check_integrity sub check_integrity { my($self) = @_; return if $self->is_dot_dist; if ($CPAN::META->has_inst("Digest::SHA")) { $self->debug("Digest::SHA is installed, verifying"); $self->verifyCHECKSUM; } else { $self->debug("Digest::SHA is NOT installed"); } } #-> CPAN::Distribution::run_preps_on_packagedir sub run_preps_on_packagedir { my($self) = @_; return if $self->is_dot_dist; $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); # unsafe meta access, ok my $builddir = $CPAN::META->{cachemgr}->dir; # unsafe meta access, ok $self->safe_chdir($builddir); $self->debug("Removing tmp-$$") if $CPAN::DEBUG; File::Path::rmtree("tmp-$$"); unless (mkdir "tmp-$$", 0755) { $CPAN::Frontend->unrecoverable_error(<safe_chdir("tmp-$$"); # # Unpack the goods # my $local_file = $self->{localfile}; my $ct = eval{CPAN::Tarzip->new($local_file)}; unless ($ct) { $self->{unwrapped} = CPAN::Distrostatus->new("NO"); delete $self->{build_dir}; return; } if ($local_file =~ /(\.tar\.(bz2|gz|Z)|\.tgz)(?!\n)\Z/i) { $self->{was_uncompressed}++ unless eval{$ct->gtest()}; $self->untar_me($ct); } elsif ( $local_file =~ /\.zip(?!\n)\Z/i ) { $self->unzip_me($ct); } else { $self->{was_uncompressed}++ unless $ct->gtest(); $local_file = $self->handle_singlefile($local_file); } # we are still in the tmp directory! # Let's check if the package has its own directory. my $dh = DirHandle->new(File::Spec->curdir) or Carp::croak("Couldn't opendir .: $!"); my @readdir = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh->read; ### MAC?? if (grep { $_ eq "pax_global_header" } @readdir) { $CPAN::Frontend->mywarn("Your (un)tar seems to have extracted a file named 'pax_global_header' from the tarball '$local_file'. This is almost certainly an error. Please upgrade your tar. I'll ignore this file for now. See also http://rt.cpan.org/Ticket/Display.html?id=38932\n"); $CPAN::Frontend->mysleep(5); @readdir = grep { $_ ne "pax_global_header" } @readdir; } $dh->close; my $tdir_base; my $from_dir; my @dirents; if (@readdir == 1 && -d $readdir[0]) { $tdir_base = $readdir[0]; $from_dir = File::Spec->catdir(File::Spec->curdir,$readdir[0]); my($mode) = (stat $from_dir)[2]; chmod $mode | 00755, $from_dir; # JONATHAN/Math-Calculus-TaylorSeries-0.1.tar.gz has 0644 my $dh2; unless ($dh2 = DirHandle->new($from_dir)) { my $why = sprintf ( "Couldn't opendir '%s', mode '%o': %s", $from_dir, $mode, $!, ); $CPAN::Frontend->mywarn("$why\n"); $self->{writemakefile} = CPAN::Distrostatus->new("NO -- $why"); return; } @dirents = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh2->read; ### MAC?? } else { my $userid = $self->cpan_userid; CPAN->debug("userid[$userid]"); if (!$userid or $userid eq "N/A") { $userid = "anon"; } $tdir_base = $userid; $from_dir = File::Spec->curdir; @dirents = @readdir; } my $packagedir; my $eexist = ($CPAN::META->has_usable("Errno") && defined &Errno::EEXIST) ? &Errno::EEXIST : undef; for(my $suffix = 0; ; $suffix++) { $packagedir = File::Spec->catdir($builddir, "$tdir_base-$suffix"); my $parent = $builddir; mkdir($packagedir, 0777) and last; if((defined($eexist) && $! != $eexist) || $suffix == 999) { $CPAN::Frontend->mydie("Cannot create directory $packagedir: $!\n"); } } my $f; for $f (@dirents) { # is already without "." and ".." my $from = File::Spec->catfile($from_dir,$f); my($mode) = (stat $from)[2]; chmod $mode | 00755, $from if -d $from; # OTTO/Pod-Trial-LinkImg-0.005.tgz my $to = File::Spec->catfile($packagedir,$f); unless (File::Copy::move($from,$to)) { my $err = $!; $from = File::Spec->rel2abs($from); $CPAN::Frontend->mydie( "Couldn't move $from to $to: $err; #82295? ". "CPAN::VERSION=$CPAN::VERSION; ". "File::Copy::VERSION=$File::Copy::VERSION; ". "$from " . (-e $from ? "exists; " : "does not exist; "). "$to " . (-e $to ? "exists; " : "does not exist; "). "cwd=" . CPAN::anycwd() . ";" ); } } $self->{build_dir} = $packagedir; $self->safe_chdir($builddir); File::Path::rmtree("tmp-$$"); $self->safe_chdir($packagedir); $self->_signature_business(); $self->safe_chdir($builddir); return($packagedir,$local_file); } #-> sub CPAN::Distribution::pick_meta_file ; sub pick_meta_file { my($self, $filter) = @_; $filter = '.' unless defined $filter; my $build_dir; unless ($build_dir = $self->{build_dir}) { # maybe permission on build_dir was missing $CPAN::Frontend->mywarn("Warning: cannot determine META.yml without a build_dir.\n"); return; } my $has_cm = $CPAN::META->has_usable("CPAN::Meta"); my $has_pcm = $CPAN::META->has_usable("Parse::CPAN::Meta"); my @choices; push @choices, 'MYMETA.json' if $has_cm; push @choices, 'MYMETA.yml' if $has_cm || $has_pcm; push @choices, 'META.json' if $has_cm; push @choices, 'META.yml' if $has_cm || $has_pcm; for my $file ( grep { /$filter/ } @choices ) { my $path = File::Spec->catfile( $build_dir, $file ); return $path if -f $path } return; } #-> sub CPAN::Distribution::parse_meta_yml ; sub parse_meta_yml { my($self, $yaml) = @_; $self->debug(sprintf("parse_meta_yml[%s]",$yaml||'undef')) if $CPAN::DEBUG; my $build_dir = $self->{build_dir} or die "PANIC: cannot parse yaml without a build_dir"; $yaml ||= File::Spec->catfile($build_dir,"META.yml"); $self->debug("meta[$yaml]") if $CPAN::DEBUG; return unless -f $yaml; my $early_yaml; eval { $CPAN::META->has_inst("Parse::CPAN::Meta") or die; die "Parse::CPAN::Meta yaml too old" unless $Parse::CPAN::Meta::VERSION >= "1.40"; # P::C::M returns last document in scalar context $early_yaml = Parse::CPAN::Meta::LoadFile($yaml); }; unless ($early_yaml) { eval { $early_yaml = CPAN->_yaml_loadfile($yaml)->[0]; }; } $self->debug(sprintf("yaml[%s]", $early_yaml || 'UNDEF')) if $CPAN::DEBUG; $self->debug($early_yaml) if $CPAN::DEBUG && $early_yaml; if (!ref $early_yaml or ref $early_yaml ne "HASH"){ # fix rt.cpan.org #95271 $CPAN::Frontend->mywarn("The content of '$yaml' is not a HASH reference. Cannot use it.\n"); return {}; } return $early_yaml || undef; } #-> sub CPAN::Distribution::satisfy_requires ; # return values: 1 means requirements are satisfied; # and 0 means not satisfied (and maybe queued) sub satisfy_requires { my ($self) = @_; $self->debug("Entering satisfy_requires") if $CPAN::DEBUG; if (my @prereq = $self->unsat_prereq("later")) { if ($CPAN::DEBUG){ require Data::Dumper; my $prereq = Data::Dumper->new(\@prereq)->Terse(1)->Indent(0)->Dump; $self->debug("unsatisfied[$prereq]"); } if ($prereq[0][0] eq "perl") { my $need = "requires perl '$prereq[0][1]'"; my $id = $self->pretty_id; $CPAN::Frontend->mywarn("$id $need; you have only $]; giving up\n"); $self->{make} = CPAN::Distrostatus->new("NO $need"); $self->store_persistent_state; die "[prereq] -- NOT OK\n"; } else { my $follow = eval { $self->follow_prereqs("later",@prereq); }; if (0) { } elsif ($follow) { return; # we need deps } elsif ($@ && ref $@ && $@->isa("CPAN::Exception::RecursiveDependency")) { $CPAN::Frontend->mywarn($@); die "[depend] -- NOT OK\n"; } } } return 1; } #-> sub CPAN::Distribution::satisfy_configure_requires ; # return values: 1 means configure_require is satisfied; # and 0 means not satisfied (and maybe queued) sub satisfy_configure_requires { my($self) = @_; $self->debug("Entering satisfy_configure_requires") if $CPAN::DEBUG; my $enable_configure_requires = 1; if (!$enable_configure_requires) { return 1; # if we return 1 here, everything is as before we introduced # configure_requires that means, things with # configure_requires simply fail, all others succeed } my @prereq = $self->unsat_prereq("configure_requires_later"); $self->debug(sprintf "configure_requires[%s]", join(",",map {join "/",@$_} @prereq)) if $CPAN::DEBUG; return 1 unless @prereq; $self->debug(\@prereq) if $CPAN::DEBUG; if ($self->{configure_requires_later}) { for my $k (sort keys %{$self->{configure_requires_later_for}||{}}) { if ($self->{configure_requires_later_for}{$k}>1) { my $type = ""; for my $p (@prereq) { if ($p->[0] eq $k) { $type = $p->[1]; } } $type = " $type" if $type; $CPAN::Frontend->mywarn("Warning: unmanageable(?) prerequisite $k$type"); sleep 1; } } } if ($prereq[0][0] eq "perl") { my $need = "requires perl '$prereq[0][1]'"; my $id = $self->pretty_id; $CPAN::Frontend->mywarn("$id $need; you have only $]; giving up\n"); $self->{make} = CPAN::Distrostatus->new("NO $need"); $self->store_persistent_state; return $self->goodbye("[prereq] -- NOT OK"); } else { my $follow = eval { $self->follow_prereqs("configure_requires_later", @prereq); }; if (0) { } elsif ($follow) { return; # we need deps } elsif ($@ && ref $@ && $@->isa("CPAN::Exception::RecursiveDependency")) { $CPAN::Frontend->mywarn($@); return $self->goodbye("[depend] -- NOT OK"); } else { return $self->goodbye("[configure_requires] -- NOT OK"); } } die "never reached"; } #-> sub CPAN::Distribution::choose_MM_or_MB ; sub choose_MM_or_MB { my($self) = @_; $self->satisfy_configure_requires() or return; my $local_file = $self->{localfile}; my($mpl) = File::Spec->catfile($self->{build_dir},"Makefile.PL"); my($mpl_exists) = -f $mpl; unless ($mpl_exists) { # NFS has been reported to have racing problems after the # renaming of a directory in some environments. # This trick helps. $CPAN::Frontend->mysleep(1); my $mpldh = DirHandle->new($self->{build_dir}) or Carp::croak("Couldn't opendir $self->{build_dir}: $!"); $mpl_exists = grep /^Makefile\.PL$/, $mpldh->read; $mpldh->close; } my $prefer_installer = "eumm"; # eumm|mb if (-f File::Spec->catfile($self->{build_dir},"Build.PL")) { if ($mpl_exists) { # they *can* choose if ($CPAN::META->has_inst("Module::Build")) { $prefer_installer = CPAN::HandleConfig->prefs_lookup( $self, q{prefer_installer} ); # M::B <= 0.35 left a DATA handle open that # causes problems upgrading M::B on Windows close *Module::Build::Version::DATA if fileno *Module::Build::Version::DATA; } } else { $prefer_installer = "mb"; } } if (lc($prefer_installer) eq "rand") { $prefer_installer = rand()<.5 ? "eumm" : "mb"; } if (lc($prefer_installer) eq "mb") { $self->{modulebuild} = 1; } elsif ($self->{archived} eq "patch") { # not an edge case, nothing to install for sure my $why = "A patch file cannot be installed"; $CPAN::Frontend->mywarn("Refusing to handle this file: $why\n"); $self->{writemakefile} = CPAN::Distrostatus->new("NO $why"); } elsif (! $mpl_exists) { $self->_edge_cases($mpl,$local_file); } if ($self->{build_dir} && $CPAN::Config->{build_dir_reuse} ) { $self->store_persistent_state; } return $self; } # see also reanimate_build_dir #-> CPAN::Distribution::store_persistent_state sub store_persistent_state { my($self) = @_; my $dir = $self->{build_dir}; unless (defined $dir && length $dir) { my $id = $self->id; $CPAN::Frontend->mywarnonce("build_dir of $id is not known, ". "will not store persistent state\n"); return; } # self-build-dir my $sbd = Cwd::realpath( File::Spec->catdir($dir, File::Spec->updir ()) ); # config-build-dir my $cbd = Cwd::realpath( # the catdir is a workaround for bug https://rt.cpan.org/Ticket/Display.html?id=101283 File::Spec->catdir($CPAN::Config->{build_dir}, File::Spec->curdir()) ); unless ($sbd eq $cbd) { $CPAN::Frontend->mywarnonce("Directory '$dir' not below $CPAN::Config->{build_dir}, ". "will not store persistent state\n"); return; } my $file = sprintf "%s.yml", $dir; my $yaml_module = CPAN::_yaml_module(); if ($CPAN::META->has_inst($yaml_module)) { CPAN->_yaml_dumpfile( $file, { time => time, perl => CPAN::_perl_fingerprint(), distribution => $self, } ); } else { $CPAN::Frontend->myprintonce("'$yaml_module' not installed, ". "will not store persistent state\n"); } } #-> CPAN::Distribution::try_download sub try_download { my($self,$patch) = @_; my $norm = $self->normalize($patch); my($local_wanted) = File::Spec->catfile( $CPAN::Config->{keep_source_where}, "authors", "id", split(/\//,$norm), ); $self->debug("Doing localize") if $CPAN::DEBUG; return CPAN::FTP->localize("authors/id/$norm", $local_wanted); } { my $stdpatchargs = ""; #-> CPAN::Distribution::patch sub patch { my($self) = @_; $self->debug("checking patches id[$self->{ID}]") if $CPAN::DEBUG; my $patches = $self->prefs->{patches}; $patches ||= ""; $self->debug("patches[$patches]") if $CPAN::DEBUG; if ($patches) { return unless @$patches; $self->safe_chdir($self->{build_dir}); CPAN->debug("patches[$patches]") if $CPAN::DEBUG; my $patchbin = $CPAN::Config->{patch}; unless ($patchbin && length $patchbin) { $CPAN::Frontend->mydie("No external patch command configured\n\n". "Please run 'o conf init /patch/'\n\n"); } unless (MM->maybe_command($patchbin)) { $CPAN::Frontend->mydie("No external patch command available\n\n". "Please run 'o conf init /patch/'\n\n"); } $patchbin = CPAN::HandleConfig->safe_quote($patchbin); local $ENV{PATCH_GET} = 0; # formerly known as -g0 unless ($stdpatchargs) { my $system = "$patchbin --version |"; local *FH; open FH, $system or die "Could not fork '$system': $!"; local $/ = "\n"; my $pversion; PARSEVERSION: while () { if (/^patch\s+([\d\.]+)/) { $pversion = $1; last PARSEVERSION; } } if ($pversion) { $stdpatchargs = "-N --fuzz=3"; } else { $stdpatchargs = "-N"; } } my $countedpatches = @$patches == 1 ? "1 patch" : (scalar @$patches . " patches"); $CPAN::Frontend->myprint("Applying $countedpatches:\n"); my $patches_dir = $CPAN::Config->{patches_dir}; for my $patch (@$patches) { if ($patches_dir && !File::Spec->file_name_is_absolute($patch)) { my $f = File::Spec->catfile($patches_dir, $patch); $patch = $f if -f $f; } unless (-f $patch) { CPAN->debug("not on disk: patch[$patch]") if $CPAN::DEBUG; if (my $trydl = $self->try_download($patch)) { $patch = $trydl; } else { my $fail = "Could not find patch '$patch'"; $CPAN::Frontend->mywarn("$fail; cannot continue\n"); $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); delete $self->{build_dir}; return; } } $CPAN::Frontend->myprint(" $patch\n"); my $readfh = CPAN::Tarzip->TIEHANDLE($patch); my $pcommand; my($ppp,$pfiles) = $self->_patch_p_parameter($readfh); if ($ppp eq "applypatch") { $pcommand = "$CPAN::Config->{applypatch} -verbose"; } else { my $thispatchargs = join " ", $stdpatchargs, $ppp; $pcommand = "$patchbin $thispatchargs"; require Config; # usually loaded from CPAN.pm if ($Config::Config{osname} eq "solaris") { # native solaris patch cannot patch readonly files for my $file (@{$pfiles||[]}) { my @stat = stat $file or next; chmod $stat[2] | 0600, $file; # may fail } } } $readfh = CPAN::Tarzip->TIEHANDLE($patch); # open again my $writefh = FileHandle->new; $CPAN::Frontend->myprint(" $pcommand\n"); unless (open $writefh, "|$pcommand") { my $fail = "Could not fork '$pcommand'"; $CPAN::Frontend->mywarn("$fail; cannot continue\n"); $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); delete $self->{build_dir}; return; } binmode($writefh); while (my $x = $readfh->READLINE) { print $writefh $x; } unless (close $writefh) { my $fail = "Could not apply patch '$patch'"; $CPAN::Frontend->mywarn("$fail; cannot continue\n"); $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); delete $self->{build_dir}; return; } } $self->{patched}++; } return 1; } } # may return # - "applypatch" # - ("-p0"|"-p1", $files) sub _patch_p_parameter { my($self,$fh) = @_; my $cnt_files = 0; my $cnt_p0files = 0; my @files; local($_); while ($_ = $fh->READLINE) { if ( $CPAN::Config->{applypatch} && /\#\#\#\# ApplyPatch data follows \#\#\#\#/ ) { return "applypatch" } next unless /^[\*\+]{3}\s(\S+)/; my $file = $1; push @files, $file; $cnt_files++; $cnt_p0files++ if -f $file; CPAN->debug("file[$file]cnt_files[$cnt_files]cnt_p0files[$cnt_p0files]") if $CPAN::DEBUG; } return "-p1" unless $cnt_files; my $opt_p = $cnt_files==$cnt_p0files ? "-p0" : "-p1"; return ($opt_p, \@files); } #-> sub CPAN::Distribution::_edge_cases # with "configure" or "Makefile" or single file scripts sub _edge_cases { my($self,$mpl,$local_file) = @_; $self->debug(sprintf("makefilepl[%s]anycwd[%s]", $mpl, CPAN::anycwd(), )) if $CPAN::DEBUG; my $build_dir = $self->{build_dir}; my($configure) = File::Spec->catfile($build_dir,"Configure"); if (-f $configure) { # do we have anything to do? $self->{configure} = $configure; } elsif (-f File::Spec->catfile($build_dir,"Makefile")) { $CPAN::Frontend->mywarn(qq{ Package comes with a Makefile and without a Makefile.PL. We\'ll try to build it with that Makefile then. }); $self->{writemakefile} = CPAN::Distrostatus->new("YES"); $CPAN::Frontend->mysleep(2); } else { my $cf = $self->called_for || "unknown"; if ($cf =~ m|/|) { $cf =~ s|.*/||; $cf =~ s|\W.*||; } $cf =~ s|[/\\:]||g; # risk of filesystem damage $cf = "unknown" unless length($cf); if (my $crud = $self->_contains_crud($build_dir)) { my $why = qq{Package contains $crud; not recognized as a perl package, giving up}; $CPAN::Frontend->mywarn("$why\n"); $self->{writemakefile} = CPAN::Distrostatus->new(qq{NO -- $why}); return; } $CPAN::Frontend->mywarn(qq{Package seems to come without Makefile.PL. (The test -f "$mpl" returned false.) Writing one on our own (setting NAME to $cf)\a\n}); $self->{had_no_makefile_pl}++; $CPAN::Frontend->mysleep(3); # Writing our own Makefile.PL my $exefile_stanza = ""; if ($self->{archived} eq "maybe_pl") { $exefile_stanza = $self->_exefile_stanza($build_dir,$local_file); } my $fh = FileHandle->new; $fh->open(">$mpl") or Carp::croak("Could not open >$mpl: $!"); $fh->print( qq{# This Makefile.PL has been autogenerated by the module CPAN.pm # because there was no Makefile.PL supplied. # Autogenerated on: }.scalar localtime().qq{ use ExtUtils::MakeMaker; WriteMakefile( NAME => q[$cf],$exefile_stanza ); }); $fh->close; } } #-> CPAN;:Distribution::_contains_crud sub _contains_crud { my($self,$dir) = @_; my(@dirs, $dh, @files); opendir $dh, $dir or return; my $dirent; for $dirent (readdir $dh) { next if $dirent =~ /^\.\.?$/; my $path = File::Spec->catdir($dir,$dirent); if (-d $path) { push @dirs, $dirent; } elsif (-f $path) { push @files, $dirent; } } if (@dirs && @files) { return "both files[@files] and directories[@dirs]"; } elsif (@files > 2) { return "several files[@files] but no Makefile.PL or Build.PL"; } return; } #-> CPAN;:Distribution::_exefile_stanza sub _exefile_stanza { my($self,$build_dir,$local_file) = @_; my $fh = FileHandle->new; my $script_file = File::Spec->catfile($build_dir,$local_file); $fh->open($script_file) or Carp::croak("Could not open script '$script_file': $!"); local $/ = "\n"; # parse name and prereq my($state) = "poddir"; my($name, $prereq) = ("", ""); while (<$fh>) { if ($state eq "poddir" && /^=head\d\s+(\S+)/) { if ($1 eq 'NAME') { $state = "name"; } elsif ($1 eq 'PREREQUISITES') { $state = "prereq"; } } elsif ($state =~ m{^(name|prereq)$}) { if (/^=/) { $state = "poddir"; } elsif (/^\s*$/) { # nop } elsif ($state eq "name") { if ($name eq "") { ($name) = /^(\S+)/; $state = "poddir"; } } elsif ($state eq "prereq") { $prereq .= $_; } } elsif (/^=cut\b/) { last; } } $fh->close; for ($name) { s{.*<}{}; # strip X<...> s{>.*}{}; } chomp $prereq; $prereq = join " ", split /\s+/, $prereq; my($PREREQ_PM) = join("\n", map { s{.*<}{}; # strip X<...> s{>.*}{}; if (/[\s\'\"]/) { # prose? } else { s/[^\w:]$//; # period? " "x28 . "'$_' => 0,"; } } split /\s*,\s*/, $prereq); if ($name) { my $to_file = File::Spec->catfile($build_dir, $name); rename $script_file, $to_file or die "Can't rename $script_file to $to_file: $!"; } return " EXE_FILES => ['$name'], PREREQ_PM => { $PREREQ_PM }, "; } #-> CPAN::Distribution::_signature_business sub _signature_business { my($self) = @_; my $check_sigs = CPAN::HandleConfig->prefs_lookup($self, q{check_sigs}); if ($check_sigs) { if ($CPAN::META->has_inst("Module::Signature")) { if (-f "SIGNATURE") { $self->debug("Module::Signature is installed, verifying") if $CPAN::DEBUG; my $rv = Module::Signature::verify(); if ($rv != Module::Signature::SIGNATURE_OK() and $rv != Module::Signature::SIGNATURE_MISSING()) { $CPAN::Frontend->mywarn( qq{\nSignature invalid for }. qq{distribution file. }. qq{Please investigate.\n\n} ); my $wrap = sprintf(qq{I'd recommend removing %s. Some error occurred }. qq{while checking its signature, so it could }. qq{be invalid. Maybe you have configured }. qq{your 'urllist' with a bad URL. Please check this }. qq{array with 'o conf urllist' and retry. Or }. qq{examine the distribution in a subshell. Try look %s and run cpansign -v }, $self->{localfile}, $self->pretty_id, ); $self->{signature_verify} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(Text::Wrap::wrap("","",$wrap)); $CPAN::Frontend->mysleep(5) if $CPAN::Frontend->can("mysleep"); } else { $self->{signature_verify} = CPAN::Distrostatus->new("YES"); $self->debug("Module::Signature has verified") if $CPAN::DEBUG; } } else { $CPAN::Frontend->mywarn(qq{Package came without SIGNATURE\n\n}); } } else { $self->debug("Module::Signature is NOT installed") if $CPAN::DEBUG; } } } #-> CPAN::Distribution::untar_me ; sub untar_me { my($self,$ct) = @_; $self->{archived} = "tar"; my $result = eval { $ct->untar() }; if ($result) { $self->{unwrapped} = CPAN::Distrostatus->new("YES"); } else { # unfortunately we have no $@ here, Tarzip is using mydie which dies with "\n" $self->{unwrapped} = CPAN::Distrostatus->new("NO -- untar failed"); } } # CPAN::Distribution::unzip_me ; sub unzip_me { my($self,$ct) = @_; $self->{archived} = "zip"; if (eval { $ct->unzip() }) { $self->{unwrapped} = CPAN::Distrostatus->new("YES"); } else { $self->{unwrapped} = CPAN::Distrostatus->new("NO -- unzip failed during unzip"); } return; } sub handle_singlefile { my($self,$local_file) = @_; if ( $local_file =~ /\.pm(\.(gz|Z))?(?!\n)\Z/ ) { $self->{archived} = "pm"; } elsif ( $local_file =~ /\.patch(\.(gz|bz2))?(?!\n)\Z/ ) { $self->{archived} = "patch"; } else { $self->{archived} = "maybe_pl"; } my $to = File::Basename::basename($local_file); if ($to =~ s/\.(gz|Z)(?!\n)\Z//) { if (eval{CPAN::Tarzip->new($local_file)->gunzip($to)}) { $self->{unwrapped} = CPAN::Distrostatus->new("YES"); } else { $self->{unwrapped} = CPAN::Distrostatus->new("NO -- uncompressing failed"); } } else { if (File::Copy::cp($local_file,".")) { $self->{unwrapped} = CPAN::Distrostatus->new("YES"); } else { $self->{unwrapped} = CPAN::Distrostatus->new("NO -- copying failed"); } } return $to; } #-> sub CPAN::Distribution::new ; sub new { my($class,%att) = @_; # $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); my $this = { %att }; return bless $this, $class; } #-> sub CPAN::Distribution::look ; sub look { my($self) = @_; if ($^O eq 'MacOS') { $self->Mac::BuildTools::look; return; } if ( $CPAN::Config->{'shell'} ) { $CPAN::Frontend->myprint(qq{ Trying to open a subshell in the build directory... }); } else { $CPAN::Frontend->myprint(qq{ Your configuration does not define a value for subshells. Please define it with "o conf shell " }); return; } my $dist = $self->id; my $dir; unless ($dir = $self->dir) { $self->get; } unless ($dir ||= $self->dir) { $CPAN::Frontend->mywarn(qq{ Could not determine which directory to use for looking at $dist. }); return; } my $pwd = CPAN::anycwd(); $self->safe_chdir($dir); $CPAN::Frontend->myprint(qq{Working directory is $dir\n}); { local $ENV{CPAN_SHELL_LEVEL} = $ENV{CPAN_SHELL_LEVEL}||0; $ENV{CPAN_SHELL_LEVEL} += 1; my $shell = CPAN::HandleConfig->safe_quote($CPAN::Config->{'shell'}); local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; # local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # look $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls unless (system($shell) == 0) { my $code = $? >> 8; $CPAN::Frontend->mywarn("Subprocess shell exit code $code\n"); } } $self->safe_chdir($pwd); } # CPAN::Distribution::cvs_import ; sub cvs_import { my($self) = @_; $self->get; my $dir = $self->dir; my $package = $self->called_for; my $module = $CPAN::META->instance('CPAN::Module', $package); my $version = $module->cpan_version; my $userid = $self->cpan_userid; my $cvs_dir = (split /\//, $dir)[-1]; $cvs_dir =~ s/-\d+[^-]+(?!\n)\Z//; my $cvs_root = $CPAN::Config->{cvsroot} || $ENV{CVSROOT}; my $cvs_site_perl = $CPAN::Config->{cvs_site_perl} || $ENV{CVS_SITE_PERL}; if ($cvs_site_perl) { $cvs_dir = "$cvs_site_perl/$cvs_dir"; } my $cvs_log = qq{"imported $package $version sources"}; $version =~ s/\./_/g; # XXX cvs: undocumented and unclear how it was meant to work my @cmd = ('cvs', '-d', $cvs_root, 'import', '-m', $cvs_log, "$cvs_dir", $userid, "v$version"); my $pwd = CPAN::anycwd(); chdir($dir) or $CPAN::Frontend->mydie(qq{Could not chdir to "$dir": $!}); $CPAN::Frontend->myprint(qq{Working directory is $dir\n}); $CPAN::Frontend->myprint(qq{@cmd\n}); system(@cmd) == 0 or # XXX cvs $CPAN::Frontend->mydie("cvs import failed"); chdir($pwd) or $CPAN::Frontend->mydie(qq{Could not chdir to "$pwd": $!}); } #-> sub CPAN::Distribution::readme ; sub readme { my($self) = @_; my($dist) = $self->id; my($sans,$suffix) = $dist =~ /(.+)\.(tgz|tar[\._-]gz|tar\.Z|zip)$/; $self->debug("sans[$sans] suffix[$suffix]\n") if $CPAN::DEBUG; my($local_file); my($local_wanted) = File::Spec->catfile( $CPAN::Config->{keep_source_where}, "authors", "id", split(/\//,"$sans.readme"), ); my $readme = "authors/id/$sans.readme"; $self->debug("Doing localize for '$readme'") if $CPAN::DEBUG; $local_file = CPAN::FTP->localize($readme, $local_wanted) or $CPAN::Frontend->mydie(qq{No $sans.readme found}); if ($^O eq 'MacOS') { Mac::BuildTools::launch_file($local_file); return; } my $fh_pager = FileHandle->new; local($SIG{PIPE}) = "IGNORE"; my $pager = $CPAN::Config->{'pager'} || "cat"; $fh_pager->open("|$pager") or die "Could not open pager $pager\: $!"; my $fh_readme = FileHandle->new; $fh_readme->open($local_file) or $CPAN::Frontend->mydie(qq{Could not open "$local_file": $!}); $CPAN::Frontend->myprint(qq{ Displaying file $local_file with pager "$pager" }); $fh_pager->print(<$fh_readme>); $fh_pager->close; } #-> sub CPAN::Distribution::verifyCHECKSUM ; sub verifyCHECKSUM { my($self) = @_; EXCUSE: { my @e; $self->{CHECKSUM_STATUS} ||= ""; $self->{CHECKSUM_STATUS} eq "OK" and push @e, "Checksum was ok"; $CPAN::Frontend->myprint(join "", map {" $_\n"} @e) and return if @e; } my($lc_want,$lc_file,@local,$basename); @local = split(/\//,$self->id); pop @local; push @local, "CHECKSUMS"; $lc_want = File::Spec->catfile($CPAN::Config->{keep_source_where}, "authors", "id", @local); local($") = "/"; if (my $size = -s $lc_want) { $self->debug("lc_want[$lc_want]size[$size]") if $CPAN::DEBUG; my @stat = stat $lc_want; my $epoch_starting_support_of_cpan_path = 1637471530; if ($stat[9] >= $epoch_starting_support_of_cpan_path) { if ($self->CHECKSUM_check_file($lc_want, 1)) { return $self->{CHECKSUM_STATUS} = "OK"; } } else { unlink $lc_want; } } $lc_file = CPAN::FTP->localize("authors/id/@local", $lc_want,1); unless ($lc_file) { $CPAN::Frontend->myprint("Trying $lc_want.gz\n"); $local[-1] .= ".gz"; $lc_file = CPAN::FTP->localize("authors/id/@local", "$lc_want.gz",1); if ($lc_file) { $lc_file =~ s/\.gz(?!\n)\Z//; eval{CPAN::Tarzip->new("$lc_file.gz")->gunzip($lc_file)}; } else { return; } } if ($self->CHECKSUM_check_file($lc_file)) { return $self->{CHECKSUM_STATUS} = "OK"; } } #-> sub CPAN::Distribution::SIG_check_file ; sub SIG_check_file { my($self,$chk_file) = @_; my $rv = eval { Module::Signature::_verify($chk_file) }; if ($rv eq Module::Signature::CANNOT_VERIFY()) { $CPAN::Frontend->myprint(qq{\nSignature for }. qq{file $chk_file could not be verified for an unknown reason. }. $self->as_string. qq{Module::Signature verification returned value $rv\n\n} ); my $wrap = qq{The manual says for this case: Cannot verify the OpenPGP signature, maybe due to the lack of a network connection to the key server, or if neither gnupg nor Crypt::OpenPGP exists on the system. You probably want to analyse the situation and if you cannot fix it you will have to decide whether you want to stop this session or you want to turn off signature verification. The latter would be done with the command 'o conf init check_sigs'}; $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap)); } if ($rv == Module::Signature::SIGNATURE_OK()) { $CPAN::Frontend->myprint("Signature for $chk_file ok\n"); return $self->{SIG_STATUS} = "OK"; } else { $CPAN::Frontend->mywarn(qq{\nSignature invalid for }. qq{file $chk_file. }. qq{Please investigate.\n\n}. $self->as_string. qq{Module::Signature verification returned value $rv\n\n} ); my $wrap = qq{I\'d recommend removing $chk_file. Its signature is invalid. Maybe you have configured your 'urllist' with a bad URL. Please check this array with 'o conf urllist', and retry.}; $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap)); } } #-> sub CPAN::Distribution::CHECKSUM_check_file ; # sloppy is 1 when we have an old checksums file that maybe is good # enough sub CHECKSUM_check_file { my($self,$chk_file,$sloppy) = @_; my($cksum,$file,$basename); $sloppy ||= 0; $self->debug("chk_file[$chk_file]sloppy[$sloppy]") if $CPAN::DEBUG; my $check_sigs = CPAN::HandleConfig->prefs_lookup($self, q{check_sigs}); if ($check_sigs) { if ($CPAN::META->has_inst("Module::Signature")) { $self->debug("Module::Signature is installed, verifying") if $CPAN::DEBUG; $self->SIG_check_file($chk_file); } else { $self->debug("Module::Signature is NOT installed") if $CPAN::DEBUG; } } $file = $self->{localfile}; $basename = File::Basename::basename($file); my($signed_data); my $fh = FileHandle->new; if ($check_sigs) { my $tempdir; if ($CPAN::META->has_usable("File::Temp")) { $tempdir = File::Temp::tempdir("CHECKSUMS-XXXX", CLEANUP => 1, DIR => "/tmp" ); } else { $tempdir = File::Spec->catdir(File::Spec->tmpdir, "CHECKSUMS-$$"); File::Path::mkpath($tempdir); } my $tempfile = File::Spec->catfile($tempdir, "CHECKSUMS.$$"); unlink $tempfile; # ignore missing file my $devnull = File::Spec->devnull; my $gpg = $CPAN::Config->{gpg} or $CPAN::Frontend->mydie("Your configuration suggests that you do not have 'gpg' installed. This is needed to verify checksums with the config variable 'check_sigs' on. Please configure it with 'o conf init gpg'"); my $system = qq{"$gpg" --verify --batch --no-tty --output "$tempfile" "$chk_file" 2> "$devnull"}; 0 == system $system or $CPAN::Frontend->mydie("gpg run was failing, cannot continue: $system"); open $fh, $tempfile or $CPAN::Frontend->mydie("Could not open $tempfile: $!"); local $/; $signed_data = <$fh>; close $fh; File::Path::rmtree($tempdir); } else { my $fh = FileHandle->new; if (open $fh, $chk_file) { local($/); $signed_data = <$fh>; } else { $CPAN::Frontend->mydie("Could not open $chk_file for reading"); } close $fh; } $signed_data =~ s/\015?\012/\n/g; my($compmt) = Safe->new(); $cksum = $compmt->reval($signed_data); if ($@) { rename $chk_file, "$chk_file.bad"; Carp::confess($@) if $@; } if (! ref $cksum or ref $cksum ne "HASH") { $CPAN::Frontend->mywarn(qq{ Warning: checksum file '$chk_file' broken. When trying to read that file I expected to get a hash reference for further processing, but got garbage instead. }); my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed nonetheless?", "no"); $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); $self->{CHECKSUM_STATUS} = "NIL -- CHECKSUMS file broken"; return; } elsif (exists $cksum->{$basename} && ! exists $cksum->{$basename}{cpan_path}) { $CPAN::Frontend->mywarn(qq{ Warning: checksum file '$chk_file' not conforming. The cksum does not contain the key 'cpan_path' for '$basename'. }); my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed nonetheless?", "no"); $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); $self->{CHECKSUM_STATUS} = "NIL -- CHECKSUMS file without cpan_path"; return; } elsif (exists $cksum->{$basename} && substr($self->{ID},0,length($cksum->{$basename}{cpan_path})) ne $cksum->{$basename}{cpan_path}) { $CPAN::Frontend->mywarn(qq{ Warning: checksum file not matching path '$self->{ID}'. The cksum contain the key 'cpan_path=$cksum->{$basename}{cpan_path}' which does not match the ID of the distribution '$self->{ID}'. Something's suspicious might be going on here. Please investigate. }); my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed nonetheless?", "no"); $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); $self->{CHECKSUM_STATUS} = "NIL -- CHECKSUMS non-matching cpan_path vs. ID"; return; } elsif (exists $cksum->{$basename}{sha256}) { $self->debug("Found checksum for $basename:" . "$cksum->{$basename}{sha256}\n") if $CPAN::DEBUG; open($fh, $file); binmode $fh; my $eq = $self->eq_CHECKSUM($fh,$cksum->{$basename}{sha256}); $fh->close; $fh = CPAN::Tarzip->TIEHANDLE($file); unless ($eq) { my $dg = Digest::SHA->new(256); my($data,$ref); $ref = \$data; while ($fh->READ($ref, 4096) > 0) { $dg->add($data); } my $hexdigest = $dg->hexdigest; $eq += $hexdigest eq $cksum->{$basename}{'sha256-ungz'}; } if ($eq) { $CPAN::Frontend->myprint("Checksum for $file ok\n"); return $self->{CHECKSUM_STATUS} = "OK"; } else { $CPAN::Frontend->myprint(qq{\nChecksum mismatch for }. qq{distribution file. }. qq{Please investigate.\n\n}. $self->as_string, $CPAN::META->instance( 'CPAN::Author', $self->cpan_userid )->as_string); my $wrap = qq{I\'d recommend removing $file. Its checksum is incorrect. Maybe you have configured your 'urllist' with a bad URL. Please check this array with 'o conf urllist', and retry.}; $CPAN::Frontend->mydie(Text::Wrap::wrap("","",$wrap)); # former versions just returned here but this seems a # serious threat that deserves a die # $CPAN::Frontend->myprint("\n\n"); # sleep 3; # return; } # close $fh if fileno($fh); } else { return if $sloppy; unless ($self->{CHECKSUM_STATUS}) { $CPAN::Frontend->mywarn(qq{ Warning: No checksum for $basename in $chk_file. The cause for this may be that the file is very new and the checksum has not yet been calculated, but it may also be that something is going awry right now. }); my $answer = CPAN::Shell::colorable_makemaker_prompt("Proceed?", "yes"); $answer =~ /^\s*y/i or $CPAN::Frontend->mydie("Aborted.\n"); } $self->{CHECKSUM_STATUS} = "NIL -- distro not in CHECKSUMS file"; return; } } #-> sub CPAN::Distribution::eq_CHECKSUM ; sub eq_CHECKSUM { my($self,$fh,$expect) = @_; if ($CPAN::META->has_inst("Digest::SHA")) { my $dg = Digest::SHA->new(256); my($data); while (read($fh, $data, 4096)) { $dg->add($data); } my $hexdigest = $dg->hexdigest; # warn "fh[$fh] hex[$hexdigest] aexp[$expectMD5]"; return $hexdigest eq $expect; } return 1; } #-> sub CPAN::Distribution::force ; # Both CPAN::Modules and CPAN::Distributions know if "force" is in # effect by autoinspection, not by inspecting a global variable. One # of the reason why this was chosen to work that way was the treatment # of dependencies. They should not automatically inherit the force # status. But this has the downside that ^C and die() will return to # the prompt but will not be able to reset the force_update # attributes. We try to correct for it currently in the read_metadata # routine, and immediately before we check for a Signal. I hope this # works out in one of v1.57_53ff # "Force get forgets previous error conditions" #-> sub CPAN::Distribution::fforce ; sub fforce { my($self, $method) = @_; $self->force($method,1); } #-> sub CPAN::Distribution::force ; sub force { my($self, $method,$fforce) = @_; my %phase_map = ( get => [ "unwrapped", "build_dir", "archived", "localfile", "CHECKSUM_STATUS", "signature_verify", "prefs", "prefs_file", "prefs_file_doc", "cleanup_after_install_done", ], make => [ "writemakefile", "make", "modulebuild", "prereq_pm", "cleanup_after_install_done", ], test => [ "badtestcnt", "make_test", "cleanup_after_install_done", ], install => [ "install", "cleanup_after_install_done", ], unknown => [ "reqtype", "yaml_content", "cleanup_after_install_done", ], ); my $methodmatch = 0; my $ldebug = 0; PHASE: for my $phase (qw(unknown get make test install)) { # order matters $methodmatch = 1 if $fforce || ($method && $phase eq $method); next unless $methodmatch; ATTRIBUTE: for my $att (@{$phase_map{$phase}}) { if ($phase eq "get") { if (substr($self->id,-1,1) eq "." && $att =~ /(unwrapped|build_dir|archived)/ ) { # cannot be undone for local distros next ATTRIBUTE; } if ($att eq "build_dir" && $self->{build_dir} && $CPAN::META->{is_tested} ) { delete $CPAN::META->{is_tested}{$self->{build_dir}}; } } elsif ($phase eq "test") { if ($att eq "make_test" && $self->{make_test} && $self->{make_test}{COMMANDID} && $self->{make_test}{COMMANDID} == $CPAN::CurrentCommandId ) { # endless loop too likely next ATTRIBUTE; } } delete $self->{$att}; if ($ldebug || $CPAN::DEBUG) { # local $CPAN::DEBUG = 16; # Distribution CPAN->debug(sprintf "id[%s]phase[%s]att[%s]", $self->id, $phase, $att); } } } if ($method && $method =~ /make|test|install/) { $self->{force_update} = 1; # name should probably have been force_install } } #-> sub CPAN::Distribution::notest ; sub notest { my($self, $method) = @_; # $CPAN::Frontend->mywarn("XDEBUG: set notest for $self $method"); $self->{"notest"}++; # name should probably have been force_install } #-> sub CPAN::Distribution::unnotest ; sub unnotest { my($self) = @_; # warn "XDEBUG: deleting notest"; delete $self->{notest}; } #-> sub CPAN::Distribution::unforce ; sub unforce { my($self) = @_; delete $self->{force_update}; } #-> sub CPAN::Distribution::isa_perl ; sub isa_perl { my($self) = @_; my $file = File::Basename::basename($self->id); if ($file =~ m{ ^ perl ( -(5\.\d+\.\d+) | (5)[._-](00[0-5](?:_[0-4][0-9])?) ) \.tar[._-](?:gz|bz2) (?!\n)\Z }xs) { my $perl_version; if ($2) { $perl_version = $2; } else { $perl_version = "$3.$4"; } return $perl_version; } elsif ($self->cpan_comment && $self->cpan_comment =~ /isa_perl\(.+?\)/) { return $1; } } #-> sub CPAN::Distribution::perl ; sub perl { my ($self) = @_; if (! $self) { use Carp qw(carp); carp __PACKAGE__ . "::perl was called without parameters."; } return CPAN::HandleConfig->safe_quote($CPAN::Perl); } #-> sub CPAN::Distribution::shortcut_prepare ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_prepare { my ($self) = @_; $self->debug("checking archive type[$self->{ID}]") if $CPAN::DEBUG; if (!$self->{archived} || $self->{archived} eq "NO") { return $self->goodbye("Is neither a tar nor a zip archive."); } $self->debug("checking unwrapping[$self->{ID}]") if $CPAN::DEBUG; if (!$self->{unwrapped} || ( UNIVERSAL::can($self->{unwrapped},"failed") ? $self->{unwrapped}->failed : $self->{unwrapped} =~ /^NO/ )) { return $self->goodbye("Had problems unarchiving. Please build manually"); } $self->debug("checking signature[$self->{ID}]") if $CPAN::DEBUG; if ( ! $self->{force_update} && exists $self->{signature_verify} && ( UNIVERSAL::can($self->{signature_verify},"failed") ? $self->{signature_verify}->failed : $self->{signature_verify} =~ /^NO/ ) ) { return $self->goodbye("Did not pass the signature test."); } $self->debug("checking writemakefile[$self->{ID}]") if $CPAN::DEBUG; if ($self->{writemakefile}) { if ( UNIVERSAL::can($self->{writemakefile},"failed") ? $self->{writemakefile}->failed : $self->{writemakefile} =~ /^NO/ ) { # XXX maybe a retry would be in order? my $err = UNIVERSAL::can($self->{writemakefile},"text") ? $self->{writemakefile}->text : $self->{writemakefile}; $err =~ s/^NO\s*(--\s+)?//; $err ||= "Had some problem writing Makefile"; $err .= ", not re-running"; return $self->goodbye($err); } else { return $self->success("Has already been prepared"); } } $self->debug("checking configure_requires_later[$self->{ID}]") if $CPAN::DEBUG; if( my $later = $self->{configure_requires_later} ) { # see also undelay return $self->goodbye($later); } return undef; # no shortcut } sub prepare { my ($self) = @_; $self->get or return; if ( defined( my $sc = $self->shortcut_prepare) ) { return $sc; } local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # prepare $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls if ($CPAN::Signal) { delete $self->{force_update}; return; } my $builddir = $self->dir or $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); return; } if ($CPAN::Signal) { delete $self->{force_update}; return; } $self->debug("Changed directory to $builddir") if $CPAN::DEBUG; local $ENV{PERL_AUTOINSTALL} = $ENV{PERL_AUTOINSTALL} || ''; local $ENV{PERL_EXTUTILS_AUTOINSTALL} = $ENV{PERL_EXTUTILS_AUTOINSTALL} || ''; $self->choose_MM_or_MB or return; my $configurator = $self->{configure} ? "Configure" : $self->{modulebuild} ? "Build.PL" : "Makefile.PL"; $CPAN::Frontend->myprint("Configuring ".$self->id." with $configurator\n"); if ($CPAN::Config->{prerequisites_policy} eq "follow") { $ENV{PERL_AUTOINSTALL} ||= "--defaultdeps"; $ENV{PERL_EXTUTILS_AUTOINSTALL} ||= "--defaultdeps"; } my $system; my $pl_commandline; if ($self->prefs->{pl}) { $pl_commandline = $self->prefs->{pl}{commandline}; } local $ENV{PERL} = defined $ENV{PERL}? $ENV{PERL} : $^X; local $ENV{PERL5_CPAN_IS_EXECUTING} = $ENV{PERL5_CPAN_IS_EXECUTING} || ''; local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; if ($pl_commandline) { $system = $pl_commandline; $ENV{PERL} = $^X; } elsif ($self->{'configure'}) { $system = $self->{'configure'}; } elsif ($self->{modulebuild}) { my($perl) = $self->perl or die "Couldn\'t find executable perl\n"; my $mbuildpl_arg = $self->_make_phase_arg("pl"); $system = sprintf("%s Build.PL%s", $perl, $mbuildpl_arg ? " $mbuildpl_arg" : "", ); } else { my($perl) = $self->perl or die "Couldn\'t find executable perl\n"; my $switch = ""; # This needs a handler that can be turned on or off: # $switch = "-MExtUtils::MakeMaker ". # "-Mops=:default,:filesys_read,:filesys_open,require,chdir" # if $] > 5.00310; my $makepl_arg = $self->_make_phase_arg("pl"); $ENV{PERL5_CPAN_IS_EXECUTING} = File::Spec->catfile($self->{build_dir}, "Makefile.PL"); $system = sprintf("%s%s Makefile.PL%s", $perl, $switch ? " $switch" : "", $makepl_arg ? " $makepl_arg" : "", ); } my $pl_env; if ($self->prefs->{pl}) { $pl_env = $self->prefs->{pl}{env}; } local @ENV{keys %$pl_env} = values %$pl_env if $pl_env; if (exists $self->{writemakefile}) { } else { local($SIG{ALRM}) = sub { die "inactivity_timeout reached\n" }; my($ret,$pid,$output); $@ = ""; my $go_via_alarm; if ($CPAN::Config->{inactivity_timeout}) { require Config; if ($Config::Config{d_alarm} && $Config::Config{d_alarm} eq "define" ) { $go_via_alarm++ } else { $CPAN::Frontend->mywarn("Warning: you have configured the config ". "variable 'inactivity_timeout' to ". "'$CPAN::Config->{inactivity_timeout}'. But ". "on this machine the system call 'alarm' ". "isn't available. This means that we cannot ". "provide the feature of intercepting long ". "waiting code and will turn this feature off.\n" ); $CPAN::Config->{inactivity_timeout} = 0; } } if ($go_via_alarm) { if ( $self->_should_report('pl') ) { ($output, $ret) = CPAN::Reporter::record_command( $system, $CPAN::Config->{inactivity_timeout}, ); CPAN::Reporter::grade_PL( $self, $system, $output, $ret ); } else { eval { alarm $CPAN::Config->{inactivity_timeout}; local $SIG{CHLD}; # = sub { wait }; if (defined($pid = fork)) { if ($pid) { #parent # wait; waitpid $pid, 0; } else { #child # note, this exec isn't necessary if # inactivity_timeout is 0. On the Mac I'd # suggest, we set it always to 0. exec $system; } } else { $CPAN::Frontend->myprint("Cannot fork: $!"); return; } }; alarm 0; if ($@) { kill 9, $pid; waitpid $pid, 0; my $err = "$@"; $CPAN::Frontend->myprint($err); $self->{writemakefile} = CPAN::Distrostatus->new("NO $err"); $@ = ""; $self->store_persistent_state; return $self->goodbye("$system -- TIMED OUT"); } } } else { if (my $expect_model = $self->_prefs_with_expect("pl")) { # XXX probably want to check _should_report here and warn # about not being able to use CPAN::Reporter with expect $ret = $self->_run_via_expect($system,'writemakefile',$expect_model); if (! defined $ret && $self->{writemakefile} && $self->{writemakefile}->failed) { # timeout return; } } elsif ( $self->_should_report('pl') ) { ($output, $ret) = eval { CPAN::Reporter::record_command($system) }; if (! defined $output or $@) { my $err = $@ || "Unknown error"; $CPAN::Frontend->mywarn("Error while running PL phase: $err\n"); $self->{writemakefile} = CPAN::Distrostatus ->new("NO '$system' returned status $ret and no output"); return $self->goodbye("$system -- NOT OK"); } CPAN::Reporter::grade_PL( $self, $system, $output, $ret ); } else { $ret = system($system); } if ($ret != 0) { $self->{writemakefile} = CPAN::Distrostatus ->new("NO '$system' returned status $ret"); $CPAN::Frontend->mywarn("Warning: No success on command[$system]\n"); $self->store_persistent_state; return $self->goodbye("$system -- NOT OK"); } } if (-f "Makefile" || -f "Build" || ($^O eq 'VMS' && (-f 'descrip.mms' || -f 'Build.com'))) { $self->{writemakefile} = CPAN::Distrostatus->new("YES"); delete $self->{make_clean}; # if cleaned before, enable next $self->store_persistent_state; return $self->success("$system -- OK"); } else { my $makefile = $self->{modulebuild} ? "Build" : "Makefile"; my $why = "No '$makefile' created"; $CPAN::Frontend->mywarn($why); $self->{writemakefile} = CPAN::Distrostatus ->new(qq{NO -- $why\n}); $self->store_persistent_state; return $self->goodbye("$system -- NOT OK"); } } $self->store_persistent_state; return 1; # success } #-> sub CPAN::Distribution::shortcut_make ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_make { my ($self) = @_; $self->debug("checking make/build results[$self->{ID}]") if $CPAN::DEBUG; if (defined $self->{make}) { if (UNIVERSAL::can($self->{make},"failed") ? $self->{make}->failed : $self->{make} =~ /^NO/ ) { if ($self->{force_update}) { # Trying an already failed 'make' (unless somebody else blocks) return undef; # no shortcut } else { # introduced for turning recursion detection into a distrostatus my $error = length $self->{make}>3 ? substr($self->{make},3) : "Unknown error"; $self->store_persistent_state; return $self->goodbye("Could not make: $error\n"); } } else { return $self->success("Has already been made") } } return undef; # no shortcut } #-> sub CPAN::Distribution::make ; sub make { my($self) = @_; $self->pre_make(); if (exists $self->{cleanup_after_install_done}) { $self->post_make(); return $self->get; } $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; if (my $goto = $self->prefs->{goto}) { $self->post_make(); return $self->goto($goto); } # Emergency brake if they said install Pippi and get newest perl # XXX Would this make more sense in shortcut_prepare, since # that doesn't make sense on a perl dist either? Broader # question: what is the purpose of suggesting force install # on a perl distribution? That seems unlikely to result in # such a dependency being satisfied, even if the perl is # successfully installed. This situation is tantamount to # a prereq on a version of perl greater than the current one # so I think we should just abort. -- xdg, 2012-04-06 if ($self->isa_perl) { if ( $self->called_for ne $self->id && ! $self->{force_update} ) { # if we die here, we break bundles $CPAN::Frontend ->mywarn(sprintf( qq{The most recent version "%s" of the module "%s" is part of the perl-%s distribution. To install that, you need to run force install %s --or-- install %s }, $CPAN::META->instance( 'CPAN::Module', $self->called_for )->cpan_version, $self->called_for, $self->isa_perl, $self->called_for, $self->pretty_id, )); $self->{make} = CPAN::Distrostatus->new("NO isa perl"); $CPAN::Frontend->mysleep(1); $self->post_make(); return; } } unless ($self->prepare){ $self->post_make(); return; } if ( defined( my $sc = $self->shortcut_make) ) { $self->post_make(); return $sc; } if ($CPAN::Signal) { delete $self->{force_update}; $self->post_make(); return; } my $builddir = $self->dir or $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); $self->post_make(); return; } my $make = $self->{modulebuild} ? "Build" : "make"; $CPAN::Frontend->myprint(sprintf "Running %s for %s\n", $make, $self->id); local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # make $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls if ($CPAN::Signal) { delete $self->{force_update}; $self->post_make(); return; } if ($^O eq 'MacOS') { Mac::BuildTools::make($self); $self->post_make(); return; } my %env; while (my($k,$v) = each %ENV) { next if defined $v; $env{$k} = ''; } local @ENV{keys %env} = values %env; my $satisfied = eval { $self->satisfy_requires }; if ($@) { return $self->goodbye($@); } unless ($satisfied){ $self->post_make(); return; } if ($CPAN::Signal) { delete $self->{force_update}; $self->post_make(); return; } # need to chdir again, because $self->satisfy_requires might change the directory unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); $self->post_make(); return; } my $system; my $make_commandline; if ($self->prefs->{make}) { $make_commandline = $self->prefs->{make}{commandline}; } local $ENV{PERL} = defined $ENV{PERL}? $ENV{PERL} : $^X; local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; if ($make_commandline) { $system = $make_commandline; $ENV{PERL} = CPAN::find_perl(); } else { if ($self->{modulebuild}) { unless (-f "Build" || ($^O eq 'VMS' && -f 'Build.com')) { my $cwd = CPAN::anycwd(); $CPAN::Frontend->mywarn("Alert: no Build file available for 'make $self->{id}'". " in cwd[$cwd]. Danger, Will Robinson!\n"); $CPAN::Frontend->mysleep(5); } $system = join " ", $self->_build_command(), $CPAN::Config->{mbuild_arg}; } else { $system = join " ", $self->_make_command(), $CPAN::Config->{make_arg}; } $system =~ s/\s+$//; my $make_arg = $self->_make_phase_arg("make"); $system = sprintf("%s%s", $system, $make_arg ? " $make_arg" : "", ); } my $make_env; if ($self->prefs->{make}) { $make_env = $self->prefs->{make}{env}; } local @ENV{keys %$make_env} = values %$make_env if $make_env; my $expect_model = $self->_prefs_with_expect("make"); my $want_expect = 0; if ( $expect_model && @{$expect_model->{talk}} ) { my $can_expect = $CPAN::META->has_inst("Expect"); if ($can_expect) { $want_expect = 1; } else { $CPAN::Frontend->mywarn("Expect not installed, falling back to ". "system()\n"); } } my ($system_ok, $system_err); if ($want_expect) { # XXX probably want to check _should_report here and # warn about not being able to use CPAN::Reporter with expect $system_ok = $self->_run_via_expect($system,'make',$expect_model) == 0; } elsif ( $self->_should_report('make') ) { my ($output, $ret) = CPAN::Reporter::record_command($system); CPAN::Reporter::grade_make( $self, $system, $output, $ret ); $system_ok = ! $ret; } else { my $rc = system($system); $system_ok = $rc == 0; $system_err = $! if $rc == -1; } $self->introduce_myself; if ( $system_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); $self->{make} = CPAN::Distrostatus->new("YES"); } else { $self->{writemakefile} ||= CPAN::Distrostatus->new("YES"); $self->{make} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); $CPAN::Frontend->mywarn(" $system_err\n") if defined $system_err; } $self->store_persistent_state; $self->post_make(); return !! $system_ok; } # CPAN::Distribution::goodbye ; sub goodbye { my($self,$goodbye) = @_; my $id = $self->pretty_id; $CPAN::Frontend->mywarn(" $id\n $goodbye\n"); return 0; # must be explicit false, not undef } sub success { my($self,$why) = @_; my $id = $self->pretty_id; $CPAN::Frontend->myprint(" $id\n $why\n"); return 1; } # CPAN::Distribution::_run_via_expect ; sub _run_via_expect { my($self,$system,$phase,$expect_model) = @_; CPAN->debug("system[$system]expect_model[$expect_model]") if $CPAN::DEBUG; if ($CPAN::META->has_inst("Expect")) { my $expo = Expect->new; # expo Expect object; $expo->spawn($system); $expect_model->{mode} ||= "deterministic"; if ($expect_model->{mode} eq "deterministic") { return $self->_run_via_expect_deterministic($expo,$phase,$expect_model); } elsif ($expect_model->{mode} eq "anyorder") { return $self->_run_via_expect_anyorder($expo,$phase,$expect_model); } else { die "Panic: Illegal expect mode: $expect_model->{mode}"; } } else { $CPAN::Frontend->mywarn("Expect not installed, falling back to system()\n"); return system($system); } } sub _run_via_expect_anyorder { my($self,$expo,$phase,$expect_model) = @_; my $timeout = $expect_model->{timeout} || 5; my $reuse = $expect_model->{reuse}; my @expectacopy = @{$expect_model->{talk}}; # we trash it! my $but = ""; my $timeout_start = time; EXPECT: while () { my($eof,$ran_into_timeout); # XXX not up to the full power of expect. one could certainly # wrap all of the talk pairs into a single expect call and on # success tweak it and step ahead to the next question. The # current implementation unnecessarily limits itself to a # single match. my @match = $expo->expect(1, [ eof => sub { $eof++; } ], [ timeout => sub { $ran_into_timeout++; } ], -re => eval"qr{.}", ); if ($match[2]) { $but .= $match[2]; } $but .= $expo->clear_accum; if ($eof) { $expo->soft_close; return $expo->exitstatus(); } elsif ($ran_into_timeout) { # warn "DEBUG: they are asking a question, but[$but]"; for (my $i = 0; $i <= $#expectacopy; $i+=2) { my($next,$send) = @expectacopy[$i,$i+1]; my $regex = eval "qr{$next}"; # warn "DEBUG: will compare with regex[$regex]."; if ($but =~ /$regex/) { # warn "DEBUG: will send send[$send]"; $expo->send($send); # never allow reusing an QA pair unless they told us splice @expectacopy, $i, 2 unless $reuse; $but =~ s/(?s:^.*?)$regex//; $timeout_start = time; next EXPECT; } } my $have_waited = time - $timeout_start; if ($have_waited < $timeout) { # warn "DEBUG: have_waited[$have_waited]timeout[$timeout]"; next EXPECT; } my $why = "could not answer a question during the dialog"; $CPAN::Frontend->mywarn("Failing: $why\n"); $self->{$phase} = CPAN::Distrostatus->new("NO $why"); return 0; } } } sub _run_via_expect_deterministic { my($self,$expo,$phase,$expect_model) = @_; my $ran_into_timeout; my $ran_into_eof; my $timeout = $expect_model->{timeout} || 15; # currently unsettable my $expecta = $expect_model->{talk}; EXPECT: for (my $i = 0; $i <= $#$expecta; $i+=2) { my($re,$send) = @$expecta[$i,$i+1]; CPAN->debug("timeout[$timeout]re[$re]") if $CPAN::DEBUG; my $regex = eval "qr{$re}"; $expo->expect($timeout, [ eof => sub { my $but = $expo->clear_accum; $CPAN::Frontend->mywarn("EOF (maybe harmless) expected[$regex]\nbut[$but]\n\n"); $ran_into_eof++; } ], [ timeout => sub { my $but = $expo->clear_accum; $CPAN::Frontend->mywarn("TIMEOUT expected[$regex]\nbut[$but]\n\n"); $ran_into_timeout++; } ], -re => $regex); if ($ran_into_timeout) { # note that the caller expects 0 for success $self->{$phase} = CPAN::Distrostatus->new("NO timeout during expect dialog"); return 0; } elsif ($ran_into_eof) { last EXPECT; } $expo->send($send); } $expo->soft_close; return $expo->exitstatus(); } #-> CPAN::Distribution::_validate_distropref sub _validate_distropref { my($self,@args) = @_; if ( $CPAN::META->has_inst("CPAN::Kwalify") && $CPAN::META->has_inst("Kwalify") ) { eval {CPAN::Kwalify::_validate("distroprefs",@args);}; if ($@) { $CPAN::Frontend->mywarn($@); } } else { CPAN->debug("not validating '@args'") if $CPAN::DEBUG; } } #-> CPAN::Distribution::_find_prefs sub _find_prefs { my($self) = @_; my $distroid = $self->pretty_id; #CPAN->debug("distroid[$distroid]") if $CPAN::DEBUG; my $prefs_dir = $CPAN::Config->{prefs_dir}; return if $prefs_dir =~ /^\s*$/; eval { File::Path::mkpath($prefs_dir); }; if ($@) { $CPAN::Frontend->mydie("Cannot create directory $prefs_dir"); } # shortcut if there are no distroprefs files { my $dh = DirHandle->new($prefs_dir) or $CPAN::Frontend->mydie("Couldn't open '$prefs_dir': $!"); my @files = map { /\.(yml|dd|st)\z/i } $dh->read; return unless @files; } my $yaml_module = CPAN::_yaml_module(); my $ext_map = {}; my @extensions; if ($CPAN::META->has_inst($yaml_module)) { $ext_map->{yml} = 'CPAN'; } else { my @fallbacks; if ($CPAN::META->has_inst("Data::Dumper")) { push @fallbacks, $ext_map->{dd} = 'Data::Dumper'; } if ($CPAN::META->has_inst("Storable")) { push @fallbacks, $ext_map->{st} = 'Storable'; } if (@fallbacks) { local $" = " and "; unless ($self->{have_complained_about_missing_yaml}++) { $CPAN::Frontend->mywarnonce("'$yaml_module' not installed, falling back ". "to @fallbacks to read prefs '$prefs_dir'\n"); } } else { unless ($self->{have_complained_about_missing_yaml}++) { $CPAN::Frontend->mywarnonce("'$yaml_module' not installed, cannot ". "read prefs '$prefs_dir'\n"); } } } my $finder = CPAN::Distroprefs->find($prefs_dir, $ext_map); DIRENT: while (my $result = $finder->next) { if ($result->is_warning) { $CPAN::Frontend->mywarn($result->as_string); $CPAN::Frontend->mysleep(1); next DIRENT; } elsif ($result->is_fatal) { $CPAN::Frontend->mydie($result->as_string); } my @prefs = @{ $result->prefs }; ELEMENT: for my $y (0..$#prefs) { my $pref = $prefs[$y]; $self->_validate_distropref($pref->data, $result->abs, $y); # I don't know why we silently skip when there's no match, but # complain if there's an empty match hashref, and there's no # comment explaining why -- hdp, 2008-03-18 unless ($pref->has_any_match) { next ELEMENT; } unless ($pref->has_valid_subkeys) { $CPAN::Frontend->mydie(sprintf "Nonconforming .%s file '%s': " . "missing match/* subattribute. " . "Please remove, cannot continue.", $result->ext, $result->abs, ); } my $arg = { env => \%ENV, distribution => $distroid, perl => \&CPAN::find_perl, perlconfig => \%Config::Config, module => sub { [ $self->containsmods ] }, }; if ($pref->matches($arg)) { return { prefs => $pref->data, prefs_file => $result->abs, prefs_file_doc => $y, }; } } } return; } # CPAN::Distribution::prefs sub prefs { my($self) = @_; if (exists $self->{negative_prefs_cache} && $self->{negative_prefs_cache} != $CPAN::CurrentCommandId ) { delete $self->{negative_prefs_cache}; delete $self->{prefs}; } if (exists $self->{prefs}) { return $self->{prefs}; # XXX comment out during debugging } if ($CPAN::Config->{prefs_dir}) { CPAN->debug("prefs_dir[$CPAN::Config->{prefs_dir}]") if $CPAN::DEBUG; my $prefs = $self->_find_prefs(); $prefs ||= ""; # avoid warning next line CPAN->debug("prefs[$prefs]") if $CPAN::DEBUG; if ($prefs) { for my $x (qw(prefs prefs_file prefs_file_doc)) { $self->{$x} = $prefs->{$x}; } my $bs = sprintf( "%s[%s]", File::Basename::basename($self->{prefs_file}), $self->{prefs_file_doc}, ); my $filler1 = "_" x 22; my $filler2 = int(66 - length($bs))/2; $filler2 = 0 if $filler2 < 0; $filler2 = " " x $filler2; $CPAN::Frontend->myprint(" $filler1 D i s t r o P r e f s $filler1 $filler2 $bs $filler2 "); $CPAN::Frontend->mysleep(1); return $self->{prefs}; } } $self->{negative_prefs_cache} = $CPAN::CurrentCommandId; return $self->{prefs} = +{}; } # CPAN::Distribution::_make_phase_arg sub _make_phase_arg { my($self, $phase) = @_; my $_make_phase_arg; my $prefs = $self->prefs; if ( $prefs && exists $prefs->{$phase} && exists $prefs->{$phase}{args} && $prefs->{$phase}{args} ) { $_make_phase_arg = join(" ", map {CPAN::HandleConfig ->safe_quote($_)} @{$prefs->{$phase}{args}}, ); } # cpan[2]> o conf make[TAB] # make make_install_make_command # make_arg makepl_arg # make_install_arg # cpan[2]> o conf mbuild[TAB] # mbuild_arg mbuild_install_build_command # mbuild_install_arg mbuildpl_arg my $mantra; # must switch make/mbuild here if ($self->{modulebuild}) { $mantra = "mbuild"; } else { $mantra = "make"; } my %map = ( pl => "pl_arg", make => "_arg", test => "_test_arg", # does not really exist but maybe # will some day and now protects # us from unini warnings install => "_install_arg", ); my $phase_underscore_meshup = $map{$phase}; my $what = sprintf "%s%s", $mantra, $phase_underscore_meshup; $_make_phase_arg ||= $CPAN::Config->{$what}; return $_make_phase_arg; } # CPAN::Distribution::_make_command sub _make_command { my ($self) = @_; if ($self) { return CPAN::HandleConfig ->safe_quote( CPAN::HandleConfig->prefs_lookup($self, q{make}) || $Config::Config{make} || 'make' ); } else { # Old style call, without object. Deprecated Carp::confess("CPAN::_make_command() used as function. Don't Do That."); return safe_quote(undef, CPAN::HandleConfig->prefs_lookup($self,q{make}) || $CPAN::Config->{make} || $Config::Config{make} || 'make'); } } sub _make_install_make_command { my ($self) = @_; my $mimc = CPAN::HandleConfig->prefs_lookup($self, q{make_install_make_command}); return $self->_make_command() unless $mimc; # Quote the "make install" make command on Windows, where it is commonly # found in, e.g., C:\Program Files\... and therefore needs quoting. We can't # do this in general because the command maybe "sudo make..." (i.e. a # program with arguments), but that is unlikely to be the case on Windows. $mimc = CPAN::HandleConfig->safe_quote($mimc) if $^O eq 'MSWin32'; return $mimc; } #-> sub CPAN::Distribution::is_locally_optional sub is_locally_optional { my($self, $prereq_pm, $prereq) = @_; $prereq_pm ||= $self->{prereq_pm}; my($nmo,$opt); for my $rt (qw(requires build_requires)) { if (exists $prereq_pm->{$rt}{$prereq}) { # rt 121914 $nmo ||= $CPAN::META->instance("CPAN::Module",$prereq); my $av = $nmo->available_version; return 0 if !$av || CPAN::Version->vlt($av,$prereq_pm->{$rt}{$prereq}); } if (exists $prereq_pm->{"opt_$rt"}{$prereq}) { $opt = 1; } } return $opt||0; } #-> sub CPAN::Distribution::follow_prereqs ; sub follow_prereqs { my($self) = shift; my($slot) = shift; my(@prereq_tuples) = grep {$_->[0] ne "perl"} @_; return unless @prereq_tuples; my(@good_prereq_tuples); for my $p (@prereq_tuples) { # e.g. $p = ['Devel::PartialDump', 'r', 1] # promote if possible if ($p->[1] =~ /^(r|c)$/) { push @good_prereq_tuples, $p; } elsif ($p->[1] =~ /^(b)$/) { my $reqtype = CPAN::Queue->reqtype_of($p->[0]); if ($reqtype =~ /^(r|c)$/) { push @good_prereq_tuples, [$p->[0], $reqtype, $p->[2]]; } else { push @good_prereq_tuples, $p; } } else { die "Panic: in follow_prereqs: reqtype[$p->[1]] seen, should never happen"; } } my $pretty_id = $self->pretty_id; my %map = ( b => "build_requires", r => "requires", c => "commandline", ); my($filler1,$filler2,$filler3,$filler4); my $unsat = "Unsatisfied dependencies detected during"; my $w = length($unsat) > length($pretty_id) ? length($unsat) : length($pretty_id); { my $r = int(($w - length($unsat))/2); my $l = $w - length($unsat) - $r; $filler1 = "-"x4 . " "x$l; $filler2 = " "x$r . "-"x4 . "\n"; } { my $r = int(($w - length($pretty_id))/2); my $l = $w - length($pretty_id) - $r; $filler3 = "-"x4 . " "x$l; $filler4 = " "x$r . "-"x4 . "\n"; } $CPAN::Frontend-> myprint("$filler1 $unsat $filler2". "$filler3 $pretty_id $filler4". join("", map {sprintf " %s \[%s%s]\n", $_->[0], $map{$_->[1]}, $self->is_locally_optional(undef,$_->[0]) ? ",optional" : ""} @good_prereq_tuples), ); my $follow = 0; if ($CPAN::Config->{prerequisites_policy} eq "follow") { $follow = 1; } elsif ($CPAN::Config->{prerequisites_policy} eq "ask") { my $answer = CPAN::Shell::colorable_makemaker_prompt( "Shall I follow them and prepend them to the queue of modules we are processing right now?", "yes"); $follow = $answer =~ /^\s*y/i; } else { my @prereq = map { $_->[0] } @good_prereq_tuples; local($") = ", "; $CPAN::Frontend-> myprint(" Ignoring dependencies on modules @prereq\n"); } if ($follow) { my $id = $self->id; my(@to_queue_mand,@to_queue_opt); for my $gp (@good_prereq_tuples) { my($prereq,$reqtype,$optional) = @$gp; my $qthing = +{qmod=>$prereq,reqtype=>$reqtype,optional=>$optional}; if ($optional && $self->is_locally_optional(undef,$prereq) ){ # Since we do not depend on this one, we do not need # this in a mandatory arrangement: push @to_queue_opt, $qthing; } else { my $any = CPAN::Shell->expandany($prereq); $self->{$slot . "_for"}{$any->id}++; if ($any) { unless ($optional) { # No recursion check in an optional area of the tree $any->color_cmd_tmps(0,2); } } else { $CPAN::Frontend->mywarn("Warning (maybe a bug): Cannot expand prereq '$prereq'\n"); $CPAN::Frontend->mysleep(2); } # order everything that is not locally_optional just # like mandatory items: this keeps leaves before # branches unshift @to_queue_mand, $qthing; } } if (@to_queue_mand) { unshift @to_queue_mand, {qmod => $id, reqtype => $self->{reqtype}, optional=> !$self->{mandatory}}; CPAN::Queue->jumpqueue(@to_queue_opt,@to_queue_mand); $self->{$slot} = "Delayed until after prerequisites"; return 1; # signal we need dependencies } elsif (@to_queue_opt) { CPAN::Queue->jumpqueue(@to_queue_opt); } } return; } sub _feature_depends { my($self) = @_; my $meta_yml = $self->parse_meta_yml(); my $optf = $meta_yml->{optional_features} or return; if (!ref $optf or ref $optf ne "HASH"){ $CPAN::Frontend->mywarn("The content of optional_features is not a HASH reference. Cannot use it.\n"); $optf = {}; } my $wantf = $self->prefs->{features} or return; if (!ref $wantf or ref $wantf ne "ARRAY"){ $CPAN::Frontend->mywarn("The content of 'features' is not an ARRAY reference. Cannot use it.\n"); $wantf = []; } my $dep = +{}; for my $wf (@$wantf) { if (my $f = $optf->{$wf}) { $CPAN::Frontend->myprint("Found the demanded feature '$wf' that ". "is accompanied by this description:\n". $f->{description}. "\n\n" ); # configure_requires currently not in the spec, unlikely to be useful anyway for my $reqtype (qw(configure_requires build_requires requires)) { my $reqhash = $f->{$reqtype} or next; while (my($k,$v) = each %$reqhash) { $dep->{$reqtype}{$k} = $v; } } } else { $CPAN::Frontend->mywarn("The demanded feature '$wf' was not ". "found in the META.yml file". "\n\n" ); } } $dep; } sub prereqs_for_slot { my($self,$slot) = @_; my($prereq_pm); unless ($CPAN::META->has_usable("CPAN::Meta::Requirements")) { my $whynot = "not available"; if (defined $CPAN::Meta::Requirements::VERSION) { $whynot = "version $CPAN::Meta::Requirements::VERSION not sufficient"; } $CPAN::Frontend->mywarn("CPAN::Meta::Requirements $whynot\n"); my $before = ""; if ($self->{CALLED_FOR}){ if ($self->{CALLED_FOR} =~ /^( CPAN::Meta::Requirements |CPAN::DistnameInfo |version |parent |ExtUtils::MakeMaker |Test::Harness )$/x) { $CPAN::Frontend->mywarn("Please install CPAN::Meta::Requirements ". "as soon as possible; it is needed for a reliable operation of ". "the cpan shell; setting requirements to nil for '$1' for now ". "to prevent deadlock during bootstrapping\n"); return; } $before = " before $self->{CALLED_FOR}"; } $CPAN::Frontend->mydie("Please install CPAN::Meta::Requirements manually$before"); } my $merged = CPAN::Meta::Requirements->new; my $prefs_depends = $self->prefs->{depends}||{}; my $feature_depends = $self->_feature_depends(); if ($slot eq "configure_requires_later") { for my $hash ( $self->configure_requires, $prefs_depends->{configure_requires}, $feature_depends->{configure_requires}, ) { $merged->add_requirements( CPAN::Meta::Requirements->from_string_hash($hash) ); } if (-f "Build.PL" && ! -f File::Spec->catfile($self->{build_dir},"Makefile.PL") && ! @{[ $merged->required_modules ]} && ! $CPAN::META->has_inst("Module::Build") ) { $CPAN::Frontend->mywarn( " Warning: CPAN.pm discovered Module::Build as undeclared prerequisite.\n". " Adding it now as such.\n" ); $CPAN::Frontend->mysleep(5); $merged->add_minimum( "Module::Build" => 0 ); delete $self->{writemakefile}; } $prereq_pm = {}; # configure_requires defined as "b" } elsif ($slot eq "later") { my $prereq_pm_0 = $self->prereq_pm || {}; for my $reqtype (qw(requires build_requires opt_requires opt_build_requires)) { $prereq_pm->{$reqtype} = {%{$prereq_pm_0->{$reqtype}||{}}}; # copy to not pollute it for my $dep ($prefs_depends,$feature_depends) { for my $k (keys %{$dep->{$reqtype}||{}}) { $prereq_pm->{$reqtype}{$k} = $dep->{$reqtype}{$k}; } } } # XXX what about optional_req|breq? -- xdg, 2012-04-01 for my $hash ( $prereq_pm->{requires}, $prereq_pm->{build_requires}, $prereq_pm->{opt_requires}, $prereq_pm->{opt_build_requires}, ) { $merged->add_requirements( CPAN::Meta::Requirements->from_string_hash($hash) ); } } else { die "Panic: illegal slot '$slot'"; } return ($merged->as_string_hash, $prereq_pm); } #-> sub CPAN::Distribution::unsat_prereq ; # return ([Foo,"r"],[Bar,"b"]) for normal modules # return ([perl=>5.008]) if we need a newer perl than we are running under # (sorry for the inconsistency, it was an accident) sub unsat_prereq { my($self,$slot) = @_; my($merged_hash,$prereq_pm) = $self->prereqs_for_slot($slot); my(@need); unless ($CPAN::META->has_usable("CPAN::Meta::Requirements")) { $CPAN::Frontend->mywarn("CPAN::Meta::Requirements not available, please install as soon as possible, trying to continue with severly limited capabilities\n"); return; } my $merged = CPAN::Meta::Requirements->from_string_hash($merged_hash); my @merged = sort $merged->required_modules; CPAN->debug("all merged_prereqs[@merged]") if $CPAN::DEBUG; NEED: for my $need_module ( @merged ) { my $need_version = $merged->requirements_for_module($need_module); my($available_version,$inst_file,$available_file,$nmo); if ($need_module eq "perl") { $available_version = $]; $available_file = CPAN::find_perl(); } else { if (CPAN::_sqlite_running()) { CPAN::Index->reload; $CPAN::SQLite->search("CPAN::Module",$need_module); } $nmo = $CPAN::META->instance("CPAN::Module",$need_module); $inst_file = $nmo->inst_file || ''; $available_file = $nmo->available_file || ''; $available_version = $nmo->available_version; if ($nmo->uptodate) { my $accepts = eval { $merged->accepts_module($need_module, $available_version); }; unless ($accepts) { my $rq = $merged->requirements_for_module( $need_module ); $CPAN::Frontend->mywarn( "Warning: Version '$available_version' of ". "'$need_module' is up to date but does not ". "fulfill requirements ($rq). I will continue, ". "but chances to succeed are low.\n"); } next NEED; } # if they have not specified a version, we accept any # installed one; in that case inst_file is always # sufficient and available_file is sufficient on # both build_requires and configure_requires my $sufficient = $inst_file || ( exists $prereq_pm->{requires}{$need_module} ? 0 : $available_file ); if ( $sufficient and ( # a few quick short circuits not defined $need_version or $need_version eq '0' # "==" would trigger warning when not numeric or $need_version eq "undef" )) { unless ($nmo->inst_deprecated) { next NEED; } } } # We only want to install prereqs if either they're not installed # or if the installed version is too old. We cannot omit this # check, because if 'force' is in effect, nobody else will check. # But we don't want to accept a deprecated module installed as part # of the Perl core, so we continue if the available file is the installed # one and is deprecated if ( $available_file ) { my $fulfills_all_version_rqs = $self->_fulfills_all_version_rqs ( $need_module, $available_file, $available_version, $need_version, ); if ( $inst_file && $available_file eq $inst_file && $nmo->inst_deprecated ) { # continue installing as a prereq. we really want that # because the deprecated module may spit out warnings # and third party did not know until today. Only one # exception is OK, because CPANPLUS is special after # all: if ( $fulfills_all_version_rqs and $nmo->id =~ /^CPANPLUS(?:::Dist::Build)$/ ) { # here we have an available version that is good # enough although deprecated (preventing circular # loop CPANPLUS => CPANPLUS::Dist::Build RT#83042) next NEED; } } elsif ( $self->{reqtype} # e.g. maybe we came via goto? && $self->{reqtype} =~ /^(r|c)$/ && ( exists $prereq_pm->{requires}{$need_module} || exists $prereq_pm->{opt_requires}{$need_module} ) && $nmo && !$inst_file ) { # continue installing as a prereq; this may be a # distro we already used when it was a build_requires # so we did not install it. But suddenly somebody # wants it as a requires my $need_distro = $nmo->distribution; if ($need_distro->{install} && $need_distro->{install}->failed && $need_distro->{install}->text =~ /is only/) { my $id = $need_distro->pretty_id; $CPAN::Frontend->myprint("Promoting $id from build_requires to requires due $need_module\n"); delete $need_distro->{install}; # promote to another installation attempt $need_distro->{reqtype} = "r"; $need_distro->install; next NEED; } } else { next NEED if $fulfills_all_version_rqs; } } if ($need_module eq "perl") { return ["perl", $need_version]; } $self->{sponsored_mods}{$need_module} ||= 0; CPAN->debug("need_module[$need_module]s/s/n[$self->{sponsored_mods}{$need_module}]") if $CPAN::DEBUG; if (my $sponsoring = $self->{sponsored_mods}{$need_module}++) { # We have already sponsored it and for some reason it's still # not available. So we do ... what?? # if we push it again, we have a potential infinite loop # The following "next" was a very problematic construct. # It helped a lot but broke some day and had to be # replaced. # We must be able to deal with modules that come again and # again as a prereq and have themselves prereqs and the # queue becomes long but finally we would find the correct # order. The RecursiveDependency check should trigger a # die when it's becoming too weird. Unfortunately removing # this next breaks many other things. # The bug that brought this up is described in Todo under # "5.8.9 cannot install Compress::Zlib" # next; # this is the next that had to go away # The following "next NEED" are fine and the error message # explains well what is going on. For example when the DBI # fails and consequently DBD::SQLite fails and now we are # processing CPAN::SQLite. Then we must have a "next" for # DBD::SQLite. How can we get it and how can we identify # all other cases we must identify? my $do = $nmo->distribution; next NEED unless $do; # not on CPAN if (CPAN::Version->vcmp($need_version, $nmo->ro->{CPAN_VERSION}) > 0){ $CPAN::Frontend->mywarn("Warning: Prerequisite ". "'$need_module => $need_version' ". "for '$self->{ID}' seems ". "not available according to the indices\n" ); next NEED; } NOSAYER: for my $nosayer ( "unwrapped", "writemakefile", "signature_verify", "make", "make_test", "install", "make_clean", ) { if ($do->{$nosayer}) { my $selfid = $self->pretty_id; my $did = $do->pretty_id; if (UNIVERSAL::can($do->{$nosayer},"failed") ? $do->{$nosayer}->failed : $do->{$nosayer} =~ /^NO/) { if ($nosayer eq "make_test" && $do->{make_test}{COMMANDID} != $CPAN::CurrentCommandId ) { next NOSAYER; } ### XXX don't complain about missing optional deps -- xdg, 2012-04-01 if ($self->is_locally_optional($prereq_pm, $need_module)) { # don't complain about failing optional prereqs } else { $CPAN::Frontend->mywarn("Warning: Prerequisite ". "'$need_module => $need_version' ". "for '$selfid' failed when ". "processing '$did' with ". "'$nosayer => $do->{$nosayer}'. Continuing, ". "but chances to succeed are limited.\n" ); $CPAN::Frontend->mysleep($sponsoring/10); } next NEED; } else { # the other guy succeeded if ($nosayer =~ /^(install|make_test)$/) { # we had this with # DMAKI/DateTime-Calendar-Chinese-0.05.tar.gz # in 2007-03 for 'make install' # and 2008-04: #30464 (for 'make test') # $CPAN::Frontend->mywarn("Warning: Prerequisite ". # "'$need_module => $need_version' ". # "for '$selfid' already built ". # "but the result looks suspicious. ". # "Skipping another build attempt, ". # "to prevent looping endlessly.\n" # ); next NEED; } } } } } my $needed_as; if (0) { } elsif (exists $prereq_pm->{requires}{$need_module} || exists $prereq_pm->{opt_requires}{$need_module} ) { $needed_as = "r"; } elsif ($slot eq "configure_requires_later") { # in ae872487d5 we said: C< we have not yet run the # {Build,Makefile}.PL, we must presume "r" >; but the # meta.yml standard says C< These dependencies are not # required after the distribution is installed. >; so now # we change it back to "b" and care for the proper # promotion later. $needed_as = "b"; } else { $needed_as = "b"; } # here need to flag as optional for recommends/suggests # -- xdg, 2012-04-01 $self->debug(sprintf "%s manadory?[%s]", $self->pretty_id, $self->{mandatory}) if $CPAN::DEBUG; my $optional = !$self->{mandatory} || $self->is_locally_optional($prereq_pm, $need_module); push @need, [$need_module,$needed_as,$optional]; } my @unfolded = map { "[".join(",",@$_)."]" } @need; CPAN->debug("returning from unsat_prereq[@unfolded]") if $CPAN::DEBUG; @need; } sub _fulfills_all_version_rqs { my($self,$need_module,$available_file,$available_version,$need_version) = @_; my(@all_requirements) = split /\s*,\s*/, $need_version; local($^W) = 0; my $ok = 0; RQ: for my $rq (@all_requirements) { if ($rq =~ s|>=\s*||) { } elsif ($rq =~ s|>\s*||) { # 2005-12: one user if (CPAN::Version->vgt($available_version,$rq)) { $ok++; } next RQ; } elsif ($rq =~ s|!=\s*||) { # 2005-12: no user if (CPAN::Version->vcmp($available_version,$rq)) { $ok++; next RQ; } else { $ok=0; last RQ; } } elsif ($rq =~ m|<=?\s*|) { # 2005-12: no user $CPAN::Frontend->mywarn("Downgrading not supported (rq[$rq])\n"); $ok++; next RQ; } elsif ($rq =~ s|==\s*||) { # 2009-07: ELLIOTJS/Perl-Critic-1.099_002.tar.gz if (CPAN::Version->vcmp($available_version,$rq)) { $ok=0; last RQ; } else { $ok++; next RQ; } } if (! CPAN::Version->vgt($rq, $available_version)) { $ok++; } CPAN->debug(sprintf("need_module[%s]available_file[%s]". "available_version[%s]rq[%s]ok[%d]", $need_module, $available_file, $available_version, CPAN::Version->readable($rq), $ok, )) if $CPAN::DEBUG; } my $ret = $ok == @all_requirements; CPAN->debug(sprintf("need_module[%s]ok[%s]all_requirements[%d]",$need_module, $ok, scalar @all_requirements)) if $CPAN::DEBUG; return $ret; } #-> sub CPAN::Distribution::read_meta # read any sort of meta files, return CPAN::Meta object if no errors sub read_meta { my($self) = @_; my $meta_file = $self->pick_meta_file or return; return unless $CPAN::META->has_usable("CPAN::Meta"); my $meta = eval { CPAN::Meta->load_file($meta_file)} or return; # Very old EU::MM could have wrong META if ($meta_file eq 'META.yml' && $meta->generated_by =~ /ExtUtils::MakeMaker version ([\d\._]+)/ ) { my $eummv = do { local $^W = 0; $1+0; }; return if $eummv < 6.2501; } return $meta; } #-> sub CPAN::Distribution::read_yaml ; # XXX This should be DEPRECATED -- dagolden, 2011-02-05 sub read_yaml { my($self) = @_; my $meta_file = $self->pick_meta_file('\.yml$'); $self->debug("meta_file[$meta_file]") if $CPAN::DEBUG; return unless $meta_file; my $yaml; eval { $yaml = $self->parse_meta_yml($meta_file) }; if ($@ or ! $yaml) { return undef; # if we die, then we cannot read YAML's own META.yml } # not "authoritative" if (defined $yaml && (! ref $yaml || ref $yaml ne "HASH")) { $CPAN::Frontend->mywarn("META.yml does not seem to be conforming, cannot use it.\n"); $yaml = undef; } $self->debug(sprintf "yaml[%s]", $yaml || "UNDEF") if $CPAN::DEBUG; $self->debug($yaml) if $CPAN::DEBUG && $yaml; # MYMETA.yml is static and authoritative by definition if ( $meta_file =~ /MYMETA\.yml/ ) { return $yaml; } # META.yml is authoritative only if dynamic_config is defined and false if ( defined $yaml->{dynamic_config} && ! $yaml->{dynamic_config} ) { return $yaml; } # otherwise, we can't use what we found return undef; } #-> sub CPAN::Distribution::configure_requires ; sub configure_requires { my($self) = @_; return unless my $meta_file = $self->pick_meta_file('^META'); if (my $meta_obj = $self->read_meta) { my $prereqs = $meta_obj->effective_prereqs; my $cr = $prereqs->requirements_for(qw/configure requires/); return $cr ? $cr->as_string_hash : undef; } else { my $yaml = eval { $self->parse_meta_yml($meta_file) }; return $yaml->{configure_requires}; } } #-> sub CPAN::Distribution::prereq_pm ; sub prereq_pm { my($self) = @_; return unless $self->{writemakefile} # no need to have succeeded # but we must have run it || $self->{modulebuild}; unless ($self->{build_dir}) { return; } # no Makefile/Build means configuration aborted, so don't look for prereqs my $makefile = File::Spec->catfile($self->{build_dir}, $^O eq 'VMS' ? 'descrip.mms' : 'Makefile'); my $buildfile = File::Spec->catfile($self->{build_dir}, $^O eq 'VMS' ? 'Build.com' : 'Build'); return unless -f $makefile || -f $buildfile; CPAN->debug(sprintf "writemakefile[%s]modulebuild[%s]", $self->{writemakefile}||"", $self->{modulebuild}||"", ) if $CPAN::DEBUG; my($req,$breq, $opt_req, $opt_breq); my $meta_obj = $self->read_meta; # META/MYMETA is only authoritative if dynamic_config is false if ($meta_obj && ! $meta_obj->dynamic_config) { my $prereqs = $meta_obj->effective_prereqs; my $requires = $prereqs->requirements_for(qw/runtime requires/); my $build_requires = $prereqs->requirements_for(qw/build requires/); my $test_requires = $prereqs->requirements_for(qw/test requires/); # XXX we don't yet distinguish build vs test, so merge them for now $build_requires->add_requirements($test_requires); $req = $requires->as_string_hash; $breq = $build_requires->as_string_hash; # XXX assemble optional_req && optional_breq from recommends/suggests # depending on corresponding policies -- xdg, 2012-04-01 CPAN->use_inst("CPAN::Meta::Requirements"); my $opt_runtime = CPAN::Meta::Requirements->new; my $opt_build = CPAN::Meta::Requirements->new; if ( $CPAN::Config->{recommends_policy} ) { $opt_runtime->add_requirements( $prereqs->requirements_for(qw/runtime recommends/)); $opt_build->add_requirements( $prereqs->requirements_for(qw/build recommends/)); $opt_build->add_requirements( $prereqs->requirements_for(qw/test recommends/)); } if ( $CPAN::Config->{suggests_policy} ) { $opt_runtime->add_requirements( $prereqs->requirements_for(qw/runtime suggests/)); $opt_build->add_requirements( $prereqs->requirements_for(qw/build suggests/)); $opt_build->add_requirements( $prereqs->requirements_for(qw/test suggests/)); } $opt_req = $opt_runtime->as_string_hash; $opt_breq = $opt_build->as_string_hash; } elsif (my $yaml = $self->read_yaml) { # often dynamic_config prevents a result here $req = $yaml->{requires} || {}; $breq = $yaml->{build_requires} || {}; if ( $CPAN::Config->{recommends_policy} ) { $opt_req = $yaml->{recommends} || {}; } undef $req unless ref $req eq "HASH" && %$req; if ($req) { if ($yaml->{generated_by} && $yaml->{generated_by} =~ /ExtUtils::MakeMaker version ([\d\._]+)/) { my $eummv = do { local $^W = 0; $1+0; }; if ($eummv < 6.2501) { # thanks to Slaven for digging that out: MM before # that could be wrong because it could reflect a # previous release undef $req; } } my $areq; my $do_replace; foreach my $k (sort keys %{$req||{}}) { my $v = $req->{$k}; next unless defined $v; if ($v =~ /\d/) { $areq->{$k} = $v; } elsif ($k =~ /[A-Za-z]/ && $v =~ /[A-Za-z]/ && $CPAN::META->exists("CPAN::Module",$v) ) { $CPAN::Frontend->mywarn("Suspicious key-value pair in META.yml's ". "requires hash: $k => $v; I'll take both ". "key and value as a module name\n"); $CPAN::Frontend->mysleep(1); $areq->{$k} = 0; $areq->{$v} = 0; $do_replace++; } } $req = $areq if $do_replace; } } else { $CPAN::Frontend->mywarnonce("Could not read metadata file. Falling back to other ". "methods to determine prerequisites\n"); } unless ($req || $breq) { my $build_dir; unless ( $build_dir = $self->{build_dir} ) { return; } my $makefile = File::Spec->catfile($build_dir,"Makefile"); my $fh; if (-f $makefile and $fh = FileHandle->new("<$makefile\0")) { CPAN->debug("Getting prereq from Makefile") if $CPAN::DEBUG; local($/) = "\n"; while (<$fh>) { last if /MakeMaker post_initialize section/; my($p) = m{^[\#] \s+PREREQ_PM\s+=>\s+(.+) }x; next unless $p; # warn "Found prereq expr[$p]"; # Regexp modified by A.Speer to remember actual version of file # PREREQ_PM hash key wants, then add to while ( $p =~ m/(?:\s)([\w\:]+)=>(q\[.*?\]|undef),?/g ) { my($m,$n) = ($1,$2); # When a prereq is mentioned twice: let the bigger # win; usual culprit is that they declared # build_requires separately from requires; see # rt.cpan.org #47774 my($prevn); if ( defined $req->{$m} ) { $prevn = $req->{$m}; } if ($n =~ /^q\[(.*?)\]$/) { $n = $1; } if (!$prevn || CPAN::Version->vlt($prevn, $n)){ $req->{$m} = $n; } } last; } } } unless ($req || $breq) { my $build_dir = $self->{build_dir} or die "Panic: no build_dir?"; my $buildfile = File::Spec->catfile($build_dir,"Build"); if (-f $buildfile) { CPAN->debug("Found '$buildfile'") if $CPAN::DEBUG; my $build_prereqs = File::Spec->catfile($build_dir,"_build","prereqs"); if (-f $build_prereqs) { CPAN->debug("Getting prerequisites from '$build_prereqs'") if $CPAN::DEBUG; my $content = do { local *FH; open FH, $build_prereqs or $CPAN::Frontend->mydie("Could not open ". "'$build_prereqs': $!"); local $/; ; }; my $bphash = eval $content; if ($@) { } else { $req = $bphash->{requires} || +{}; $breq = $bphash->{build_requires} || +{}; } } } } # XXX needs to be adapted for optional_req & optional_breq -- xdg, 2012-04-01 if ($req || $breq || $opt_req || $opt_breq ) { return $self->{prereq_pm} = { requires => $req, build_requires => $breq, opt_requires => $opt_req, opt_build_requires => $opt_breq, }; } } #-> sub CPAN::Distribution::shortcut_test ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_test { my ($self) = @_; $self->debug("checking badtestcnt[$self->{ID}]") if $CPAN::DEBUG; $self->{badtestcnt} ||= 0; if ($self->{badtestcnt} > 0) { require Data::Dumper; CPAN->debug(sprintf "NOREPEAT[%s]", Data::Dumper::Dumper($self)) if $CPAN::DEBUG; return $self->goodbye("Won't repeat unsuccessful test during this command"); } for my $slot ( qw/later configure_requires_later/ ) { $self->debug("checking $slot slot[$self->{ID}]") if $CPAN::DEBUG; return $self->success($self->{$slot}) if $self->{$slot}; } $self->debug("checking if tests passed[$self->{ID}]") if $CPAN::DEBUG; if ( $self->{make_test} ) { if ( UNIVERSAL::can($self->{make_test},"failed") ? $self->{make_test}->failed : $self->{make_test} =~ /^NO/ ) { if ( UNIVERSAL::can($self->{make_test},"commandid") && $self->{make_test}->commandid == $CPAN::CurrentCommandId ) { return $self->goodbye("Has already been tested within this command"); } } else { # if global "is_tested" has been cleared, we need to mark this to # be added to PERL5LIB if not already installed if ($self->tested_ok_but_not_installed) { $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); } return $self->success("Has already been tested successfully"); } } if ($self->{notest}) { $self->{make_test} = CPAN::Distrostatus->new("YES"); return $self->success("Skipping test because of notest pragma"); } return undef; # no shortcut } #-> sub CPAN::Distribution::_exe_files ; sub _exe_files { my($self) = @_; return unless $self->{writemakefile} # no need to have succeeded # but we must have run it || $self->{modulebuild}; unless ($self->{build_dir}) { return; } CPAN->debug(sprintf "writemakefile[%s]modulebuild[%s]", $self->{writemakefile}||"", $self->{modulebuild}||"", ) if $CPAN::DEBUG; my $build_dir; unless ( $build_dir = $self->{build_dir} ) { return; } my $makefile = File::Spec->catfile($build_dir,"Makefile"); my $fh; my @exe_files; if (-f $makefile and $fh = FileHandle->new("<$makefile\0")) { CPAN->debug("Getting exefiles from Makefile") if $CPAN::DEBUG; local($/) = "\n"; while (<$fh>) { last if /MakeMaker post_initialize section/; my($p) = m{^[\#] \s+EXE_FILES\s+=>\s+\[(.+)\] }x; next unless $p; # warn "Found exefiles expr[$p]"; my @p = split /,\s*/, $p; for my $p2 (@p) { if ($p2 =~ /^q\[(.+)\]/) { push @exe_files, $1; } } } } return \@exe_files if @exe_files; my $buildparams = File::Spec->catfile($build_dir,"_build","build_params"); if (-f $buildparams) { CPAN->debug("Found '$buildparams'") if $CPAN::DEBUG; my $x = do $buildparams; for my $sf ($x->[2]{script_files}) { if (my $reftype = ref $sf) { if ($reftype eq "ARRAY") { push @exe_files, @$sf; } elsif ($reftype eq "HASH") { push @exe_files, keys %$sf; } else { $CPAN::Frontend->mywarn("Invalid reftype $reftype for Build.PL 'script_files'\n"); } } elsif (defined $sf) { push @exe_files, $sf; } } } return \@exe_files; } #-> sub CPAN::Distribution::test ; sub test { my($self) = @_; $self->pre_test(); if (exists $self->{cleanup_after_install_done}) { $self->post_test(); return $self->make; } $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; if (my $goto = $self->prefs->{goto}) { $self->post_test(); return $self->goto($goto); } unless ($self->make){ $self->post_test(); return; } if ( defined( my $sc = $self->shortcut_test ) ) { $self->post_test(); return $sc; } if ($CPAN::Signal) { delete $self->{force_update}; $self->post_test(); return; } # warn "XDEBUG: checking for notest: $self->{notest} $self"; my $make = $self->{modulebuild} ? "Build" : "make"; local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # test $CPAN::META->set_perl5lib; local $ENV{MAKEFLAGS}; # protect us from outer make calls local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; if ($run_allow_installing_within_test) { my($allow_installing, $why) = $self->_allow_installing; if (! $allow_installing) { $CPAN::Frontend->mywarn("Testing/Installation stopped: $why\n"); $self->introduce_myself; $self->{make_test} = CPAN::Distrostatus->new("NO -- testing/installation stopped due $why"); $CPAN::Frontend->mywarn(" [testing] -- NOT OK\n"); delete $self->{force_update}; $self->post_test(); return; } } $CPAN::Frontend->myprint(sprintf "Running %s test for %s\n", $make, $self->pretty_id); my $builddir = $self->dir or $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); $self->post_test(); return; } $self->debug("Changed directory to $self->{build_dir}") if $CPAN::DEBUG; if ($^O eq 'MacOS') { Mac::BuildTools::make_test($self); $self->post_test(); return; } if ($self->{modulebuild}) { my $thm = CPAN::Shell->expand("Module","Test::Harness"); my $v = $thm->inst_version; if (CPAN::Version->vlt($v,2.62)) { # XXX Eric Wilhelm reported this as a bug: klapperl: # Test::Harness 3.0 self-tests, so that should be 'unless # installing Test::Harness' unless ($self->id eq $thm->distribution->id) { $CPAN::Frontend->mywarn(qq{The version of your Test::Harness is only '$v', you need at least '2.62'. Please upgrade your Test::Harness.\n}); $self->{make_test} = CPAN::Distrostatus->new("NO Test::Harness too old"); $self->post_test(); return; } } } if ( ! $self->{force_update} ) { # bypass actual tests if "trust_test_report_history" and have a report my $have_tested_fcn; if ( $CPAN::Config->{trust_test_report_history} && $CPAN::META->has_inst("CPAN::Reporter::History") && ( $have_tested_fcn = CPAN::Reporter::History->can("have_tested" ))) { if ( my @reports = $have_tested_fcn->( dist => $self->base_id ) ) { # Do nothing if grade was DISCARD if ( $reports[-1]->{grade} =~ /^(?:PASS|UNKNOWN)$/ ) { $self->{make_test} = CPAN::Distrostatus->new("YES"); # if global "is_tested" has been cleared, we need to mark this to # be added to PERL5LIB if not already installed if ($self->tested_ok_but_not_installed) { $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); } $CPAN::Frontend->myprint("Found prior test report -- OK\n"); $self->post_test(); return; } elsif ( $reports[-1]->{grade} =~ /^(?:FAIL|NA)$/ ) { $self->{make_test} = CPAN::Distrostatus->new("NO"); $self->{badtestcnt}++; $CPAN::Frontend->mywarn("Found prior test report -- NOT OK\n"); $self->post_test(); return; } } } } my $system; my $prefs_test = $self->prefs->{test}; if (my $commandline = exists $prefs_test->{commandline} ? $prefs_test->{commandline} : "") { $system = $commandline; $ENV{PERL} = CPAN::find_perl(); } elsif ($self->{modulebuild}) { $system = sprintf "%s test", $self->_build_command(); unless (-e "Build" || ($^O eq 'VMS' && -e "Build.com")) { my $id = $self->pretty_id; $CPAN::Frontend->mywarn("Alert: no 'Build' file found while trying to test '$id'"); } } else { $system = join " ", $self->_make_command(), "test"; } my $make_test_arg = $self->_make_phase_arg("test"); $system = sprintf("%s%s", $system, $make_test_arg ? " $make_test_arg" : "", ); my($tests_ok); my $test_env; if ($self->prefs->{test}) { $test_env = $self->prefs->{test}{env}; } local @ENV{keys %$test_env} = values %$test_env if $test_env; my $expect_model = $self->_prefs_with_expect("test"); my $want_expect = 0; if ( $expect_model && @{$expect_model->{talk}} ) { my $can_expect = $CPAN::META->has_inst("Expect"); if ($can_expect) { $want_expect = 1; } else { $CPAN::Frontend->mywarn("Expect not installed, falling back to ". "testing without\n"); } } FORK: { my $pid = fork; if (! defined $pid) { # contention warn "Contention '$!', sleeping 2"; sleep 2; redo FORK; } elsif ($pid) { # parent if ($^O eq "MSWin32") { wait; } else { SUPERVISE: while (waitpid($pid, WNOHANG) <= 0) { if ($CPAN::Signal) { kill 9, -$pid; } sleep 1; } } $tests_ok = !$?; } else { # child POSIX::setsid() unless $^O eq "MSWin32"; my $c_ok; $|=1; if ($want_expect) { if ($self->_should_report('test')) { $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is currently ". "not supported when distroprefs specify ". "an interactive test\n"); } $c_ok = $self->_run_via_expect($system,'test',$expect_model) == 0; } elsif ( $self->_should_report('test') ) { $c_ok = CPAN::Reporter::test($self, $system); } else { $c_ok = system($system) == 0; } exit !$c_ok; } } # FORK $self->introduce_myself; my $but = $self->_make_test_illuminate_prereqs(); if ( $tests_ok ) { if ($but) { $CPAN::Frontend->mywarn("Tests succeeded but $but\n"); $self->{make_test} = CPAN::Distrostatus->new("NO $but"); $self->store_persistent_state; $self->post_test(); return $self->goodbye("[dependencies] -- NA"); } $CPAN::Frontend->myprint(" $system -- OK\n"); $self->{make_test} = CPAN::Distrostatus->new("YES"); $CPAN::META->is_tested($self->{build_dir},$self->{make_test}{TIME}); # probably impossible to need the next line because badtestcnt # has a lifespan of one command delete $self->{badtestcnt}; } else { if ($but) { $but .= "; additionally test harness failed"; $CPAN::Frontend->mywarn("$but\n"); $self->{make_test} = CPAN::Distrostatus->new("NO $but"); } elsif ( $self->{force_update} ) { $self->{make_test} = CPAN::Distrostatus->new( "NO but failure ignored because 'force' in effect" ); } elsif ($CPAN::Signal) { $self->{make_test} = CPAN::Distrostatus->new("NO -- Interrupted"); } else { $self->{make_test} = CPAN::Distrostatus->new("NO"); } $self->{badtestcnt}++; $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); CPAN::Shell->optprint ("hint", sprintf ("//hint// to see the cpan-testers results for installing this module, try: reports %s\n", $self->pretty_id)); } $self->store_persistent_state; $self->post_test(); return $self->{force_update} ? 1 : !! $tests_ok; } sub _make_test_illuminate_prereqs { my($self) = @_; my @prereq; # local $CPAN::DEBUG = 16; # Distribution for my $m (sort keys %{$self->{sponsored_mods}}) { next unless $self->{sponsored_mods}{$m} > 0; my $m_obj = CPAN::Shell->expand("Module",$m) or next; # XXX we need available_version which reflects # $ENV{PERL5LIB} so that already tested but not yet # installed modules are counted. my $available_version = $m_obj->available_version; my $available_file = $m_obj->available_file; if ($available_version && !CPAN::Version->vlt($available_version,$self->{prereq_pm}{$m}) ) { CPAN->debug("m[$m] good enough available_version[$available_version]") if $CPAN::DEBUG; } elsif ($available_file && ( !$self->{prereq_pm}{$m} || $self->{prereq_pm}{$m} == 0 ) ) { # lex Class::Accessor::Chained::Fast which has no $VERSION CPAN->debug("m[$m] have available_file[$available_file]") if $CPAN::DEBUG; } else { push @prereq, $m unless $self->is_locally_optional(undef, $m); } } my $but; if (@prereq) { my $cnt = @prereq; my $which = join ",", @prereq; $but = $cnt == 1 ? "one dependency not OK ($which)" : "$cnt dependencies missing ($which)"; } $but; } sub _prefs_with_expect { my($self,$where) = @_; return unless my $prefs = $self->prefs; return unless my $where_prefs = $prefs->{$where}; if ($where_prefs->{expect}) { return { mode => "deterministic", timeout => 15, talk => $where_prefs->{expect}, }; } elsif ($where_prefs->{"eexpect"}) { return $where_prefs->{"eexpect"}; } return; } #-> sub CPAN::Distribution::clean ; sub clean { my($self) = @_; my $make = $self->{modulebuild} ? "Build" : "make"; $CPAN::Frontend->myprint(sprintf "Running %s clean for %s\n", $make, $self->pretty_id); unless (exists $self->{archived}) { $CPAN::Frontend->mywarn("Distribution seems to have never been unzipped". "/untarred, nothing done\n"); return 1; } unless (exists $self->{build_dir}) { $CPAN::Frontend->mywarn("Distribution has no own directory, nothing to do.\n"); return 1; } if (exists $self->{writemakefile} and $self->{writemakefile}->failed ) { $CPAN::Frontend->mywarn("No Makefile, don't know how to 'make clean'\n"); return 1; } EXCUSE: { my @e; exists $self->{make_clean} and $self->{make_clean} eq "YES" and push @e, "make clean already called once"; $CPAN::Frontend->myprint(join "", map {" $_\n"} @e) and return if @e; } chdir "$self->{build_dir}" or Carp::confess("Couldn't chdir to $self->{build_dir}: $!"); $self->debug("Changed directory to $self->{build_dir}") if $CPAN::DEBUG; if ($^O eq 'MacOS') { Mac::BuildTools::make_clean($self); return; } my $system; if ($self->{modulebuild}) { unless (-f "Build") { my $cwd = CPAN::anycwd(); $CPAN::Frontend->mywarn("Alert: no Build file available for 'clean $self->{id}". " in cwd[$cwd]. Danger, Will Robinson!"); $CPAN::Frontend->mysleep(5); } $system = sprintf "%s clean", $self->_build_command(); } else { $system = join " ", $self->_make_command(), "clean"; } my $system_ok = system($system) == 0; $self->introduce_myself; if ( $system_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); # $self->force; # Jost Krieger pointed out that this "force" was wrong because # it has the effect that the next "install" on this distribution # will untar everything again. Instead we should bring the # object's state back to where it is after untarring. for my $k (qw( force_update install writemakefile make make_test )) { delete $self->{$k}; } $self->{make_clean} = CPAN::Distrostatus->new("YES"); } else { # Hmmm, what to do if make clean failed? $self->{make_clean} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(qq{ $system -- NOT OK\n}); # 2006-02-27: seems silly to me to force a make now # $self->force("make"); # so that this directory won't be used again } $self->store_persistent_state; } #-> sub CPAN::Distribution::check_disabled ; sub check_disabled { my ($self) = @_; $self->debug("checking disabled id[$self->{ID}]") if $CPAN::DEBUG; if ($self->prefs->{disabled} && ! $self->{force_update}) { return sprintf( "Disabled via prefs file '%s' doc %d", $self->{prefs_file}, $self->{prefs_file_doc}, ); } return; } #-> sub CPAN::Distribution::goto ; sub goto { my($self,$goto) = @_; $goto = $self->normalize($goto); my $why = sprintf( "Goto '$goto' via prefs file '%s' doc %d", $self->{prefs_file}, $self->{prefs_file_doc}, ); $self->{unwrapped} = CPAN::Distrostatus->new("NO $why"); # 2007-07-16 akoenig : Better than NA would be if we could inherit # the status of the $goto distro but given the exceptional nature # of 'goto' I feel reluctant to implement it my $goodbye_message = "[goto] -- NA $why"; $self->goodbye($goodbye_message); # inject into the queue CPAN::Queue->delete($self->id); CPAN::Queue->jumpqueue({qmod => $goto, reqtype => $self->{reqtype}}); # and run where we left off my($method) = (caller(1))[3]; my $goto_do = CPAN->instance("CPAN::Distribution",$goto); $goto_do->called_for($self->called_for) unless $goto_do->called_for; $goto_do->{mandatory} ||= $self->{mandatory}; $goto_do->{reqtype} ||= $self->{reqtype}; $goto_do->{coming_from} = $self->pretty_id; $goto_do->$method(); CPAN::Queue->delete_first($goto); # XXX delete_first returns undef; is that what this should return # up the call stack, eg. return $sefl->goto($goto) -- xdg, 2012-04-04 } #-> sub CPAN::Distribution::shortcut_install ; # return values: undef means don't shortcut; 0 means shortcut as fail; # and 1 means shortcut as success sub shortcut_install { my ($self) = @_; $self->debug("checking previous install results[$self->{ID}]") if $CPAN::DEBUG; if (exists $self->{install}) { my $text = UNIVERSAL::can($self->{install},"text") ? $self->{install}->text : $self->{install}; if ($text =~ /^YES/) { $CPAN::META->is_installed($self->{build_dir}); return $self->success("Already done"); } elsif ($text =~ /is only/) { # e.g. 'is only build_requires': may be overruled later return $self->goodbye($text); } else { # comment in Todo on 2006-02-11; maybe retry? return $self->goodbye("Already tried without success"); } } for my $slot ( qw/later configure_requires_later/ ) { return $self->success($self->{$slot}) if $self->{$slot}; } return undef; } #-> sub CPAN::Distribution::is_being_sponsored ; # returns true if we find a distro object in the queue that has # sponsored this one sub is_being_sponsored { my($self) = @_; my $iterator = CPAN::Queue->iterator; QITEM: while (my $q = $iterator->()) { my $s = $q->as_string; my $obj = CPAN::Shell->expandany($s) or next QITEM; my $type = ref $obj; if ( $type eq 'CPAN::Distribution' ){ for my $module (sort keys %{$obj->{sponsored_mods} || {}}) { return 1 if grep { $_ eq $module } $self->containsmods; } } } return 0; } #-> sub CPAN::Distribution::install ; sub install { my($self) = @_; $self->pre_install(); if (exists $self->{cleanup_after_install_done}) { return $self->test; } $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG; if (my $goto = $self->prefs->{goto}) { $self->goto($goto); $self->post_install(); return; } unless ($self->test) { $self->post_install(); return; } if ( defined( my $sc = $self->shortcut_install ) ) { $self->post_install(); return $sc; } if ($CPAN::Signal) { delete $self->{force_update}; $self->post_install(); return; } my $builddir = $self->dir or $CPAN::Frontend->mydie("PANIC: Cannot determine build directory\n"); unless (chdir $builddir) { $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!"); $self->post_install(); return; } $self->debug("Changed directory to $self->{build_dir}") if $CPAN::DEBUG; my $make = $self->{modulebuild} ? "Build" : "make"; $CPAN::Frontend->myprint(sprintf "Running %s install for %s\n", $make, $self->pretty_id); if ($^O eq 'MacOS') { Mac::BuildTools::make_install($self); $self->post_install(); return; } my $system; if (my $commandline = $self->prefs->{install}{commandline}) { $system = $commandline; $ENV{PERL} = CPAN::find_perl(); } elsif ($self->{modulebuild}) { my($mbuild_install_build_command) = exists $CPAN::HandleConfig::keys{mbuild_install_build_command} && $CPAN::Config->{mbuild_install_build_command} ? $CPAN::Config->{mbuild_install_build_command} : $self->_build_command(); my $install_directive = $^O eq 'VMS' ? '"install"' : 'install'; $system = sprintf("%s %s %s", $mbuild_install_build_command, $install_directive, $CPAN::Config->{mbuild_install_arg}, ); } else { my($make_install_make_command) = $self->_make_install_make_command(); $system = sprintf("%s install %s", $make_install_make_command, $CPAN::Config->{make_install_arg}, ); } my($stderr) = $^O eq "MSWin32" || $^O eq 'VMS' ? "" : " 2>&1 "; my $brip = CPAN::HandleConfig->prefs_lookup($self, q{build_requires_install_policy}); $brip ||="ask/yes"; my $id = $self->id; my $reqtype = $self->{reqtype} ||= "c"; # in doubt it was a command my $want_install = "yes"; if ($reqtype eq "b") { if ($brip eq "no") { $want_install = "no"; } elsif ($brip =~ m|^ask/(.+)|) { my $default = $1; $default = "yes" unless $default =~ /^(y|n)/i; $want_install = CPAN::Shell::colorable_makemaker_prompt ("$id is just needed temporarily during building or testing. ". "Do you want to install it permanently?", $default); } } unless ($want_install =~ /^y/i) { my $is_only = "is only 'build_requires'"; $self->{install} = CPAN::Distrostatus->new("NO -- $is_only"); delete $self->{force_update}; $self->goodbye("Not installing because $is_only"); $self->post_install(); return; } local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) ? $ENV{PERL5LIB} : ($ENV{PERLLIB} || ""); local $ENV{PERL5OPT} = defined $ENV{PERL5OPT} ? $ENV{PERL5OPT} : ""; local $ENV{PERL_USE_UNSAFE_INC} = exists $ENV{PERL_USE_UNSAFE_INC} && defined $ENV{PERL_USE_UNSAFE_INC} ? $ENV{PERL_USE_UNSAFE_INC} : 1; # install $CPAN::META->set_perl5lib; local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default}; local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default}; my $install_env; if ($self->prefs->{install}) { $install_env = $self->prefs->{install}{env}; } local @ENV{keys %$install_env} = values %$install_env if $install_env; if (! $run_allow_installing_within_test) { my($allow_installing, $why) = $self->_allow_installing; if (! $allow_installing) { $CPAN::Frontend->mywarn("Installation stopped: $why\n"); $self->introduce_myself; $self->{install} = CPAN::Distrostatus->new("NO -- installation stopped due $why"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); delete $self->{force_update}; $self->post_install(); return; } } my($pipe) = FileHandle->new("$system $stderr |"); unless ($pipe) { $CPAN::Frontend->mywarn("Can't execute $system: $!"); $self->introduce_myself; $self->{install} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); delete $self->{force_update}; $self->post_install(); return; } my($makeout) = ""; while (<$pipe>) { print $_; # intentionally NOT use Frontend->myprint because it # looks irritating when we markup in color what we # just pass through from an external program $makeout .= $_; } $pipe->close; my $close_ok = $? == 0; $self->introduce_myself; if ( $close_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); $CPAN::META->is_installed($self->{build_dir}); $self->{install} = CPAN::Distrostatus->new("YES"); if ($CPAN::Config->{'cleanup_after_install'} && ! $self->is_dot_dist && ! $self->is_being_sponsored) { my $parent = File::Spec->catdir( $self->{build_dir}, File::Spec->updir ); chdir $parent or $CPAN::Frontend->mydie("Couldn't chdir to $parent: $!\n"); File::Path::rmtree($self->{build_dir}); my $yml = "$self->{build_dir}.yml"; if (-e $yml) { unlink $yml or $CPAN::Frontend->mydie("Couldn't unlink $yml: $!\n"); } $self->{cleanup_after_install_done}=1; } } else { $self->{install} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); my $mimc = CPAN::HandleConfig->prefs_lookup($self, q{make_install_make_command}); if ( $makeout =~ /permission/s && $> > 0 && ( ! $mimc || $mimc eq (CPAN::HandleConfig->prefs_lookup($self, q{make})) ) ) { $CPAN::Frontend->myprint( qq{----\n}. qq{ You may have to su }. qq{to root to install the package\n}. qq{ (Or you may want to run something like\n}. qq{ o conf make_install_make_command 'sudo make'\n}. qq{ to raise your permissions.} ); } } delete $self->{force_update}; unless ($CPAN::Config->{'cleanup_after_install'}) { $self->store_persistent_state; } $self->post_install(); return !! $close_ok; } sub blib_pm_walk { my @queue = grep { -e $_ } File::Spec->catdir("blib","lib"), File::Spec->catdir("blib","arch"); return sub { LOOP: { if (@queue) { my $file = shift @queue; if (-d $file) { my $dh; opendir $dh, $file or next; my @newfiles = map { my @ret; my $maybedir = File::Spec->catdir($file, $_); if (-d $maybedir) { unless (File::Spec->catdir("blib","arch","auto") eq $maybedir) { # prune the blib/arch/auto directory, no pm files there @ret = $maybedir; } } elsif (/\.pm$/) { my $mustbefile = File::Spec->catfile($file, $_); if (-f $mustbefile) { @ret = $mustbefile; } } @ret; } grep { $_ ne "." && $_ ne ".." } readdir $dh; push @queue, @newfiles; redo LOOP; } else { return $file; } } else { return; } } }; } sub _allow_installing { my($self) = @_; my $id = my $pretty_id = $self->pretty_id; if ($self->{CALLED_FOR}) { $id .= " (called for $self->{CALLED_FOR})"; } my $allow_down = CPAN::HandleConfig->prefs_lookup($self,q{allow_installing_module_downgrades}); $allow_down ||= "ask/yes"; my $allow_outdd = CPAN::HandleConfig->prefs_lookup($self,q{allow_installing_outdated_dists}); $allow_outdd ||= "ask/yes"; return 1 if $allow_down eq "yes" && $allow_outdd eq "yes"; if (($allow_outdd ne "yes") && ! $CPAN::META->has_inst('CPAN::DistnameInfo')) { return 1 if grep { $_ eq 'CPAN::DistnameInfo'} $self->containsmods; if ($allow_outdd ne "yes") { $CPAN::Frontend->mywarn("The current configuration of allow_installing_outdated_dists is '$allow_outdd', but for this option we would need 'CPAN::DistnameInfo' installed. Please install 'CPAN::DistnameInfo' as soon as possible. As long as we are not equipped with 'CPAN::DistnameInfo' this option does not take effect\n"); $allow_outdd = "yes"; } } return 1 if $allow_down eq "yes" && $allow_outdd eq "yes"; my($dist_version, $dist_dist); if ($allow_outdd ne "yes"){ my $dni = CPAN::DistnameInfo->new($pretty_id); $dist_version = $dni->version; $dist_dist = $dni->dist; } my $iterator = blib_pm_walk(); my(@down,@outdd); while (my $file = $iterator->()) { my $version = CPAN::Module->parse_version($file); my($volume, $directories, $pmfile) = File::Spec->splitpath( $file ); my @dirs = File::Spec->splitdir( $directories ); my(@blib_plus1) = splice @dirs, 0, 2; my($pmpath) = File::Spec->catfile(grep { length($_) } @dirs, $pmfile); unless ($allow_down eq "yes") { if (my $inst_file = $self->_file_in_path($pmpath, \@INC)) { my $inst_version = CPAN::Module->parse_version($inst_file); my $cmp = CPAN::Version->vcmp($version, $inst_version); if ($cmp) { if ($cmp < 0) { push @down, { pmpath => $pmpath, version => $version, inst_version => $inst_version }; } } if (@down) { my $why = "allow_installing_module_downgrades: $id contains downgrading module(s) (e.g. '$down[0]{pmpath}' would downgrade installed '$down[0]{inst_version}' to '$down[0]{version}')"; if (my($default) = $allow_down =~ m|^ask/(.+)|) { $default = "yes" unless $default =~ /^(y|n)/i; my $answer = CPAN::Shell::colorable_makemaker_prompt ("$why. Do you want to allow installing it?", $default, "colorize_warn"); $allow_down = $answer =~ /^\s*y/i ? "yes" : "no"; } if ($allow_down eq "no") { return (0, $why); } } } } unless ($allow_outdd eq "yes") { my @pmpath = (@dirs, $pmfile); $pmpath[-1] =~ s/\.pm$//; my $mo = CPAN::Shell->expand("Module",join "::", grep { length($_) } @pmpath); if ($mo) { my $cpan_version = $mo->cpan_version; my $is_lower = CPAN::Version->vlt($version, $cpan_version); my $other_dist; if (my $mo_dist = $mo->distribution) { $other_dist = $mo_dist->pretty_id; my $dni = CPAN::DistnameInfo->new($other_dist); if ($dni->dist eq $dist_dist){ if (CPAN::Version->vgt($dni->version, $dist_version)) { push @outdd, { pmpath => $pmpath, cpan_path => $dni->pathname, dist_version => $dni->version, dist_dist => $dni->dist, }; } } } } if (@outdd && $allow_outdd ne "yes") { my $why = "allow_installing_outdated_dists: $id contains module(s) that are indexed on the CPAN with a different distro: (e.g. '$outdd[0]{pmpath}' is indexed with '$outdd[0]{cpan_path}')"; if ($outdd[0]{dist_dist} eq $dist_dist) { $why .= ", and this has a higher distribution-version, i.e. version '$outdd[0]{dist_version}' is higher than '$dist_version')"; } if (my($default) = $allow_outdd =~ m|^ask/(.+)|) { $default = "yes" unless $default =~ /^(y|n)/i; my $answer = CPAN::Shell::colorable_makemaker_prompt ("$why. Do you want to allow installing it?", $default, "colorize_warn"); $allow_outdd = $answer =~ /^\s*y/i ? "yes" : "no"; } if ($allow_outdd eq "no") { return (0, $why); } } } } return 1; } sub _file_in_path { # similar to CPAN::Module::_file_in_path my($self,$pmpath,$incpath) = @_; my($dir,@packpath); foreach $dir (@$incpath) { my $pmfile = File::Spec->catfile($dir,$pmpath); if (-f $pmfile) { return $pmfile; } } return; } sub introduce_myself { my($self) = @_; $CPAN::Frontend->myprint(sprintf(" %s\n",$self->pretty_id)); } #-> sub CPAN::Distribution::dir ; sub dir { shift->{build_dir}; } #-> sub CPAN::Distribution::perldoc ; sub perldoc { my($self) = @_; my($dist) = $self->id; my $package = $self->called_for; if ($CPAN::META->has_inst("Pod::Perldocs")) { my($perl) = $self->perl or $CPAN::Frontend->mydie("Couldn't find executable perl\n"); my @args = ($perl, q{-MPod::Perldocs}, q{-e}, q{Pod::Perldocs->run()}, $package); my($wstatus); unless ( ($wstatus = system(@args)) == 0 ) { my $estatus = $wstatus >> 8; $CPAN::Frontend->myprint(qq{ Function system("@args") returned status $estatus (wstat $wstatus) }); } } else { $self->_display_url( $CPAN::Defaultdocs . $package ); } } #-> sub CPAN::Distribution::_check_binary ; sub _check_binary { my ($dist,$shell,$binary) = @_; my ($pid,$out); $CPAN::Frontend->myprint(qq{ + _check_binary($binary)\n}) if $CPAN::DEBUG; if ($CPAN::META->has_inst("File::Which")) { return File::Which::which($binary); } else { local *README; $pid = open README, "which $binary|" or $CPAN::Frontend->mywarn(qq{Could not fork 'which $binary': $!\n}); return unless $pid; while () { $out .= $_; } close README or $CPAN::Frontend->mywarn("Could not run 'which $binary': $!\n") and return; } $CPAN::Frontend->myprint(qq{ + $out \n}) if $CPAN::DEBUG && $out; return $out; } #-> sub CPAN::Distribution::_display_url ; sub _display_url { my($self,$url) = @_; my($res,$saved_file,$pid,$out); $CPAN::Frontend->myprint(qq{ + _display_url($url)\n}) if $CPAN::DEBUG; # should we define it in the config instead? my $html_converter = "html2text.pl"; my $web_browser = $CPAN::Config->{'lynx'} || undef; my $web_browser_out = $web_browser ? CPAN::Distribution->_check_binary($self,$web_browser) : undef; if ($web_browser_out) { # web browser found, run the action my $browser = CPAN::HandleConfig->safe_quote($CPAN::Config->{'lynx'}); $CPAN::Frontend->myprint(qq{system[$browser $url]}) if $CPAN::DEBUG; $CPAN::Frontend->myprint(qq{ Displaying URL $url with browser $browser }); $CPAN::Frontend->mysleep(1); system("$browser $url"); if ($saved_file) { 1 while unlink($saved_file) } } else { # web browser not found, let's try text only my $html_converter_out = CPAN::Distribution->_check_binary($self,$html_converter); $html_converter_out = CPAN::HandleConfig->safe_quote($html_converter_out); if ($html_converter_out ) { # html2text found, run it $saved_file = CPAN::Distribution->_getsave_url( $self, $url ); $CPAN::Frontend->mydie(qq{ERROR: problems while getting $url\n}) unless defined($saved_file); local *README; $pid = open README, "$html_converter $saved_file |" or $CPAN::Frontend->mydie(qq{ Could not fork '$html_converter $saved_file': $!}); my($fh,$filename); if ($CPAN::META->has_usable("File::Temp")) { $fh = File::Temp->new( dir => File::Spec->tmpdir, template => 'cpan_htmlconvert_XXXX', suffix => '.txt', unlink => 0, ); $filename = $fh->filename; } else { $filename = "cpan_htmlconvert_$$.txt"; $fh = FileHandle->new(); open $fh, ">$filename" or die; } while () { $fh->print($_); } close README or $CPAN::Frontend->mydie(qq{Could not run '$html_converter $saved_file': $!}); my $tmpin = $fh->filename; $CPAN::Frontend->myprint(sprintf(qq{ Run '%s %s' and saved output to %s\n}, $html_converter, $saved_file, $tmpin, )) if $CPAN::DEBUG; close $fh; local *FH; open FH, $tmpin or $CPAN::Frontend->mydie(qq{Could not open "$tmpin": $!}); my $fh_pager = FileHandle->new; local($SIG{PIPE}) = "IGNORE"; my $pager = $CPAN::Config->{'pager'} || "cat"; $fh_pager->open("|$pager") or $CPAN::Frontend->mydie(qq{ Could not open pager '$pager': $!}); $CPAN::Frontend->myprint(qq{ Displaying URL $url with pager "$pager" }); $CPAN::Frontend->mysleep(1); $fh_pager->print(); $fh_pager->close; } else { # coldn't find the web browser or html converter $CPAN::Frontend->myprint(qq{ You need to install lynx or $html_converter to use this feature.}); } } } #-> sub CPAN::Distribution::_getsave_url ; sub _getsave_url { my($dist, $shell, $url) = @_; $CPAN::Frontend->myprint(qq{ + _getsave_url($url)\n}) if $CPAN::DEBUG; my($fh,$filename); if ($CPAN::META->has_usable("File::Temp")) { $fh = File::Temp->new( dir => File::Spec->tmpdir, template => "cpan_getsave_url_XXXX", suffix => ".html", unlink => 0, ); $filename = $fh->filename; } else { $fh = FileHandle->new; $filename = "cpan_getsave_url_$$.html"; } my $tmpin = $filename; if ($CPAN::META->has_usable('LWP')) { $CPAN::Frontend->myprint("Fetching with LWP: $url "); my $Ua; CPAN::LWP::UserAgent->config; eval { $Ua = CPAN::LWP::UserAgent->new; }; if ($@) { $CPAN::Frontend->mywarn("ERROR: CPAN::LWP::UserAgent->new dies with $@\n"); return; } else { my($var); $Ua->proxy('http', $var) if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy}; $Ua->no_proxy($var) if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy}; } my $req = HTTP::Request->new(GET => $url); $req->header('Accept' => 'text/html'); my $res = $Ua->request($req); if ($res->is_success) { $CPAN::Frontend->myprint(" + request successful.\n") if $CPAN::DEBUG; print $fh $res->content; close $fh; $CPAN::Frontend->myprint(qq{ + saved content to $tmpin \n}) if $CPAN::DEBUG; return $tmpin; } else { $CPAN::Frontend->myprint(sprintf( "LWP failed with code[%s], message[%s]\n", $res->code, $res->message, )); return; } } else { $CPAN::Frontend->mywarn(" LWP not available\n"); return; } } #-> sub CPAN::Distribution::_build_command sub _build_command { my($self) = @_; if ($^O eq "MSWin32") { # special code needed at least up to # Module::Build 0.2611 and 0.2706; a fix # in M:B has been promised 2006-01-30 my($perl) = $self->perl or $CPAN::Frontend->mydie("Couldn't find executable perl\n"); return "$perl ./Build"; } elsif ($^O eq 'VMS') { return "$^X Build.com"; } return "./Build"; } #-> sub CPAN::Distribution::_should_report sub _should_report { my($self, $phase) = @_; die "_should_report() requires a 'phase' argument" if ! defined $phase; return unless $CPAN::META->has_usable("CPAN::Reporter"); # configured my $test_report = CPAN::HandleConfig->prefs_lookup($self, q{test_report}); return unless $test_report; # don't repeat if we cached a result return $self->{should_report} if exists $self->{should_report}; # don't report if we generated a Makefile.PL if ( $self->{had_no_makefile_pl} ) { $CPAN::Frontend->mywarn( "Will not send CPAN Testers report with generated Makefile.PL.\n" ); return $self->{should_report} = 0; } # available if ( ! $CPAN::META->has_inst("CPAN::Reporter")) { $CPAN::Frontend->mywarnonce( "CPAN::Reporter not installed. No reports will be sent.\n" ); return $self->{should_report} = 0; } # capable my $crv = CPAN::Reporter->VERSION; if ( CPAN::Version->vlt( $crv, 0.99 ) ) { # don't cache $self->{should_report} -- need to check each phase if ( $phase eq 'test' ) { return 1; } else { $CPAN::Frontend->mywarn( "Reporting on the '$phase' phase requires CPAN::Reporter 0.99, but \n" . "you only have version $crv\. Only 'test' phase reports will be sent.\n" ); return; } } # appropriate if ($self->is_dot_dist) { $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is disabled ". "for local directories\n"); return $self->{should_report} = 0; } if ($self->prefs->{patches} && @{$self->prefs->{patches}} && $self->{patched} ) { $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is disabled ". "when the source has been patched\n"); return $self->{should_report} = 0; } # proceed and cache success return $self->{should_report} = 1; } #-> sub CPAN::Distribution::reports sub reports { my($self) = @_; my $pathname = $self->id; $CPAN::Frontend->myprint("Distribution: $pathname\n"); unless ($CPAN::META->has_inst("CPAN::DistnameInfo")) { $CPAN::Frontend->mydie("CPAN::DistnameInfo not installed; cannot continue"); } unless ($CPAN::META->has_usable("LWP")) { $CPAN::Frontend->mydie("LWP not installed; cannot continue"); } unless ($CPAN::META->has_usable("File::Temp")) { $CPAN::Frontend->mydie("File::Temp not installed; cannot continue"); } my $format; if ($CPAN::META->has_inst("YAML::XS") || $CPAN::META->has_inst("YAML::Syck")){ $format = 'yaml'; } elsif (!$format && $CPAN::META->has_inst("JSON::PP") ) { $format = 'json'; } else { $CPAN::Frontend->mydie("JSON::PP not installed, cannot continue"); } my $d = CPAN::DistnameInfo->new($pathname); my $dist = $d->dist; # "CPAN-DistnameInfo" my $version = $d->version; # "0.02" my $maturity = $d->maturity; # "released" my $filename = $d->filename; # "CPAN-DistnameInfo-0.02.tar.gz" my $cpanid = $d->cpanid; # "GBARR" my $distvname = $d->distvname; # "CPAN-DistnameInfo-0.02" my $url = sprintf "http://www.cpantesters.org/show/%s.%s", $dist, $format; CPAN::LWP::UserAgent->config; my $Ua; eval { $Ua = CPAN::LWP::UserAgent->new; }; if ($@) { $CPAN::Frontend->mydie("CPAN::LWP::UserAgent->new dies with $@\n"); } $CPAN::Frontend->myprint("Fetching '$url'..."); my $resp = $Ua->get($url); unless ($resp->is_success) { $CPAN::Frontend->mydie(sprintf "Could not download '%s': %s\n", $url, $resp->code); } $CPAN::Frontend->myprint("DONE\n\n"); my $unserialized; if ( $format eq 'yaml' ) { my $yaml = $resp->content; # what a long way round! my $fh = File::Temp->new( dir => File::Spec->tmpdir, template => 'cpan_reports_XXXX', suffix => '.yaml', unlink => 0, ); my $tfilename = $fh->filename; print $fh $yaml; close $fh or $CPAN::Frontend->mydie("Could not close '$tfilename': $!"); $unserialized = CPAN->_yaml_loadfile($tfilename)->[0]; unlink $tfilename or $CPAN::Frontend->mydie("Could not unlink '$tfilename': $!"); } else { require JSON::PP; $unserialized = JSON::PP->new->utf8->decode($resp->content); } my %other_versions; my $this_version_seen; for my $rep (@$unserialized) { my $rversion = $rep->{version}; if ($rversion eq $version) { unless ($this_version_seen++) { $CPAN::Frontend->myprint ("$rep->{version}:\n"); } my $arch = $rep->{archname} || $rep->{platform} || '????'; my $grade = $rep->{action} || $rep->{status} || '????'; my $ostext = $rep->{ostext} || ucfirst($rep->{osname}) || '????'; $CPAN::Frontend->myprint (sprintf("%1s%1s%-4s %s on %s %s (%s)\n", $arch eq $Config::Config{archname}?"*":"", $grade eq "PASS"?"+":$grade eq"FAIL"?"-":"", $grade, $rep->{perl}, $ostext, $rep->{osvers}, $arch, )); } else { $other_versions{$rep->{version}}++; } } unless ($this_version_seen) { $CPAN::Frontend->myprint("No reports found for version '$version' Reports for other versions:\n"); for my $v (sort keys %other_versions) { $CPAN::Frontend->myprint(" $v\: $other_versions{$v}\n"); } } $url = substr($url,0,-4) . 'html'; $CPAN::Frontend->myprint("See $url for details\n"); } 1; CPAN/FirstTime.pm000044400000222025151112047420007527 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::FirstTime; use strict; use ExtUtils::MakeMaker (); use FileHandle (); use File::Basename (); use File::Path (); use File::Spec (); use CPAN::Mirrors (); use CPAN::Version (); use vars qw($VERSION $auto_config); $VERSION = "5.5317"; =head1 NAME CPAN::FirstTime - Utility for CPAN::Config file Initialization =head1 SYNOPSIS CPAN::FirstTime::init() =head1 DESCRIPTION The init routine asks a few questions and writes a CPAN/Config.pm or CPAN/MyConfig.pm file (depending on what it is currently using). In the following all questions and explanations regarding config variables are collected. =cut # down until the next =back the manpage must be parsed by the program # because the text is used in the init dialogues. my @podpara = split /\n\n/, <<'=back'; =over 2 =item allow_installing_module_downgrades The CPAN shell can watch the C directories that are built up before running C to determine whether the current distribution will end up with modules being overwritten with decreasing module version numbers. It can then let the build of this distro fail when it discovers a downgrade. Do you want to allow installing distros with decreasing module versions compared to what you have installed (yes, no, ask/yes, ask/no)? =item allow_installing_outdated_dists The CPAN shell can watch the C directories that are built up before running C to determine whether the current distribution contains modules that are indexed with a distro with a higher distro-version number than the current one. It can then let the build of this distro fail when it would not represent the most up-to-date version of the distro. Note: choosing anything but 'yes' for this option will need CPAN::DistnameInfo being installed for taking effect. Do you want to allow installing distros that are not indexed as the highest distro-version for all contained modules (yes, no, ask/yes, ask/no)? =item auto_commit Normally CPAN.pm keeps config variables in memory and changes need to be saved in a separate 'o conf commit' command to make them permanent between sessions. If you set the 'auto_commit' option to true, changes to a config variable are always automatically committed to disk. Always commit changes to config variables to disk? =item build_cache CPAN.pm can limit the size of the disk area for keeping the build directories with all the intermediate files. Cache size for build directory (in MB)? =item build_dir Directory where the build process takes place? =item build_dir_reuse Until version 1.88 CPAN.pm never trusted the contents of the build_dir directory between sessions. Since 1.88_58 CPAN.pm has a YAML-based mechanism that makes it possible to share the contents of the build_dir/ directory between different sessions with the same version of perl. People who prefer to test things several days before installing will like this feature because it saves a lot of time. If you say yes to the following question, CPAN will try to store enough information about the build process so that it can pick up in future sessions at the same state of affairs as it left a previous session. Store and re-use state information about distributions between CPAN.pm sessions? =item build_requires_install_policy When a module declares another one as a 'build_requires' prerequisite this means that the other module is only needed for building or testing the module but need not be installed permanently. In this case you may wish to install that other module nonetheless or just keep it in the 'build_dir' directory to have it available only temporarily. Installing saves time on future installations but makes the perl installation bigger. You can choose if you want to always install (yes), never install (no) or be always asked. In the latter case you can set the default answer for the question to yes (ask/yes) or no (ask/no). Policy on installing 'build_requires' modules (yes, no, ask/yes, ask/no)? =item cache_metadata To considerably speed up the initial CPAN shell startup, it is possible to use Storable to create a cache of metadata. If Storable is not available, the normal index mechanism will be used. Note: this mechanism is not used when use_sqlite is on and SQLite is running. Cache metadata (yes/no)? =item check_sigs CPAN packages can be digitally signed by authors and thus verified with the security provided by strong cryptography. The exact mechanism is defined in the Module::Signature module. While this is generally considered a good thing, it is not always convenient to the end user to install modules that are signed incorrectly or where the key of the author is not available or where some prerequisite for Module::Signature has a bug and so on. With the check_sigs parameter you can turn signature checking on and off. The default is off for now because the whole tool chain for the functionality is not yet considered mature by some. The author of CPAN.pm would recommend setting it to true most of the time and turning it off only if it turns out to be annoying. Note that if you do not have Module::Signature installed, no signature checks will be performed at all. Always try to check and verify signatures if a SIGNATURE file is in the package and Module::Signature is installed (yes/no)? =item cleanup_after_install Users who install modules and do not intend to look back, can free occupied disk space quickly by letting CPAN.pm cleanup each build directory immediately after a successful install. Remove build directory after a successful install? (yes/no)? =item colorize_output When you have Term::ANSIColor installed, you can turn on colorized output to have some visual differences between normal CPAN.pm output, warnings, debugging output, and the output of the modules being installed. Set your favorite colors after some experimenting with the Term::ANSIColor module. Please note that on Windows platforms colorized output also requires the Win32::Console::ANSI module. Do you want to turn on colored output? =item colorize_print Color for normal output? =item colorize_warn Color for warnings? =item colorize_debug Color for debugging messages? =item commandnumber_in_prompt The prompt of the cpan shell can contain the current command number for easier tracking of the session or be a plain string. Do you want the command number in the prompt (yes/no)? =item connect_to_internet_ok If you have never defined your own C in your configuration then C will be hesitant to use the built in default sites for downloading. It will ask you once per session if a connection to the internet is OK and only if you say yes, it will try to connect. But to avoid this question, you can choose your favorite download sites once and get away with it. Or, if you have no favorite download sites answer yes to the following question. If no urllist has been chosen yet, would you prefer CPAN.pm to connect to the built-in default sites without asking? (yes/no)? =item ftp_passive Shall we always set the FTP_PASSIVE environment variable when dealing with ftp download (yes/no)? =item ftpstats_period Statistics about downloads are truncated by size and period simultaneously. How many days shall we keep statistics about downloads? =item ftpstats_size Statistics about downloads are truncated by size and period simultaneously. Setting this to zero or negative disables download statistics. How many items shall we keep in the statistics about downloads? =item getcwd CPAN.pm changes the current working directory often and needs to determine its own current working directory. Per default it uses Cwd::cwd but if this doesn't work on your system for some reason, alternatives can be configured according to the following table: cwd Cwd::cwd getcwd Cwd::getcwd fastcwd Cwd::fastcwd getdcwd Cwd::getdcwd backtickcwd external command cwd Preferred method for determining the current working directory? =item halt_on_failure Normally, CPAN.pm continues processing the full list of targets and dependencies, even if one of them fails. However, you can specify that CPAN should halt after the first failure. (Note that optional recommended or suggested modules that fail will not cause a halt.) Do you want to halt on failure (yes/no)? =item histfile If you have one of the readline packages (Term::ReadLine::Perl, Term::ReadLine::Gnu, possibly others) installed, the interactive CPAN shell will have history support. The next two questions deal with the filename of the history file and with its size. If you do not want to set this variable, please hit SPACE ENTER to the following question. File to save your history? =item histsize Number of lines to save? =item inactivity_timeout Sometimes you may wish to leave the processes run by CPAN alone without caring about them. Because the Makefile.PL or the Build.PL sometimes contains question you're expected to answer, you can set a timer that will kill a 'perl Makefile.PL' process after the specified time in seconds. If you set this value to 0, these processes will wait forever. This is the default and recommended setting. Timeout for inactivity during {Makefile,Build}.PL? =item index_expire The CPAN indexes are usually rebuilt once or twice per hour, but the typical CPAN mirror mirrors only once or twice per day. Depending on the quality of your mirror and your desire to be on the bleeding edge, you may want to set the following value to more or less than one day (which is the default). It determines after how many days CPAN.pm downloads new indexes. Let the index expire after how many days? =item inhibit_startup_message When the CPAN shell is started it normally displays a greeting message that contains the running version and the status of readline support. Do you want to turn this message off? =item keep_source_where Unless you are accessing the CPAN on your filesystem via a file: URL, CPAN.pm needs to keep the source files it downloads somewhere. Please supply a directory where the downloaded files are to be kept. Download target directory? =item load_module_verbosity When CPAN.pm loads a module it needs for some optional feature, it usually reports about module name and version. Choose 'v' to get this message, 'none' to suppress it. Verbosity level for loading modules (none or v)? =item makepl_arg Every Makefile.PL is run by perl in a separate process. Likewise we run 'make' and 'make install' in separate processes. If you have any parameters (e.g. PREFIX, UNINST or the like) you want to pass to the calls, please specify them here. If you don't understand this question, just press ENTER. Typical frequently used settings: PREFIX=~/perl # non-root users (please see manual for more hints) Parameters for the 'perl Makefile.PL' command? =item make_arg Parameters for the 'make' command? Typical frequently used setting: -j3 # dual processor system (on GNU make) Your choice: =item make_install_arg Parameters for the 'make install' command? Typical frequently used setting: UNINST=1 # to always uninstall potentially conflicting files # (but do NOT use with local::lib or INSTALL_BASE) Your choice: =item make_install_make_command Do you want to use a different make command for 'make install'? Cautious people will probably prefer: su root -c make or sudo make or /path1/to/sudo -u admin_account /path2/to/make or some such. Your choice: =item mbuildpl_arg A Build.PL is run by perl in a separate process. Likewise we run './Build' and './Build install' in separate processes. If you have any parameters you want to pass to the calls, please specify them here. Typical frequently used settings: --install_base /home/xxx # different installation directory Parameters for the 'perl Build.PL' command? =item mbuild_arg Parameters for the './Build' command? Setting might be: --extra_linker_flags -L/usr/foo/lib # non-standard library location Your choice: =item mbuild_install_arg Parameters for the './Build install' command? Typical frequently used setting: --uninst 1 # uninstall conflicting files # (but do NOT use with local::lib or INSTALL_BASE) Your choice: =item mbuild_install_build_command Do you want to use a different command for './Build install'? Sudo users will probably prefer: su root -c ./Build or sudo ./Build or /path1/to/sudo -u admin_account ./Build or some such. Your choice: =item pager What is your favorite pager program? =item prefer_installer When you have Module::Build installed and a module comes with both a Makefile.PL and a Build.PL, which shall have precedence? The main two standard installer modules are the old and well established ExtUtils::MakeMaker (for short: EUMM) which uses the Makefile.PL. And the next generation installer Module::Build (MB) which works with the Build.PL (and often comes with a Makefile.PL too). If a module comes only with one of the two we will use that one but if both are supplied then a decision must be made between EUMM and MB. See also http://rt.cpan.org/Ticket/Display.html?id=29235 for a discussion about the right default. Or, as a third option you can choose RAND which will make a random decision (something regular CPAN testers will enjoy). In case you can choose between running a Makefile.PL or a Build.PL, which installer would you prefer (EUMM or MB or RAND)? =item prefs_dir CPAN.pm can store customized build environments based on regular expressions for distribution names. These are YAML files where the default options for CPAN.pm and the environment can be overridden and dialog sequences can be stored that can later be executed by an Expect.pm object. The CPAN.pm distribution comes with some prefab YAML files that cover sample distributions that can be used as blueprints to store your own prefs. Please check out the distroprefs/ directory of the CPAN.pm distribution to get a quick start into the prefs system. Directory where to store default options/environment/dialogs for building modules that need some customization? =item prerequisites_policy The CPAN module can detect when a module which you are trying to build depends on prerequisites. If this happens, it can build the prerequisites for you automatically ('follow'), ask you for confirmation ('ask'), or just ignore them ('ignore'). Choosing 'follow' also sets PERL_AUTOINSTALL and PERL_EXTUTILS_AUTOINSTALL for "--defaultdeps" if not already set. Please set your policy to one of the three values. Policy on building prerequisites (follow, ask or ignore)? =item pushy_https Boolean. Defaults to true. If this option is true, the cpan shell will use https://cpan.org/ to download stuff from the CPAN. It will fall back to http://cpan.org/ if it can't handle https for some reason (missing modules, missing programs). Whenever it falls back to the http protocol, it will issue a warning. If this option is true, the option C will be ignored. Consequently, if you want to work with local mirrors via your own configured list of URLs, you will have to choose no below. Do you want to turn the pushy_https behaviour on? =item randomize_urllist CPAN.pm can introduce some randomness when using hosts for download that are configured in the urllist parameter. Enter a numeric value between 0 and 1 to indicate how often you want to let CPAN.pm try a random host from the urllist. A value of one specifies to always use a random host as the first try. A value of zero means no randomness at all. Anything in between specifies how often, on average, a random host should be tried first. Randomize parameter =item recommends_policy (Experimental feature!) Some CPAN modules recommend additional, optional dependencies. These should generally be installed except in resource constrained environments. When this policy is true, recommended modules will be included with required modules. Include recommended modules? =item scan_cache By default, each time the CPAN module is started, cache scanning is performed to keep the cache size in sync ('atstart'). Alternatively, scanning and cleanup can happen when CPAN exits ('atexit'). To prevent any cache cleanup, answer 'never'. Perform cache scanning ('atstart', 'atexit' or 'never')? =item shell What is your favorite shell? =item show_unparsable_versions During the 'r' command CPAN.pm finds modules without version number. When the command finishes, it prints a report about this. If you want this report to be very verbose, say yes to the following variable. Show all individual modules that have no $VERSION? =item show_upload_date The 'd' and the 'm' command normally only show you information they have in their in-memory database and thus will never connect to the internet. If you set the 'show_upload_date' variable to true, 'm' and 'd' will additionally show you the upload date of the module or distribution. Per default this feature is off because it may require a net connection to get at the upload date. Always try to show upload date with 'd' and 'm' command (yes/no)? =item show_zero_versions During the 'r' command CPAN.pm finds modules with a version number of zero. When the command finishes, it prints a report about this. If you want this report to be very verbose, say yes to the following variable. Show all individual modules that have a $VERSION of zero? =item suggests_policy (Experimental feature!) Some CPAN modules suggest additional, optional dependencies. These 'suggest' dependencies provide enhanced operation. When this policy is true, suggested modules will be included with required modules. Include suggested modules? =item tar_verbosity When CPAN.pm uses the tar command, which switch for the verbosity shall be used? Choose 'none' for quiet operation, 'v' for file name listing, 'vv' for full listing. Tar command verbosity level (none or v or vv)? =item term_is_latin The next option deals with the charset (a.k.a. character set) your terminal supports. In general, CPAN is English speaking territory, so the charset does not matter much but some CPAN have names that are outside the ASCII range. If your terminal supports UTF-8, you should say no to the next question. If it expects ISO-8859-1 (also known as LATIN1) then you should say yes. If it supports neither, your answer does not matter because you will not be able to read the names of some authors anyway. If you answer no, names will be output in UTF-8. Your terminal expects ISO-8859-1 (yes/no)? =item term_ornaments When using Term::ReadLine, you can turn ornaments on so that your input stands out against the output from CPAN.pm. Do you want to turn ornaments on? =item test_report The goal of the CPAN Testers project (http://testers.cpan.org/) is to test as many CPAN packages as possible on as many platforms as possible. This provides valuable feedback to module authors and potential users to identify bugs or platform compatibility issues and improves the overall quality and value of CPAN. One way you can contribute is to send test results for each module that you install. If you install the CPAN::Reporter module, you have the option to automatically generate and deliver test reports to CPAN Testers whenever you run tests on a CPAN package. See the CPAN::Reporter documentation for additional details and configuration settings. If your firewall blocks outgoing traffic, you may need to configure CPAN::Reporter before sending reports. Generate test reports if CPAN::Reporter is installed (yes/no)? =item perl5lib_verbosity When CPAN.pm extends @INC via PERL5LIB, it prints a list of directories added (or a summary of how many directories are added). Choose 'v' to get this message, 'none' to suppress it. Verbosity level for PERL5LIB changes (none or v)? =item prefer_external_tar Per default all untar operations are done with the perl module Archive::Tar; by setting this variable to true the external tar command is used if available; on Unix this is usually preferred because they have a reliable and fast gnutar implementation. Use the external tar program instead of Archive::Tar? =item trust_test_report_history When a distribution has already been tested by CPAN::Reporter on this machine, CPAN can skip the test phase and just rely on the test report history instead. Note that this will not apply to distributions that failed tests because of missing dependencies. Also, tests can be run regardless of the history using "force". Do you want to rely on the test report history (yes/no)? =item urllist_ping_external When automatic selection of the nearest cpan mirrors is performed, turn on the use of the external ping via Net::Ping::External. This is recommended in the case the local network has a transparent proxy. Do you want to use the external ping command when autoselecting mirrors? =item urllist_ping_verbose When automatic selection of the nearest cpan mirrors is performed, this option can be used to turn on verbosity during the selection process. Do you want to see verbosity turned on when autoselecting mirrors? =item use_prompt_default When this is true, CPAN will set PERL_MM_USE_DEFAULT to a true value. This causes ExtUtils::MakeMaker (and compatible) prompts to use default values instead of stopping to prompt you to answer questions. It also sets NONINTERACTIVE_TESTING to a true value to signal more generally that distributions should not try to interact with you. Do you want to use prompt defaults (yes/no)? =item use_sqlite CPAN::SQLite is a layer between the index files that are downloaded from the CPAN and CPAN.pm that speeds up metadata queries and reduces memory consumption of CPAN.pm considerably. Use CPAN::SQLite if available? (yes/no)? =item version_timeout This timeout prevents CPAN from hanging when trying to parse a pathologically coded $VERSION from a module. The default is 15 seconds. If you set this value to 0, no timeout will occur, but this is not recommended. Timeout for parsing module versions? =item yaml_load_code Both YAML.pm and YAML::Syck are capable of deserialising code. As this requires a string eval, which might be a security risk, you can use this option to enable or disable the deserialisation of code via CPAN::DeferredCode. (Note: This does not work under perl 5.6) Do you want to enable code deserialisation (yes/no)? =item yaml_module At the time of this writing (2009-03) there are three YAML implementations working: YAML, YAML::Syck, and YAML::XS. The latter two are faster but need a C compiler installed on your system. There may be more alternative YAML conforming modules. When I tried two other players, YAML::Tiny and YAML::Perl, they seemed not powerful enough to work with CPAN.pm. This may have changed in the meantime. Which YAML implementation would you prefer? =back =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut use vars qw( %prompts ); { my @prompts = ( auto_config => qq{ CPAN.pm requires configuration, but most of it can be done automatically. If you answer 'no' below, you will enter an interactive dialog for each configuration option instead. Would you like to configure as much as possible automatically?}, auto_pick => qq{ Would you like me to automatically choose some CPAN mirror sites for you? (This means connecting to the Internet)}, config_intro => qq{ The following questions are intended to help you with the configuration. The CPAN module needs a directory of its own to cache important index files and maybe keep a temporary mirror of CPAN files. This may be a site-wide or a personal directory. }, # cpan_home => qq{ }, cpan_home_where => qq{ First of all, I'd like to create this directory. Where? }, external_progs => qq{ The CPAN module will need a few external programs to work properly. Please correct me, if I guess the wrong path for a program. Don't panic if you do not have some of them, just press ENTER for those. To disable the use of a program, you can type a space followed by ENTER. }, proxy_intro => qq{ If you're accessing the net via proxies, you can specify them in the CPAN configuration or via environment variables. The variable in the \$CPAN::Config takes precedence. }, proxy_user => qq{ If your proxy is an authenticating proxy, you can store your username permanently. If you do not want that, just press ENTER. You will then be asked for your username in every future session. }, proxy_pass => qq{ Your password for the authenticating proxy can also be stored permanently on disk. If this violates your security policy, just press ENTER. You will then be asked for the password in every future session. }, urls_intro => qq{ Now you need to choose your CPAN mirror sites. You can let me pick mirrors for you, you can select them from a list or you can enter them by hand. }, urls_picker_intro => qq{First, pick a nearby continent and country by typing in the number(s) in front of the item(s) you want to select. You can pick several of each, separated by spaces. Then, you will be presented with a list of URLs of CPAN mirrors in the countries you selected, along with previously selected URLs. Select some of those URLs, or just keep the old list. Finally, you will be prompted for any extra URLs -- file:, ftp:, or http: -- that host a CPAN mirror. You should select more than one (just in case the first isn't available). }, password_warn => qq{ Warning: Term::ReadKey seems not to be available, your password will be echoed to the terminal! }, install_help => qq{ Warning: You do not have write permission for Perl library directories. To install modules, you need to configure a local Perl library directory or escalate your privileges. CPAN can help you by bootstrapping the local::lib module or by configuring itself to use 'sudo' (if available). You may also resolve this problem manually if you need to customize your setup. What approach do you want? (Choose 'local::lib', 'sudo' or 'manual') }, local_lib_installed => qq{ local::lib is installed. You must now add the following environment variables to your shell configuration files (or registry, if you are on Windows) and then restart your command line shell and CPAN before installing modules: }, ); die "Coding error in \@prompts declaration. Odd number of elements, above" if (@prompts % 2); %prompts = @prompts; if (scalar(keys %prompts) != scalar(@prompts)/2) { my %already; for my $item (0..$#prompts) { next if $item % 2; die "$prompts[$item] is duplicated\n" if $already{$prompts[$item]}++; } } shift @podpara; while (@podpara) { warn "Alert: cannot parse my own manpage for init dialog" unless $podpara[0] =~ s/^=item\s+//; my $name = shift @podpara; my @para; while (@podpara && $podpara[0] !~ /^=item/) { push @para, shift @podpara; } $prompts{$name} = pop @para; if (@para) { $prompts{$name . "_intro"} = join "", map { "$_\n\n" } @para; } } } sub init { my($configpm, %args) = @_; use Config; # extra args after 'o conf init' my $matcher = $args{args} && @{$args{args}} ? $args{args}[0] : ''; if ($matcher =~ /^\/(.*)\/$/) { # case /regex/ => take the first, ignore the rest $matcher = $1; shift @{$args{args}}; if (@{$args{args}}) { local $" = " "; $CPAN::Frontend->mywarn("Ignoring excessive arguments '@{$args{args}}'"); $CPAN::Frontend->mysleep(2); } } elsif (0 == length $matcher) { } elsif (0 && $matcher eq "~") { # extremely buggy, but a nice idea my @unconfigured = sort grep { not exists $CPAN::Config->{$_} or not defined $CPAN::Config->{$_} or not length $CPAN::Config->{$_} } keys %$CPAN::Config; $matcher = "\\b(".join("|", @unconfigured).")\\b"; $CPAN::Frontend->mywarn("matcher[$matcher]"); } else { # case WORD... => all arguments must be valid for my $arg (@{$args{args}}) { unless (exists $CPAN::HandleConfig::keys{$arg}) { $CPAN::Frontend->mywarn("'$arg' is not a valid configuration variable\n"); return; } } $matcher = "\\b(".join("|",@{$args{args}}).")\\b"; } CPAN->debug("matcher[$matcher]") if $CPAN::DEBUG; unless ($CPAN::VERSION) { require CPAN::Nox; } require CPAN::HandleConfig; CPAN::HandleConfig::require_myconfig_or_config(); $CPAN::Config ||= {}; local($/) = "\n"; local($\) = ""; local($|) = 1; my($ans,$default); # why so half global? # #= Files, directories # local *_real_prompt; if ( $args{autoconfig} ) { $auto_config = 1; } elsif ($matcher) { $auto_config = 0; } else { my $_conf = prompt($prompts{auto_config}, "yes"); $auto_config = ($_conf and $_conf =~ /^y/i) ? 1 : 0; } CPAN->debug("auto_config[$auto_config]") if $CPAN::DEBUG; if ( $auto_config ) { local $^W = 0; # prototype should match that of &MakeMaker::prompt my $current_second = time; my $current_second_count = 0; my $i_am_mad = 0; # silent prompting -- just quietly use default *_real_prompt = sub { return $_[1] }; } # # bootstrap local::lib or sudo # unless ( $matcher || _can_write_to_libdirs() || _using_installbase() || _using_sudo() ) { local $auto_config = 0; # We *must* ask, even under autoconfig local *_real_prompt; # We *must* show prompt my_prompt_loop(install_help => 'local::lib', $matcher, 'local::lib|sudo|manual'); } $CPAN::Config->{install_help} ||= ''; # Temporary to suppress warnings if (!$matcher or q{ build_dir build_dir_reuse cpan_home keep_source_where prefs_dir } =~ /$matcher/) { $CPAN::Frontend->myprint($prompts{config_intro}) unless $auto_config; init_cpan_home($matcher); my_dflt_prompt("keep_source_where", File::Spec->catdir($CPAN::Config->{cpan_home},"sources"), $matcher, ); my_dflt_prompt("build_dir", File::Spec->catdir($CPAN::Config->{cpan_home},"build"), $matcher ); my_yn_prompt(build_dir_reuse => 0, $matcher); my_dflt_prompt("prefs_dir", File::Spec->catdir($CPAN::Config->{cpan_home},"prefs"), $matcher ); } # #= Config: auto_commit # my_yn_prompt(auto_commit => 0, $matcher); # #= Cache size, Index expire # my_dflt_prompt(build_cache => 100, $matcher); my_dflt_prompt(index_expire => 1, $matcher); my_prompt_loop(scan_cache => 'atstart', $matcher, 'atstart|atexit|never'); my_yn_prompt(cleanup_after_install => 0, $matcher); # #= cache_metadata # my_yn_prompt(cache_metadata => 1, $matcher); my_yn_prompt(use_sqlite => 0, $matcher); # #= Do we follow PREREQ_PM? # my_prompt_loop(prerequisites_policy => 'follow', $matcher, 'follow|ask|ignore'); my_prompt_loop(build_requires_install_policy => 'yes', $matcher, 'yes|no|ask/yes|ask/no'); my_yn_prompt(recommends_policy => 1, $matcher); my_yn_prompt(suggests_policy => 0, $matcher); # #= Module::Signature # my_yn_prompt(check_sigs => 0, $matcher); # #= CPAN::Reporter # if (!$matcher or 'test_report' =~ /$matcher/) { my_yn_prompt(test_report => 0, $matcher); if ( $matcher && $CPAN::Config->{test_report} && $CPAN::META->has_inst("CPAN::Reporter") && CPAN::Reporter->can('configure') ) { my $_conf = prompt("Would you like me configure CPAN::Reporter now?", "yes"); if ($_conf =~ /^y/i) { $CPAN::Frontend->myprint("\nProceeding to configure CPAN::Reporter.\n"); CPAN::Reporter::configure(); $CPAN::Frontend->myprint("\nReturning to CPAN configuration.\n"); } } } my_yn_prompt(trust_test_report_history => 0, $matcher); # #= YAML vs. YAML::Syck # if (!$matcher or "yaml_module" =~ /$matcher/) { my_dflt_prompt(yaml_module => "YAML", $matcher); my $old_v = $CPAN::Config->{load_module_verbosity}; $CPAN::Config->{load_module_verbosity} = q[none]; if (!$auto_config && !$CPAN::META->has_inst($CPAN::Config->{yaml_module})) { $CPAN::Frontend->mywarn ("Warning (maybe harmless): '$CPAN::Config->{yaml_module}' not installed.\n"); $CPAN::Frontend->mysleep(3); } $CPAN::Config->{load_module_verbosity} = $old_v; } # #= YAML code deserialisation # my_yn_prompt(yaml_load_code => 0, $matcher); # #= External programs # my(@path) = split /$Config{'path_sep'}/, $ENV{'PATH'}; $CPAN::Frontend->myprint($prompts{external_progs}) if !$matcher && !$auto_config; _init_external_progs($matcher, { path => \@path, progs => [ qw/make bzip2 gzip tar unzip gpg patch applypatch/ ], shortcut => 0 }); _init_external_progs($matcher, { path => \@path, progs => [ qw/wget curl lynx ncftpget ncftp ftp/ ], shortcut => 1 }); { my $path = $CPAN::Config->{'pager'} || $ENV{PAGER} || find_exe("less",\@path) || find_exe("more",\@path) || ($^O eq 'MacOS' ? $ENV{EDITOR} : 0 ) || "more"; my_dflt_prompt(pager => $path, $matcher); } { my $path = $CPAN::Config->{'shell'}; if ($path && File::Spec->file_name_is_absolute($path)) { $CPAN::Frontend->mywarn("Warning: configured $path does not exist\n") unless -e $path; $path = ""; } $path ||= $ENV{SHELL}; $path ||= $ENV{COMSPEC} if $^O eq "MSWin32"; if ($^O eq 'MacOS') { $CPAN::Config->{'shell'} = 'not_here'; } else { $path ||= 'sh', $path =~ s,\\,/,g if $^O eq 'os2'; # Cosmetic only my_dflt_prompt(shell => $path, $matcher); } } { my $tar = $CPAN::Config->{tar}; my $prefer_external_tar = $CPAN::Config->{prefer_external_tar}; # XXX not yet supported unless (defined $prefer_external_tar) { if ($^O =~ /(MSWin32|solaris)/) { # both have a record of broken tars $prefer_external_tar = 0; } elsif ($tar) { $prefer_external_tar = 1; } else { $prefer_external_tar = 0; } } my_yn_prompt(prefer_external_tar => $prefer_external_tar, $matcher); } # # verbosity # my_prompt_loop(tar_verbosity => 'none', $matcher, 'none|v|vv'); my_prompt_loop(load_module_verbosity => 'none', $matcher, 'none|v'); my_prompt_loop(perl5lib_verbosity => 'none', $matcher, 'none|v'); my_yn_prompt(inhibit_startup_message => 0, $matcher); # #= Installer, arguments to make etc. # my_prompt_loop(prefer_installer => 'MB', $matcher, 'MB|EUMM|RAND'); if (!$matcher or 'makepl_arg make_arg' =~ /$matcher/) { my_dflt_prompt(makepl_arg => "", $matcher); my_dflt_prompt(make_arg => "", $matcher); if ( $CPAN::Config->{makepl_arg} =~ /LIBS=|INC=/ ) { $CPAN::Frontend->mywarn( "Warning: Using LIBS or INC in makepl_arg will likely break distributions\n" . "that specify their own LIBS or INC options in Makefile.PL.\n" ); } } require CPAN::HandleConfig; if (exists $CPAN::HandleConfig::keys{make_install_make_command}) { # as long as Windows needs $self->_build_command, we cannot # support sudo on windows :-) my $default = $CPAN::Config->{make} || ""; if ( $default && $CPAN::Config->{install_help} eq 'sudo' ) { if ( find_exe('sudo') ) { $default = "sudo $default"; delete $CPAN::Config->{make_install_make_command} unless $CPAN::Config->{make_install_make_command} =~ /sudo/; } else { $CPAN::Frontend->mywarnonce("Could not find 'sudo' in PATH\n"); } } my_dflt_prompt(make_install_make_command => $default, $matcher); } my_dflt_prompt(make_install_arg => $CPAN::Config->{make_arg} || "", $matcher); my_dflt_prompt(mbuildpl_arg => "", $matcher); my_dflt_prompt(mbuild_arg => "", $matcher); if (exists $CPAN::HandleConfig::keys{mbuild_install_build_command} and $^O ne "MSWin32") { # as long as Windows needs $self->_build_command, we cannot # support sudo on windows :-) my $default = $^O eq 'VMS' ? '@Build.com' : "./Build"; if ( $CPAN::Config->{install_help} eq 'sudo' ) { if ( find_exe('sudo') ) { $default = "sudo $default"; delete $CPAN::Config->{mbuild_install_build_command} unless $CPAN::Config->{mbuild_install_build_command} =~ /sudo/; } else { $CPAN::Frontend->mywarnonce("Could not find 'sudo' in PATH\n"); } } my_dflt_prompt(mbuild_install_build_command => $default, $matcher); } my_dflt_prompt(mbuild_install_arg => "", $matcher); for my $o (qw( allow_installing_outdated_dists allow_installing_module_downgrades )) { my_prompt_loop($o => 'ask/no', $matcher, 'yes|no|ask/yes|ask/no'); } # #== use_prompt_default # my_yn_prompt(use_prompt_default => 0, $matcher); # #= Alarm period # my_dflt_prompt(inactivity_timeout => 0, $matcher); my_dflt_prompt(version_timeout => 15, $matcher); # #== halt_on_failure # my_yn_prompt(halt_on_failure => 0, $matcher); # #= Proxies # my @proxy_vars = qw/ftp_proxy http_proxy no_proxy/; my @proxy_user_vars = qw/proxy_user proxy_pass/; if (!$matcher or "@proxy_vars @proxy_user_vars" =~ /$matcher/) { $CPAN::Frontend->myprint($prompts{proxy_intro}) unless $auto_config; for (@proxy_vars) { $prompts{$_} = "Your $_?"; my_dflt_prompt($_ => $ENV{$_}||"", $matcher); } if ($CPAN::Config->{ftp_proxy} || $CPAN::Config->{http_proxy}) { $default = $CPAN::Config->{proxy_user} || $CPAN::LWP::UserAgent::USER || ""; $CPAN::Frontend->myprint($prompts{proxy_user}) unless $auto_config; if ($CPAN::Config->{proxy_user} = prompt("Your proxy user id?",$default)) { $CPAN::Frontend->myprint($prompts{proxy_pass}) unless $auto_config; if ($CPAN::META->has_inst("Term::ReadKey")) { Term::ReadKey::ReadMode("noecho"); } else { $CPAN::Frontend->myprint($prompts{password_warn}) unless $auto_config; } $CPAN::Config->{proxy_pass} = prompt_no_strip("Your proxy password?"); if ($CPAN::META->has_inst("Term::ReadKey")) { Term::ReadKey::ReadMode("restore"); } $CPAN::Frontend->myprint("\n\n") unless $auto_config; } } } # #= how plugins work # # XXX MISSING: my_array_prompt to be used with plugins. We did something like this near # git log -p fd68f8f5e33f4cecea4fdb7abc5ee19c12f138f0..test-notest-test-dependency # Need to do similar steps for plugin_list. As long as we do not support it here, people # must use the cpan shell prompt to write something like # o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/foo-20141013,... # o conf commit # #= how FTP works # my_yn_prompt(ftp_passive => 1, $matcher); # #= how cwd works # my_prompt_loop(getcwd => 'cwd', $matcher, 'cwd|getcwd|fastcwd|getdcwd|backtickcwd'); # #= the CPAN shell itself (prompt, color) # my_yn_prompt(commandnumber_in_prompt => 1, $matcher); my_yn_prompt(term_ornaments => 1, $matcher); if ("colorize_output colorize_print colorize_warn colorize_debug" =~ $matcher) { my_yn_prompt(colorize_output => 0, $matcher); if ($CPAN::Config->{colorize_output}) { if ($CPAN::META->has_inst("Term::ANSIColor")) { my $T="gYw"; $CPAN::Frontend->myprint( " on_ on_y ". " on_ma on_\n") unless $auto_config; $CPAN::Frontend->myprint( " on_black on_red green ellow ". "on_blue genta on_cyan white\n") unless $auto_config; for my $FG ("", "bold", map {$_,"bold $_"} "black","red","green", "yellow","blue", "magenta", "cyan","white") { $CPAN::Frontend->myprint(sprintf( "%12s ", $FG)) unless $auto_config; for my $BG ("",map {"on_$_"} qw(black red green yellow blue magenta cyan white)) { $CPAN::Frontend->myprint( $FG||$BG ? Term::ANSIColor::colored(" $T ","$FG $BG") : " $T ") unless $auto_config; } $CPAN::Frontend->myprint( "\n" ) unless $auto_config; } $CPAN::Frontend->myprint( "\n" ) unless $auto_config; } for my $tuple ( ["colorize_print", "bold blue on_white"], ["colorize_warn", "bold red on_white"], ["colorize_debug", "black on_cyan"], ) { my_dflt_prompt($tuple->[0] => $tuple->[1], $matcher); if ($CPAN::META->has_inst("Term::ANSIColor")) { eval { Term::ANSIColor::color($CPAN::Config->{$tuple->[0]})}; if ($@) { $CPAN::Config->{$tuple->[0]} = $tuple->[1]; $CPAN::Frontend->mywarn($@."setting to default '$tuple->[1]'\n"); } } } } } # #== term_is_latin # my_yn_prompt(term_is_latin => 1, $matcher); # #== save history in file 'histfile' # if (!$matcher or 'histfile histsize' =~ /$matcher/) { $CPAN::Frontend->myprint($prompts{histfile_intro}) unless $auto_config; defined($default = $CPAN::Config->{histfile}) or $default = File::Spec->catfile($CPAN::Config->{cpan_home},"histfile"); my_dflt_prompt(histfile => $default, $matcher); if ($CPAN::Config->{histfile}) { defined($default = $CPAN::Config->{histsize}) or $default = 100; my_dflt_prompt(histsize => $default, $matcher); } } # #== do an ls on the m or the d command # my_yn_prompt(show_upload_date => 0, $matcher); # #== verbosity at the end of the r command # if (!$matcher or 'show_unparsable_versions' =~ /$matcher/ or 'show_zero_versions' =~ /$matcher/ ) { my_yn_prompt(show_unparsable_versions => 0, $matcher); my_yn_prompt(show_zero_versions => 0, $matcher); } # #= MIRRORED.BY and conf_sites() # # Let's assume they want to use the internet and make them turn it # off if they really don't. my_yn_prompt("connect_to_internet_ok" => 1, $matcher); my_yn_prompt("pushy_https" => 1, $matcher); # Allow matching but don't show during manual config if ($matcher) { if ("urllist_ping_external" =~ $matcher) { my_yn_prompt(urllist_ping_external => 0, $matcher); } if ("urllist_ping_verbose" =~ $matcher) { my_yn_prompt(urllist_ping_verbose => 0, $matcher); } if ("randomize_urllist" =~ $matcher) { my_dflt_prompt(randomize_urllist => 0, $matcher); } if ("ftpstats_size" =~ $matcher) { my_dflt_prompt(ftpstats_size => 99, $matcher); } if ("ftpstats_period" =~ $matcher) { my_dflt_prompt(ftpstats_period => 14, $matcher); } } $CPAN::Config->{urllist} ||= []; if ($auto_config) { if(@{ $CPAN::Config->{urllist} }) { $CPAN::Frontend->myprint( "Your 'urllist' is already configured. Type 'o conf init urllist' to change it.\n" ); } else { # Hint: as of 2021-11: to get http, use http://www.cpan.org/ $CPAN::Config->{urllist} = [ 'https://cpan.org/' ]; $CPAN::Frontend->myprint( "We initialized your 'urllist' to @{$CPAN::Config->{urllist}}. Type 'o conf init urllist' to change it.\n" ); } } elsif (!$matcher || "urllist" =~ $matcher) { _do_pick_mirrors(); } if ($auto_config) { $CPAN::Frontend->myprint( "\nAutoconfiguration complete.\n" ); $auto_config = 0; # reset } # bootstrap local::lib now if requested if ( $CPAN::Config->{install_help} eq 'local::lib' ) { if ( ! @{ $CPAN::Config->{urllist} } ) { $CPAN::Frontend->myprint( "\nALERT: Skipping local::lib bootstrap because 'urllist' is not configured.\n" ); } elsif (! $CPAN::Config->{make} ) { $CPAN::Frontend->mywarn( "\nALERT: Skipping local::lib bootstrap because 'make' is not configured.\n" ); _beg_for_make(); # repetitive, but we don't want users to miss it } else { $CPAN::Frontend->myprint("\nAttempting to bootstrap local::lib...\n"); $CPAN::Frontend->myprint("\nWriting $configpm for bootstrap...\n"); delete $CPAN::Config->{install_help}; # temporary only CPAN::HandleConfig->commit; my($dist, $locallib); $locallib = CPAN::Shell->expand('Module', 'local::lib'); if ( $locallib and $dist = $locallib->distribution ) { # this is a hack to force bootstrapping $dist->{prefs}{pl}{commandline} = "$^X Makefile.PL --bootstrap"; # Set @INC for this process so we find things as they bootstrap require lib; lib->import(_local_lib_inc_path()); eval { $dist->install }; } if ( ! $dist || (my $err = $@) ) { $err ||= 'Could not locate local::lib in the CPAN index'; $CPAN::Frontend->mywarn("Error bootstrapping local::lib: $@\n"); $CPAN::Frontend->myprint("From the CPAN Shell, you might try 'look local::lib' and \n" . "run 'perl Makefile --bootstrap' and see if that is successful. Then\n" . "restart your CPAN client\n" ); } else { _local_lib_config(); } } } # install_help is temporary for configuration and not saved delete $CPAN::Config->{install_help}; $CPAN::Frontend->myprint("\n"); if ($matcher && !$CPAN::Config->{auto_commit}) { $CPAN::Frontend->myprint("Please remember to call 'o conf commit' to ". "make the config permanent!\n"); } else { CPAN::HandleConfig->commit; } if (! $matcher) { $CPAN::Frontend->myprint( "\nYou can re-run configuration any time with 'o conf init' in the CPAN shell\n" ); } } sub _local_lib_config { # Set environment stuff for this process require local::lib; # Tell user about environment vars to set $CPAN::Frontend->myprint($prompts{local_lib_installed}); local $ENV{SHELL} = $CPAN::Config->{shell} || $ENV{SHELL}; my $shellvars = local::lib->environment_vars_string_for(_local_lib_path()); $CPAN::Frontend->myprint($shellvars); # Set %ENV after getting string above my %env = local::lib->build_environment_vars_for(_local_lib_path(), 1); while ( my ($k, $v) = each %env ) { $ENV{$k} = $v; } # Offer to mangle the shell config my $munged_rc; if ( my $rc = _find_shell_config() ) { local $auto_config = 0; # We *must* ask, even under autoconfig local *_real_prompt; # We *must* show prompt my $_conf = prompt( "\nWould you like me to append that to $rc now?", "yes" ); if ($_conf =~ /^y/i) { open my $fh, ">>", $rc; print {$fh} "\n$shellvars"; close $fh; $munged_rc++; } } # Warn at exit time if ($munged_rc) { push @{$CPAN::META->_exit_messages}, << "HERE"; *** Remember to restart your shell before running cpan again *** HERE } else { push @{$CPAN::META->_exit_messages}, << "HERE"; *** Remember to add these environment variables to your shell config and restart your shell before running cpan again *** $shellvars HERE } } { my %shell_rc_map = ( map { $_ => ".${_}rc" } qw/ bash tcsh csh /, map { $_ => ".profile" } qw/dash ash sh/, zsh => ".zshenv", ); sub _find_shell_config { my $shell = File::Basename::basename($CPAN::Config->{shell}); if ( my $rc = $shell_rc_map{$shell} ) { my $path = File::Spec->catfile($ENV{HOME}, $rc); return $path if -w $path; } } } sub _local_lib_inc_path { return File::Spec->catdir(_local_lib_path(), qw/lib perl5/); } sub _local_lib_path { return File::Spec->catdir(_local_lib_home(), 'perl5'); } # Adapted from resolve_home_path() in local::lib -- this is where # local::lib thinks the user's home is { my $local_lib_home; sub _local_lib_home { $local_lib_home ||= File::Spec->rel2abs( do { if ($CPAN::META->has_usable("File::HomeDir") && File::HomeDir->VERSION >= 0.65) { File::HomeDir->my_home; } elsif (defined $ENV{HOME}) { $ENV{HOME}; } else { (getpwuid $<)[7] || "~"; } }); } } sub _do_pick_mirrors { local *_real_prompt; *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; $CPAN::Frontend->myprint($prompts{urls_intro}); # Only prompt for auto-pick if Net::Ping is new enough to do timings my $_conf = 'n'; if ( $CPAN::META->has_usable("Net::Ping") && CPAN::Version->vgt(Net::Ping->VERSION, '2.13')) { $_conf = prompt($prompts{auto_pick}, "yes"); } else { prompt("Autoselection disabled due to Net::Ping missing or insufficient. Please press ENTER"); } my @old_list = @{ $CPAN::Config->{urllist} }; if ( $_conf =~ /^y/i ) { conf_sites( auto_pick => 1 ) or bring_your_own(); } else { _print_urllist('Current') if @old_list; my $msg = scalar @old_list ? "\nWould you like to edit the urllist or pick new mirrors from a list?" : "\nWould you like to pick from the CPAN mirror list?" ; my $_conf = prompt($msg, "yes"); if ( $_conf =~ /^y/i ) { conf_sites(); } bring_your_own(); } _print_urllist('New'); } sub _init_external_progs { my($matcher,$args) = @_; my $PATH = $args->{path}; my @external_progs = @{ $args->{progs} }; my $shortcut = $args->{shortcut}; my $showed_make_warning; if (!$matcher or "@external_progs" =~ /$matcher/) { my $old_warn = $^W; local $^W if $^O eq 'MacOS'; local $^W = $old_warn; my $progname; for $progname (@external_progs) { next if $matcher && $progname !~ /$matcher/; if ($^O eq 'MacOS') { $CPAN::Config->{$progname} = 'not_here'; next; } my $progcall = $progname; unless ($matcher) { # we really don't need ncftp if we have ncftpget, but # if they chose this dialog via matcher, they shall have it next if $progname eq "ncftp" && $CPAN::Config->{ncftpget} gt " "; } my $path = $CPAN::Config->{$progname} || $Config::Config{$progname} || ""; if (File::Spec->file_name_is_absolute($path)) { # testing existence is not good enough, some have these exe # extensions # warn "Warning: configured $path does not exist\n" unless -e $path; # $path = ""; } elsif ($path =~ /^\s+$/) { # preserve disabled programs } else { $path = ''; } unless ($path) { # e.g. make -> nmake $progcall = $Config::Config{$progname} if $Config::Config{$progname}; } $path ||= find_exe($progcall,$PATH); unless ($path) { # not -e $path, because find_exe already checked that local $"=";"; $CPAN::Frontend->mywarn("Warning: $progcall not found in PATH[@$PATH]\n") unless $auto_config; _beg_for_make(), $showed_make_warning++ if $progname eq "make"; } $prompts{$progname} = "Where is your $progname program?"; $path = my_dflt_prompt($progname,$path,$matcher,1); # 1 => no strip spaces my $disabling = $path =~ m/^\s*$/; # don't let them disable or misconfigure make without warning if ( $progname eq "make" && ( $disabling || ! _check_found($path) ) ) { if ( $disabling && $showed_make_warning ) { next; } else { _beg_for_make() unless $showed_make_warning++; undef $CPAN::Config->{$progname}; $CPAN::Frontend->mywarn("Press SPACE and ENTER to disable make (NOT RECOMMENDED)\n"); redo; } } elsif ( $disabling ) { next; } elsif ( _check_found( $CPAN::Config->{$progname} ) ) { last if $shortcut && !$matcher; } else { undef $CPAN::Config->{$progname}; $CPAN::Frontend->mywarn("Press SPACE and ENTER to disable $progname\n"); redo; } } } } sub _check_found { my ($prog) = @_; if ( ! -f $prog ) { $CPAN::Frontend->mywarn("Warning: '$prog' does not exist\n") unless $auto_config; return; } elsif ( ! -x $prog ) { $CPAN::Frontend->mywarn("Warning: '$prog' is not executable\n") unless $auto_config; return; } return 1; } sub _beg_for_make { $CPAN::Frontend->mywarn(<<"HERE"); ALERT: 'make' is an essential tool for building perl Modules. Please make sure you have 'make' (or some equivalent) working. HERE if ($^O eq "MSWin32") { $CPAN::Frontend->mywarn(<<"HERE"); Windows users may want to follow this procedure when back in the CPAN shell: look YVES/scripts/alien_nmake.pl perl alien_nmake.pl This will install nmake on your system which can be used as a 'make' substitute. HERE } $CPAN::Frontend->mywarn(<<"HERE"); You can then retry the 'make' configuration step with o conf init make HERE } sub init_cpan_home { my($matcher) = @_; if (!$matcher or 'cpan_home' =~ /$matcher/) { my $cpan_home = $CPAN::Config->{cpan_home} || CPAN::HandleConfig::cpan_home(); if (-d $cpan_home) { $CPAN::Frontend->myprint( "\nI see you already have a directory\n" . "\n$cpan_home\n" . "Shall we use it as the general CPAN build and cache directory?\n\n" ) unless $auto_config; } else { # no cpan-home, must prompt and get one $CPAN::Frontend->myprint($prompts{cpan_home_where}) unless $auto_config; } my $default = $cpan_home; my $loop = 0; my($last_ans,$ans); $CPAN::Frontend->myprint(" \n") unless $auto_config; PROMPT: while ($ans = prompt("CPAN build and cache directory?",$default)) { if (File::Spec->file_name_is_absolute($ans)) { my @cpan_home = split /[\/\\]/, $ans; DIR: for my $dir (@cpan_home) { if ($dir =~ /^~/ and (!$last_ans or $ans ne $last_ans)) { $CPAN::Frontend ->mywarn("Warning: a tilde in the path will be ". "taken as a literal tilde. Please ". "confirm again if you want to keep it\n"); $last_ans = $default = $ans; next PROMPT; } } } else { require Cwd; my $cwd = Cwd::cwd(); my $absans = File::Spec->catdir($cwd,$ans); $CPAN::Frontend->mywarn("The path '$ans' is not an ". "absolute path. Please specify ". "an absolute path\n"); $default = $absans; next PROMPT; } eval { File::Path::mkpath($ans); }; # dies if it can't if ($@) { $CPAN::Frontend->mywarn("Couldn't create directory $ans.\n". "Please retry.\n"); next PROMPT; } if (-d $ans && -w _) { last PROMPT; } else { $CPAN::Frontend->mywarn("Couldn't find directory $ans\n". "or directory is not writable. Please retry.\n"); if (++$loop > 5) { $CPAN::Frontend->mydie("Giving up"); } } } $CPAN::Config->{cpan_home} = $ans; } } sub my_dflt_prompt { my ($item, $dflt, $m, $no_strip) = @_; my $default = $CPAN::Config->{$item} || $dflt; if (!$auto_config && (!$m || $item =~ /$m/)) { if (my $intro = $prompts{$item . "_intro"}) { $CPAN::Frontend->myprint($intro); } $CPAN::Frontend->myprint(" <$item>\n"); $CPAN::Config->{$item} = $no_strip ? prompt_no_strip($prompts{$item}, $default) : prompt( $prompts{$item}, $default); } else { $CPAN::Config->{$item} = $default; } return $CPAN::Config->{$item}; } sub my_yn_prompt { my ($item, $dflt, $m) = @_; my $default; defined($default = $CPAN::Config->{$item}) or $default = $dflt; if (!$auto_config && (!$m || $item =~ /$m/)) { if (my $intro = $prompts{$item . "_intro"}) { $CPAN::Frontend->myprint($intro); } $CPAN::Frontend->myprint(" <$item>\n"); my $ans = prompt($prompts{$item}, $default ? 'yes' : 'no'); $CPAN::Config->{$item} = ($ans =~ /^[y1]/i ? 1 : 0); } else { $CPAN::Config->{$item} = $default; } } sub my_prompt_loop { my ($item, $dflt, $m, $ok) = @_; my $default = $CPAN::Config->{$item} || $dflt; my $ans; if (!$auto_config && (!$m || $item =~ /$m/)) { my $intro = $prompts{$item . "_intro"}; $CPAN::Frontend->myprint($intro) if defined $intro; $CPAN::Frontend->myprint(" <$item>\n"); do { $ans = prompt($prompts{$item}, $default); } until $ans =~ /$ok/; $CPAN::Config->{$item} = $ans; } else { $CPAN::Config->{$item} = $default; } } # Here's the logic about the MIRRORED.BY file. There are a number of scenarios: # (1) We have a cached MIRRORED.BY file # (1a) We're auto-picking # - Refresh it automatically if it's old # (1b) Otherwise, ask if using cached is ok. If old, default to no. # - If cached is not ok, get it from the Internet. If it succeeds we use # the new file. Otherwise, we use the old file. # (2) We don't have a copy at all # (2a) If we are allowed to connect, we try to get a new copy. If it succeeds, # we use it, otherwise, we warn about failure # (2b) If we aren't allowed to connect, sub conf_sites { my %args = @_; # auto pick implies using the internet $CPAN::Config->{connect_to_internet_ok} = 1 if $args{auto_pick}; my $m = 'MIRRORED.BY'; my $mby = File::Spec->catfile($CPAN::Config->{keep_source_where},$m); File::Path::mkpath(File::Basename::dirname($mby)); # Why are we using MIRRORED.BY from the current directory? # Is this for testing? -- dagolden, 2009-11-05 if (-f $mby && -f $m && -M $m < -M $mby) { require File::Copy; File::Copy::copy($m,$mby) or die "Could not update $mby: $!"; } local $^T = time; # if we have a cached copy is not older than 60 days, we either # use it or refresh it or fall back to it if the refresh failed. if ($mby && -f $mby && -s _ > 0 ) { my $very_old = (-M $mby > 60); my $mtime = localtime((stat _)[9]); # if auto_pick, refresh anything old automatically if ( $args{auto_pick} ) { if ( $very_old ) { $CPAN::Frontend->myprint(qq{Trying to refresh your mirror list\n}); eval { CPAN::FTP->localize($m,$mby,3,1) } or $CPAN::Frontend->myprint(qq{Refresh failed. Using the old cached copy instead.\n}); $CPAN::Frontend->myprint("\n"); } } else { my $prompt = qq{Found a cached mirror list as of $mtime If you'd like to just use the cached copy, answer 'yes', below. If you'd like an updated copy of the mirror list, answer 'no' and I'll get a fresh one from the Internet. Shall I use the cached mirror list?}; my $ans = prompt($prompt, $very_old ? "no" : "yes"); if ($ans =~ /^n/i) { $CPAN::Frontend->myprint(qq{Trying to refresh your mirror list\n}); # you asked for it from the Internet $CPAN::Config->{connect_to_internet_ok} = 1; eval { CPAN::FTP->localize($m,$mby,3,1) } or $CPAN::Frontend->myprint(qq{Refresh failed. Using the old cached copy instead.\n}); $CPAN::Frontend->myprint("\n"); } } } # else there is no cached copy and we must fetch or fail else { # If they haven't agree to connect to the internet, ask again if ( ! $CPAN::Config->{connect_to_internet_ok} ) { my $prompt = q{You are missing a copy of the CPAN mirror list. May I connect to the Internet to get it?}; my $ans = prompt($prompt, "yes"); if ($ans =~ /^y/i) { $CPAN::Config->{connect_to_internet_ok} = 1; } } # Now get it from the Internet or complain if ( $CPAN::Config->{connect_to_internet_ok} ) { $CPAN::Frontend->myprint(qq{Trying to fetch a mirror list from the Internet\n}); eval { CPAN::FTP->localize($m,$mby,3,1) } or $CPAN::Frontend->mywarn(<<'HERE'); We failed to get a copy of the mirror list from the Internet. You will need to provide CPAN mirror URLs yourself. HERE $CPAN::Frontend->myprint("\n"); } else { $CPAN::Frontend->mywarn(<<'HERE'); You will need to provide CPAN mirror URLs yourself or set 'o conf connect_to_internet_ok 1' and try again. HERE } } # if we finally have a good local MIRRORED.BY, get on with picking if (-f $mby && -s _ > 0){ $CPAN::Config->{urllist} = $args{auto_pick} ? auto_mirrored_by($mby) : choose_mirrored_by($mby); return 1; } return; } sub find_exe { my($exe,$path) = @_; $path ||= [split /$Config{'path_sep'}/, $ENV{'PATH'}]; my($dir); #warn "in find_exe exe[$exe] path[@$path]"; for $dir (@$path) { my $abs = File::Spec->catfile($dir,$exe); if (($abs = MM->maybe_command($abs))) { return $abs; } } } sub picklist { my($items,$prompt,$default,$require_nonempty,$empty_warning)=@_; CPAN->debug("picklist('$items','$prompt','$default','$require_nonempty',". "'$empty_warning')") if $CPAN::DEBUG; $default ||= ''; my $pos = 0; my @nums; SELECTION: while (1) { # display, at most, 15 items at a time my $limit = $#{ $items } - $pos; $limit = 15 if $limit > 15; # show the next $limit items, get the new position $pos = display_some($items, $limit, $pos, $default); $pos = 0 if $pos >= @$items; my $num = prompt($prompt,$default); @nums = split (' ', $num); { my %seen; @nums = grep { !$seen{$_}++ } @nums; } my $i = scalar @$items; unrangify(\@nums); if (0 == @nums) { # cannot allow nothing because nothing means paging! # return; } elsif (grep (/\D/ || $_ < 1 || $_ > $i, @nums)) { $CPAN::Frontend->mywarn("invalid items entered, try again\n"); if ("@nums" =~ /\D/) { $CPAN::Frontend->mywarn("(we are expecting only numbers between 1 and $i)\n"); } next SELECTION; } if ($require_nonempty && !@nums) { $CPAN::Frontend->mywarn("$empty_warning\n"); } # a blank line continues... unless (@nums){ $CPAN::Frontend->mysleep(0.1); # prevent hot spinning process on the next bug next SELECTION; } last; } for (@nums) { $_-- } @{$items}[@nums]; } sub unrangify ($) { my($nums) = $_[0]; my @nums2 = (); while (@{$nums||[]}) { my $n = shift @$nums; if ($n =~ /^(\d+)-(\d+)$/) { my @range = $1 .. $2; # warn "range[@range]"; push @nums2, @range; } else { push @nums2, $n; } } push @$nums, @nums2; } sub display_some { my ($items, $limit, $pos, $default) = @_; $pos ||= 0; my @displayable = @$items[$pos .. ($pos + $limit)]; for my $item (@displayable) { $CPAN::Frontend->myprint(sprintf "(%d) %s\n", ++$pos, $item); } my $hit_what = $default ? "SPACE ENTER" : "ENTER"; $CPAN::Frontend->myprint(sprintf("%d more items, hit %s to show them\n", (@$items - $pos), $hit_what, )) if $pos < @$items; return $pos; } sub auto_mirrored_by { my $local = shift or return; local $|=1; $CPAN::Frontend->myprint("Looking for CPAN mirrors near you (please be patient)\n"); my $mirrors = CPAN::Mirrors->new($local); my $cnt = 0; my $callback_was_active = 0; my @best = $mirrors->best_mirrors( how_many => 3, callback => sub { $callback_was_active++; $CPAN::Frontend->myprint("."); if ($cnt++>60) { $cnt=0; $CPAN::Frontend->myprint("\n"); } }, $CPAN::Config->{urllist_ping_external} ? (external_ping => 1) : (), $CPAN::Config->{urllist_ping_verbose} ? (verbose => 1) : (), ); my $urllist = [ map { $_->http } grep { $_ && ref $_ && $_->can('http') } @best ]; push @$urllist, grep { /^file:/ } @{$CPAN::Config->{urllist}}; $CPAN::Frontend->myprint(" done!\n\n") if $callback_was_active; return $urllist } sub choose_mirrored_by { my $local = shift or return; my ($default); my $mirrors = CPAN::Mirrors->new($local); my @previous_urls = @{$CPAN::Config->{urllist}}; $CPAN::Frontend->myprint($prompts{urls_picker_intro}); my (@cont, $cont, %cont, @countries, @urls, %seen); my $no_previous_warn = "Sorry! since you don't have any existing picks, you must make a\n" . "geographic selection."; my $offer_cont = [sort $mirrors->continents]; if (@previous_urls) { push @$offer_cont, "(edit previous picks)"; $default = @$offer_cont; } else { # cannot allow nothing because nothing means paging! # push @$offer_cont, "(none of the above)"; } @cont = picklist($offer_cont, "Select your continent (or several nearby continents)", $default, ! @previous_urls, $no_previous_warn); # cannot allow nothing because nothing means paging! # return unless @cont; foreach $cont (@cont) { my @c = sort $mirrors->countries($cont); @cont{@c} = map ($cont, 0..$#c); @c = map ("$_ ($cont)", @c) if @cont > 1; push (@countries, @c); } if (@previous_urls && @countries) { push @countries, "(edit previous picks)"; $default = @countries; } if (@countries) { @countries = picklist (\@countries, "Select your country (or several nearby countries)", $default, ! @previous_urls, $no_previous_warn); %seen = map (($_ => 1), @previous_urls); # hmmm, should take list of defaults from CPAN::Config->{'urllist'}... foreach my $country (@countries) { next if $country =~ /edit previous picks/; (my $bare_country = $country) =~ s/ \(.*\)//; my @u; for my $m ( $mirrors->mirrors($bare_country) ) { push @u, $m->ftp if $m->ftp; push @u, $m->http if $m->http; } @u = grep (! $seen{$_}, @u); @u = map ("$_ ($bare_country)", @u) if @countries > 1; push (@urls, sort @u); } } push (@urls, map ("$_ (previous pick)", @previous_urls)); my $prompt = "Select as many URLs as you like (by number), put them on one line, separated by blanks, hyphenated ranges allowed e.g. '1 4 5' or '7 1-4 8'"; if (@previous_urls) { $default = join (' ', ((scalar @urls) - (scalar @previous_urls) + 1) .. (scalar @urls)); $prompt .= "\n(or just hit ENTER to keep your previous picks)"; } @urls = picklist (\@urls, $prompt, $default); foreach (@urls) { s/ \(.*\)//; } return [ @urls ]; } sub bring_your_own { my $urllist = [ @{$CPAN::Config->{urllist}} ]; my %seen = map (($_ => 1), @$urllist); my($ans,@urls); my $eacnt = 0; # empty answers $CPAN::Frontend->myprint(<<'HERE'); Now you can enter your own CPAN URLs by hand. A local CPAN mirror can be listed using a 'file:' URL like 'file:///path/to/cpan/' HERE do { my $prompt = "Enter another URL or ENTER to quit:"; unless (%seen) { $prompt = qq{CPAN.pm needs at least one URL where it can fetch CPAN files from. Please enter your CPAN site:}; } $ans = prompt ($prompt, ""); if ($ans) { $ans =~ s|/?\z|/|; # has to end with one slash # XXX This manipulation is odd. Shouldn't we check that $ans is # a directory before converting to file:///? And we need /// below, # too, don't we? -- dagolden, 2009-11-05 $ans = "file:$ans" unless $ans =~ /:/; # without a scheme is a file: if ($ans =~ /^\w+:\/./) { push @urls, $ans unless $seen{$ans}++; } else { $CPAN::Frontend-> myprint(sprintf(qq{"%s" doesn\'t look like an URL at first sight. I\'ll ignore it for now. You can add it to your %s later if you\'re sure it\'s right.\n}, $ans, $INC{'CPAN/MyConfig.pm'} || $INC{'CPAN/Config.pm'} || "configuration file", )); } } else { if (++$eacnt >= 5) { $CPAN::Frontend-> mywarn("Giving up.\n"); $CPAN::Frontend->mysleep(5); return; } } } while $ans || !%seen; @$urllist = CPAN::_uniq(@$urllist, @urls); $CPAN::Config->{urllist} = $urllist; } sub _print_urllist { my ($which) = @_; $CPAN::Frontend->myprint("$which urllist\n"); for ( @{$CPAN::Config->{urllist} || []} ) { $CPAN::Frontend->myprint(" $_\n") }; } sub _can_write_to_libdirs { return -w $Config{installprivlib} && -w $Config{installarchlib} && -w $Config{installsitelib} && -w $Config{installsitearch} } sub _using_installbase { return 1 if $ENV{PERL_MM_OPT} && $ENV{PERL_MM_OPT} =~ /install_base/i; return 1 if grep { ($CPAN::Config->{$_}||q{}) =~ /install_base/i } qw(makepl_arg make_install_arg mbuildpl_arg mbuild_install_arg); return; } sub _using_sudo { return 1 if grep { ($CPAN::Config->{$_}||q{}) =~ /sudo/ } qw(make_install_make_command mbuild_install_build_command); return; } sub _strip_spaces { $_[0] =~ s/^\s+//; # no leading spaces $_[0] =~ s/\s+\z//; # no trailing spaces } sub prompt ($;$) { unless (defined &_real_prompt) { *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; } my $ans = _real_prompt(@_); _strip_spaces($ans); $CPAN::Frontend->myprint("\n") unless $auto_config; return $ans; } sub prompt_no_strip ($;$) { unless (defined &_real_prompt) { *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; } return _real_prompt(@_); } 1; CPAN/Debug.pm000044400000004066151112047420006652 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- package CPAN::Debug; use strict; use vars qw($VERSION); $VERSION = "5.5001"; # module is internal to CPAN.pm %CPAN::DEBUG = qw[ CPAN 1 Index 2 InfoObj 4 Author 8 Distribution 16 Bundle 32 Module 64 CacheMgr 128 Complete 256 FTP 512 Shell 1024 Eval 2048 HandleConfig 4096 Tarzip 8192 Version 16384 Queue 32768 FirstTime 65536 ]; $CPAN::DEBUG ||= 0; #-> sub CPAN::Debug::debug ; sub debug { my($self,$arg) = @_; my @caller; my $i = 0; while () { my(@c) = (caller($i))[0 .. ($i ? 3 : 2)]; last unless defined $c[0]; push @caller, \@c; for (0,3) { last if $_ > $#c; $c[$_] =~ s/.*:://; } for (1) { $c[$_] =~ s|.*/||; } last if ++$i>=3; } pop @caller; if ($CPAN::DEBUG{$caller[0][0]} & $CPAN::DEBUG) { if ($arg and ref $arg) { eval { require Data::Dumper }; if ($@) { $CPAN::Frontend->myprint("Debug(\n" . $arg->as_string . ")\n"); } else { $CPAN::Frontend->myprint("Debug(\n" . Data::Dumper::Dumper($arg) . ")\n"); } } else { my $outer = ""; local $" = ","; if (@caller>1) { $outer = ",[@{$caller[1]}]"; } $CPAN::Frontend->myprint("Debug(@{$caller[0]}$outer): $arg\n"); } } } 1; __END__ =head1 NAME CPAN::Debug - internal debugging for CPAN.pm =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut CPAN/Author.pm000044400000015272151112047420007067 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Author; use strict; use CPAN::InfoObj; @CPAN::Author::ISA = qw(CPAN::InfoObj); use vars qw( $VERSION ); $VERSION = "5.5002"; package CPAN::Author; use strict; #-> sub CPAN::Author::force sub force { my $self = shift; $self->{force}++; } #-> sub CPAN::Author::force sub unforce { my $self = shift; delete $self->{force}; } #-> sub CPAN::Author::id sub id { my $self = shift; my $id = $self->{ID}; $CPAN::Frontend->mydie("Illegal author id[$id]") unless $id =~ /^[A-Z]/; $id; } #-> sub CPAN::Author::as_glimpse ; sub as_glimpse { my($self) = @_; my(@m); my $class = ref($self); $class =~ s/^CPAN:://; push @m, sprintf(qq{%-15s %s ("%s" <%s>)\n}, $class, $self->{ID}, $self->fullname, $self->email); join "", @m; } #-> sub CPAN::Author::fullname ; sub fullname { shift->ro->{FULLNAME}; } *name = \&fullname; #-> sub CPAN::Author::email ; sub email { shift->ro->{EMAIL}; } #-> sub CPAN::Author::ls ; sub ls { my $self = shift; my $glob = shift || ""; my $silent = shift || 0; my $id = $self->id; # adapted from CPAN::Distribution::verifyCHECKSUM ; my(@csf); # chksumfile @csf = $self->id =~ /(.)(.)(.*)/; $csf[1] = join "", @csf[0,1]; $csf[2] = join "", @csf[1,2]; # ("A","AN","ANDK") my(@dl); @dl = $self->dir_listing([$csf[0],"CHECKSUMS"], 0, 1); unless (grep {$_->[2] eq $csf[1]} @dl) { $CPAN::Frontend->myprint("Directory $csf[1]/ does not exist\n") unless $silent ; return; } @dl = $self->dir_listing([@csf[0,1],"CHECKSUMS"], 0, 1); unless (grep {$_->[2] eq $csf[2]} @dl) { $CPAN::Frontend->myprint("Directory $id/ does not exist\n") unless $silent; return; } @dl = $self->dir_listing([@csf,"CHECKSUMS"], 1, 1); if ($glob) { if ($CPAN::META->has_inst("Text::Glob")) { $glob =~ s|/$|/*|; my $rglob = Text::Glob::glob_to_regex($glob); CPAN->debug("glob[$glob]rglob[$rglob]dl[@dl]") if $CPAN::DEBUG; my @tmpdl = grep { $_->[2] =~ /$rglob/ } @dl; if (1==@tmpdl && $tmpdl[0][0]==0) { $rglob = Text::Glob::glob_to_regex("$glob/*"); @dl = grep { $_->[2] =~ /$rglob/ } @dl; } else { @dl = @tmpdl; } CPAN->debug("rglob[$rglob]dl[@dl]") if $CPAN::DEBUG; } else { $CPAN::Frontend->mydie("Text::Glob not installed, cannot proceed"); } } unless ($silent >= 2) { $CPAN::Frontend->myprint ( join "", map { sprintf ( "%8d %10s %s/%s%s\n", $_->[0], $_->[1], $id, $_->[2], 0==$_->[0]?"/":"", ) } sort { $a->[2] cmp $b->[2] } @dl ); } @dl; } # returns an array of arrays, the latter contain (size,mtime,filename) #-> sub CPAN::Author::dir_listing ; sub dir_listing { my $self = shift; my $chksumfile = shift; my $recursive = shift; my $may_ftp = shift; my $lc_want = File::Spec->catfile($CPAN::Config->{keep_source_where}, "authors", "id", @$chksumfile); my $fh; CPAN->debug("chksumfile[@$chksumfile]recursive[$recursive]may_ftp[$may_ftp]") if $CPAN::DEBUG; # Purge and refetch old (pre-PGP) CHECKSUMS; they are a security # hazard. (Without GPG installed they are not that much better, # though.) $fh = FileHandle->new; if (open($fh, $lc_want)) { my $line = <$fh>; close $fh; unlink($lc_want) unless $line =~ /PGP/; } local($") = "/"; # connect "force" argument with "index_expire". my $force = $self->{force}; if (my @stat = stat $lc_want) { $force ||= $stat[9] + $CPAN::Config->{index_expire}*86400 <= time; } my $lc_file; if ($may_ftp) { $lc_file = eval { CPAN::FTP->localize ( "authors/id/@$chksumfile", $lc_want, $force, ); }; unless ($lc_file) { $CPAN::Frontend->myprint("Trying $lc_want.gz\n"); $chksumfile->[-1] .= ".gz"; $lc_file = eval { CPAN::FTP->localize ("authors/id/@$chksumfile", "$lc_want.gz", 1, ); }; if ($lc_file) { $lc_file =~ s{\.gz(?!\n)\Z}{}; #}; eval{CPAN::Tarzip->new("$lc_file.gz")->gunzip($lc_file)}; } else { return; } } } else { $lc_file = $lc_want; # we *could* second-guess and if the user has a file: URL, # then we could look there. But on the other hand, if they do # have a file: URL, why did they choose to set # $CPAN::Config->{show_upload_date} to false? } # adapted from CPAN::Distribution::CHECKSUM_check_file ; $fh = FileHandle->new; my($cksum); if (open $fh, $lc_file) { local($/); my $eval = <$fh>; $eval =~ s/\015?\012/\n/g; close $fh; my($compmt) = Safe->new(); $cksum = $compmt->reval($eval); if ($@) { rename $lc_file, "$lc_file.bad"; Carp::confess($@) if $@; } } elsif ($may_ftp) { Carp::carp ("Could not open '$lc_file' for reading."); } else { # Maybe should warn: "You may want to set show_upload_date to a true value" return; } my(@result,$f); for $f (sort keys %$cksum) { if (exists $cksum->{$f}{isdir}) { if ($recursive) { my(@dir) = @$chksumfile; pop @dir; push @dir, $f, "CHECKSUMS"; push @result, [ 0, "-", $f ]; push @result, map { [$_->[0], $_->[1], "$f/$_->[2]"] } $self->dir_listing(\@dir,1,$may_ftp); } else { push @result, [ 0, "-", $f ]; } } else { push @result, [ ($cksum->{$f}{"size"}||0), $cksum->{$f}{"mtime"}||"---", $f ]; } } @result; } #-> sub CPAN::Author::reports sub reports { $CPAN::Frontend->mywarn("reports on authors not implemented. Please file a bugreport if you need this.\n"); } 1; CPAN/LWP/UserAgent.pm000044400000003762151112047420010165 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::LWP::UserAgent; use strict; use vars qw(@ISA $USER $PASSWD $SETUPDONE); use CPAN::HTTP::Credentials; # we delay requiring LWP::UserAgent and setting up inheritance until we need it $CPAN::LWP::UserAgent::VERSION = $CPAN::LWP::UserAgent::VERSION = "1.9601"; sub config { return if $SETUPDONE; if ($CPAN::META->has_usable('LWP::UserAgent')) { require LWP::UserAgent; @ISA = qw(Exporter LWP::UserAgent); ## no critic $SETUPDONE++; } else { $CPAN::Frontend->mywarn(" LWP::UserAgent not available\n"); } } sub get_basic_credentials { my($self, $realm, $uri, $proxy) = @_; if ( $proxy ) { return CPAN::HTTP::Credentials->get_proxy_credentials(); } else { return CPAN::HTTP::Credentials->get_non_proxy_credentials(); } } sub no_proxy { my ( $self, $no_proxy ) = @_; return $self->SUPER::no_proxy( split(',',$no_proxy) ); } # mirror(): Its purpose is to deal with proxy authentication. When we # call SUPER::mirror, we really call the mirror method in # LWP::UserAgent. LWP::UserAgent will then call # $self->get_basic_credentials or some equivalent and this will be # $self->dispatched to our own get_basic_credentials method. # Our own get_basic_credentials sets $USER and $PASSWD, two globals. # 407 stands for HTTP_PROXY_AUTHENTICATION_REQUIRED. Which means # although we have gone through our get_basic_credentials, the proxy # server refuses to connect. This could be a case where the username or # password has changed in the meantime, so I'm trying once again without # $USER and $PASSWD to give the get_basic_credentials routine another # chance to set $USER and $PASSWD. sub mirror { my($self,$url,$aslocal) = @_; my $result = $self->SUPER::mirror($url,$aslocal); if ($result->code == 407) { CPAN::HTTP::Credentials->clear_credentials; $result = $self->SUPER::mirror($url,$aslocal); } $result; } 1; CPAN/Prompt.pm000044400000001067151112047420007103 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Prompt; use overload '""' => "as_string"; use vars qw($prompt); use vars qw( $VERSION ); $VERSION = "5.5"; $prompt = "cpan> "; $CPAN::CurrentCommandId ||= 0; sub new { bless {}, shift; } sub as_string { my $word = "cpan"; unless ($CPAN::META->{LOCK}) { $word = "nolock_cpan"; } if ($CPAN::Config->{commandnumber_in_prompt}) { sprintf "$word\[%d]> ", $CPAN::CurrentCommandId; } else { "$word> "; } } 1; CPAN/Module.pm000044400000053566151112047420007062 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Module; use strict; @CPAN::Module::ISA = qw(CPAN::InfoObj); use vars qw( $VERSION ); $VERSION = "5.5003"; BEGIN { # alarm() is not implemented in perl 5.6.x and earlier under Windows *ALARM_IMPLEMENTED = sub () { $] >= 5.007 || $^O !~ /MSWin/ }; } # Accessors #-> sub CPAN::Module::userid sub userid { my $self = shift; my $ro = $self->ro; return unless $ro; return $ro->{userid} || $ro->{CPAN_USERID}; } #-> sub CPAN::Module::description sub description { my $self = shift; my $ro = $self->ro or return ""; $ro->{description} } #-> sub CPAN::Module::distribution sub distribution { my($self) = @_; CPAN::Shell->expand("Distribution",$self->cpan_file); } #-> sub CPAN::Module::_is_representative_module sub _is_representative_module { my($self) = @_; return $self->{_is_representative_module} if defined $self->{_is_representative_module}; my $pm = $self->cpan_file or return $self->{_is_representative_module} = 0; $pm =~ s|.+/||; $pm =~ s{\.(?:tar\.(bz2|gz|Z)|t(?:gz|bz)|zip)$}{}i; # see base_id $pm =~ s|-\d+\.\d+.+$||; $pm =~ s|-[\d\.]+$||; $pm =~ s/-/::/g; $self->{_is_representative_module} = $pm eq $self->{ID} ? 1 : 0; # warn "DEBUG: $pm eq $self->{ID} => $self->{_is_representative_module}"; $self->{_is_representative_module}; } #-> sub CPAN::Module::undelay sub undelay { my $self = shift; delete $self->{later}; if ( my $dist = CPAN::Shell->expand("Distribution", $self->cpan_file) ) { $dist->undelay; } } # mark as dirty/clean #-> sub CPAN::Module::color_cmd_tmps ; sub color_cmd_tmps { my($self) = shift; my($depth) = shift || 0; my($color) = shift || 0; my($ancestors) = shift || []; # a module needs to recurse to its cpan_file return if exists $self->{incommandcolor} && $color==1 && $self->{incommandcolor}==$color; return if $color==0 && !$self->{incommandcolor}; if ($color>=1) { if ( $self->uptodate ) { $self->{incommandcolor} = $color; return; } elsif (my $have_version = $self->available_version) { # maybe what we have is good enough if (@$ancestors) { my $who_asked_for_me = $ancestors->[-1]; my $obj = CPAN::Shell->expandany($who_asked_for_me); if (0) { } elsif ($obj->isa("CPAN::Bundle")) { # bundles cannot specify a minimum version return; } elsif ($obj->isa("CPAN::Distribution")) { if (my $prereq_pm = $obj->prereq_pm) { for my $k (keys %$prereq_pm) { if (my $want_version = $prereq_pm->{$k}{$self->id}) { if (CPAN::Version->vcmp($have_version,$want_version) >= 0) { $self->{incommandcolor} = $color; return; } } } } } } } } else { $self->{incommandcolor} = $color; # set me before recursion, # so we can break it } if ($depth>=$CPAN::MAX_RECURSION) { my $e = CPAN::Exception::RecursiveDependency->new($ancestors); if ($e->is_resolvable) { return $self->{incommandcolor}=2; } else { die $e; } } # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; if ( my $dist = CPAN::Shell->expand("Distribution", $self->cpan_file) ) { $dist->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); } # unreached code? # if ($color==0) { # delete $self->{badtestcnt}; # } $self->{incommandcolor} = $color; } #-> sub CPAN::Module::as_glimpse ; sub as_glimpse { my($self) = @_; my(@m); my $class = ref($self); $class =~ s/^CPAN:://; my $color_on = ""; my $color_off = ""; if ( $CPAN::Shell::COLOR_REGISTERED && $CPAN::META->has_inst("Term::ANSIColor") && $self->description ) { $color_on = Term::ANSIColor::color("green"); $color_off = Term::ANSIColor::color("reset"); } my $uptodateness = " "; unless ($class eq "Bundle") { my $u = $self->uptodate; $uptodateness = $u ? "=" : "<" if defined $u; }; my $id = do { my $d = $self->distribution; $d ? $d -> pretty_id : $self->cpan_userid; }; push @m, sprintf("%-7s %1s %s%-22s%s (%s)\n", $class, $uptodateness, $color_on, $self->id, $color_off, $id, ); join "", @m; } #-> sub CPAN::Module::dslip_status sub dslip_status { my($self) = @_; my($stat); # development status @{$stat->{D}}{qw,i c a b R M S,} = qw,idea pre-alpha alpha beta released mature standard,; # support level @{$stat->{S}}{qw,m d u n a,} = qw,mailing-list developer comp.lang.perl.* none abandoned,; # language @{$stat->{L}}{qw,p c + o h,} = qw,perl C C++ other hybrid,; # interface @{$stat->{I}}{qw,f r O p h n,} = qw,functions references+ties object-oriented pragma hybrid none,; # public licence @{$stat->{P}}{qw,p g l b a 2 o d r n,} = qw,Standard-Perl GPL LGPL BSD Artistic Artistic_2 open-source distribution_allowed restricted_distribution no_licence,; for my $x (qw(d s l i p)) { $stat->{$x}{' '} = 'unknown'; $stat->{$x}{'?'} = 'unknown'; } my $ro = $self->ro; return +{} unless $ro && $ro->{statd}; return { D => $ro->{statd}, S => $ro->{stats}, L => $ro->{statl}, I => $ro->{stati}, P => $ro->{statp}, DV => $stat->{D}{$ro->{statd}}, SV => $stat->{S}{$ro->{stats}}, LV => $stat->{L}{$ro->{statl}}, IV => $stat->{I}{$ro->{stati}}, PV => $stat->{P}{$ro->{statp}}, }; } #-> sub CPAN::Module::as_string ; sub as_string { my($self) = @_; my(@m); CPAN->debug("$self entering as_string") if $CPAN::DEBUG; my $class = ref($self); $class =~ s/^CPAN:://; local($^W) = 0; push @m, $class, " id = $self->{ID}\n"; my $sprintf = " %-12s %s\n"; push @m, sprintf($sprintf, 'DESCRIPTION', $self->description) if $self->description; my $sprintf2 = " %-12s %s (%s)\n"; my($userid); $userid = $self->userid; if ( $userid ) { my $author; if ($author = CPAN::Shell->expand('Author',$userid)) { my $email = ""; my $m; # old perls if ($m = $author->email) { $email = " <$m>"; } push @m, sprintf( $sprintf2, 'CPAN_USERID', $userid, $author->fullname . $email ); } } push @m, sprintf($sprintf, 'CPAN_VERSION', $self->cpan_version) if $self->cpan_version; if (my $cpan_file = $self->cpan_file) { push @m, sprintf($sprintf, 'CPAN_FILE', $cpan_file); if (my $dist = CPAN::Shell->expand("Distribution",$cpan_file)) { my $upload_date = $dist->upload_date; if ($upload_date) { push @m, sprintf($sprintf, 'UPLOAD_DATE', $upload_date); } } } my $sprintf3 = " %-12s %1s%1s%1s%1s%1s (%s,%s,%s,%s,%s)\n"; my $dslip = $self->dslip_status; push @m, sprintf( $sprintf3, 'DSLIP_STATUS', @{$dslip}{qw(D S L I P DV SV LV IV PV)}, ) if $dslip->{D}; my $local_file = $self->inst_file; unless ($self->{MANPAGE}) { my $manpage; if ($local_file) { $manpage = $self->manpage_headline($local_file); } else { # If we have already untarred it, we should look there my $dist = $CPAN::META->instance('CPAN::Distribution', $self->cpan_file); # warn "dist[$dist]"; # mff=manifest file; mfh=manifest handle my($mff,$mfh); if ( $dist->{build_dir} and (-f ($mff = File::Spec->catfile($dist->{build_dir}, "MANIFEST"))) and $mfh = FileHandle->new($mff) ) { CPAN->debug("mff[$mff]") if $CPAN::DEBUG; my $lfre = $self->id; # local file RE $lfre =~ s/::/./g; $lfre .= "\\.pm\$"; my($lfl); # local file file local $/ = "\n"; my(@mflines) = <$mfh>; for (@mflines) { s/^\s+//; s/\s.*//s; } while (length($lfre)>5 and !$lfl) { ($lfl) = grep /$lfre/, @mflines; CPAN->debug("lfl[$lfl]lfre[$lfre]") if $CPAN::DEBUG; $lfre =~ s/.+?\.//; } $lfl =~ s/\s.*//; # remove comments $lfl =~ s/\s+//g; # chomp would maybe be too system-specific my $lfl_abs = File::Spec->catfile($dist->{build_dir},$lfl); # warn "lfl_abs[$lfl_abs]"; if (-f $lfl_abs) { $manpage = $self->manpage_headline($lfl_abs); } } } $self->{MANPAGE} = $manpage if $manpage; } my($item); for $item (qw/MANPAGE/) { push @m, sprintf($sprintf, $item, $self->{$item}) if exists $self->{$item}; } for $item (qw/CONTAINS/) { push @m, sprintf($sprintf, $item, join(" ",@{$self->{$item}})) if exists $self->{$item} && @{$self->{$item}}; } push @m, sprintf($sprintf, 'INST_FILE', $local_file || "(not installed)"); push @m, sprintf($sprintf, 'INST_VERSION', $self->inst_version) if $local_file; if (%{$CPAN::META->{is_tested}||{}}) { # XXX needs to be methodified somehow my $available_file = $self->available_file; if ($available_file && $available_file ne $local_file) { push @m, sprintf($sprintf, 'AVAILABLE_FILE', $available_file); push @m, sprintf($sprintf, 'AVAILABLE_VERSION', $self->available_version); } } join "", @m, "\n"; } #-> sub CPAN::Module::manpage_headline sub manpage_headline { my($self,$local_file) = @_; my(@local_file) = $local_file; $local_file =~ s/\.pm(?!\n)\Z/.pod/; push @local_file, $local_file; my(@result,$locf); for $locf (@local_file) { next unless -f $locf; my $fh = FileHandle->new($locf) or $Carp::Frontend->mydie("Couldn't open $locf: $!"); my $inpod = 0; local $/ = "\n"; while (<$fh>) { $inpod = m/^=(?!head1\s+NAME\s*$)/ ? 0 : m/^=head1\s+NAME\s*$/ ? 1 : $inpod; next unless $inpod; next if /^=/; next if /^\s+$/; chomp; push @result, $_; } close $fh; last if @result; } for (@result) { s/^\s+//; s/\s+$//; } join " ", @result; } #-> sub CPAN::Module::cpan_file ; # Note: also inherited by CPAN::Bundle sub cpan_file { my $self = shift; # CPAN->debug(sprintf "id[%s]", $self->id) if $CPAN::DEBUG; unless ($self->ro) { CPAN::Index->reload; } my $ro = $self->ro; if ($ro && defined $ro->{CPAN_FILE}) { return $ro->{CPAN_FILE}; } else { my $userid = $self->userid; if ( $userid ) { if ($CPAN::META->exists("CPAN::Author",$userid)) { my $author = $CPAN::META->instance("CPAN::Author", $userid); my $fullname = $author->fullname; my $email = $author->email; unless (defined $fullname && defined $email) { return sprintf("Contact Author %s", $userid, ); } return "Contact Author $fullname <$email>"; } else { return "Contact Author $userid (Email address not available)"; } } else { return "N/A"; } } } #-> sub CPAN::Module::cpan_version ; sub cpan_version { my $self = shift; my $ro = $self->ro; unless ($ro) { # Can happen with modules that are not on CPAN $ro = {}; } $ro->{CPAN_VERSION} = 'undef' unless defined $ro->{CPAN_VERSION}; $ro->{CPAN_VERSION}; } #-> sub CPAN::Module::force ; sub force { my($self) = @_; $self->{force_update} = 1; } #-> sub CPAN::Module::fforce ; sub fforce { my($self) = @_; $self->{force_update} = 2; } #-> sub CPAN::Module::notest ; sub notest { my($self) = @_; # $CPAN::Frontend->mywarn("XDEBUG: set notest for Module"); $self->{notest}++; } #-> sub CPAN::Module::rematein ; sub rematein { my($self,$meth) = @_; $CPAN::Frontend->myprint(sprintf("Running %s for module '%s'\n", $meth, $self->id)); my $cpan_file = $self->cpan_file; if ($cpan_file eq "N/A" || $cpan_file =~ /^Contact Author/) { $CPAN::Frontend->mywarn(sprintf qq{ The module %s isn\'t available on CPAN. Either the module has not yet been uploaded to CPAN, or it is temporary unavailable. Please contact the author to find out more about the status. Try 'i %s'. }, $self->id, $self->id, ); return; } my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); $pack->called_for($self->id); if (exists $self->{force_update}) { if ($self->{force_update} == 2) { $pack->fforce($meth); } else { $pack->force($meth); } } $pack->notest($meth) if exists $self->{notest} && $self->{notest}; $pack->{reqtype} ||= ""; CPAN->debug("dist-reqtype[$pack->{reqtype}]". "self-reqtype[$self->{reqtype}]") if $CPAN::DEBUG; if ($pack->{reqtype}) { if ($pack->{reqtype} eq "b" && $self->{reqtype} =~ /^[rc]$/) { $pack->{reqtype} = $self->{reqtype}; if ( exists $pack->{install} && ( UNIVERSAL::can($pack->{install},"failed") ? $pack->{install}->failed : $pack->{install} =~ /^NO/ ) ) { delete $pack->{install}; $CPAN::Frontend->mywarn ("Promoting $pack->{ID} from 'build_requires' to 'requires'"); } } } else { $pack->{reqtype} = $self->{reqtype}; } my $success = eval { $pack->$meth(); }; my $err = $@; $pack->unforce if $pack->can("unforce") && exists $self->{force_update}; $pack->unnotest if $pack->can("unnotest") && exists $self->{notest}; delete $self->{force_update}; delete $self->{notest}; if ($err) { die $err; } return $success; } #-> sub CPAN::Module::perldoc ; sub perldoc { shift->rematein('perldoc') } #-> sub CPAN::Module::readme ; sub readme { shift->rematein('readme') } #-> sub CPAN::Module::look ; sub look { shift->rematein('look') } #-> sub CPAN::Module::cvs_import ; sub cvs_import { shift->rematein('cvs_import') } #-> sub CPAN::Module::get ; sub get { shift->rematein('get',@_) } #-> sub CPAN::Module::make ; sub make { shift->rematein('make') } #-> sub CPAN::Module::test ; sub test { my $self = shift; # $self->{badtestcnt} ||= 0; $self->rematein('test',@_); } #-> sub CPAN::Module::deprecated_in_core ; sub deprecated_in_core { my ($self) = @_; return unless $CPAN::META->has_inst('Module::CoreList') && Module::CoreList->can('is_deprecated'); return Module::CoreList::is_deprecated($self->{ID}); } #-> sub CPAN::Module::inst_deprecated; # Indicates whether the *installed* version of the module is a deprecated *and* # installed as part of the Perl core library path sub inst_deprecated { my ($self) = @_; my $inst_file = $self->inst_file or return; return $self->deprecated_in_core && $self->_in_priv_or_arch($inst_file); } #-> sub CPAN::Module::uptodate ; sub uptodate { my ($self) = @_; local ($_); my $inst = $self->inst_version or return 0; my $cpan = $self->cpan_version; return 0 if CPAN::Version->vgt($cpan,$inst) || $self->inst_deprecated; CPAN->debug (join ("", "returning uptodate. ", "cpan[$cpan]inst[$inst]", )) if $CPAN::DEBUG; return 1; } # returns true if installed in privlib or archlib sub _in_priv_or_arch { my($self,$inst_file) = @_; foreach my $pair ( [qw(sitearchexp archlibexp)], [qw(sitelibexp privlibexp)] ) { my ($site, $priv) = @Config::Config{@$pair}; if ($^O eq 'VMS') { for my $d ($site, $priv) { $d = VMS::Filespec::unixify($d) }; } s!/*$!!g foreach $site, $priv; next if $site eq $priv; if ($priv eq substr($inst_file,0,length($priv))) { return 1; } } return 0; } #-> sub CPAN::Module::install ; sub install { my($self) = @_; my($doit) = 0; if ($self->uptodate && not exists $self->{force_update} ) { $CPAN::Frontend->myprint(sprintf("%s is up to date (%s).\n", $self->id, $self->inst_version, )); } else { $doit = 1; } my $ro = $self->ro; if ($ro && $ro->{stats} && $ro->{stats} eq "a") { $CPAN::Frontend->mywarn(qq{ \n\n\n ***WARNING*** The module $self->{ID} has no active maintainer (CPAN support level flag 'abandoned').\n\n\n }); $CPAN::Frontend->mysleep(5); } return $doit ? $self->rematein('install') : 1; } #-> sub CPAN::Module::clean ; sub clean { shift->rematein('clean') } #-> sub CPAN::Module::inst_file ; sub inst_file { my($self) = @_; $self->_file_in_path([@INC]); } #-> sub CPAN::Module::available_file ; sub available_file { my($self) = @_; my $sep = $Config::Config{path_sep}; my $perllib = $ENV{PERL5LIB}; $perllib = $ENV{PERLLIB} unless defined $perllib; my @perllib = split(/$sep/,$perllib) if defined $perllib; my @cpan_perl5inc; if ($CPAN::Perl5lib_tempfile) { my $yaml = CPAN->_yaml_loadfile($CPAN::Perl5lib_tempfile); @cpan_perl5inc = @{$yaml->[0]{inc} || []}; } $self->_file_in_path([@cpan_perl5inc,@perllib,@INC]); } #-> sub CPAN::Module::file_in_path ; sub _file_in_path { my($self,$path) = @_; my($dir,@packpath); @packpath = split /::/, $self->{ID}; $packpath[-1] .= ".pm"; if (@packpath == 1 && $packpath[0] eq "readline.pm") { unshift @packpath, "Term", "ReadLine"; # historical reasons } foreach $dir (@$path) { my $pmfile = File::Spec->catfile($dir,@packpath); if (-f $pmfile) { return $pmfile; } } return; } #-> sub CPAN::Module::xs_file ; sub xs_file { my($self) = @_; my($dir,@packpath); @packpath = split /::/, $self->{ID}; push @packpath, $packpath[-1]; $packpath[-1] .= "." . $Config::Config{'dlext'}; foreach $dir (@INC) { my $xsfile = File::Spec->catfile($dir,'auto',@packpath); if (-f $xsfile) { return $xsfile; } } return; } #-> sub CPAN::Module::inst_version ; sub inst_version { my($self) = @_; my $parsefile = $self->inst_file or return; my $have = $self->parse_version($parsefile); $have; } #-> sub CPAN::Module::inst_version ; sub available_version { my($self) = @_; my $parsefile = $self->available_file or return; my $have = $self->parse_version($parsefile); $have; } #-> sub CPAN::Module::parse_version ; sub parse_version { my($self,$parsefile) = @_; if (ALARM_IMPLEMENTED) { my $timeout = (exists($CPAN::Config{'version_timeout'})) ? $CPAN::Config{'version_timeout'} : 15; alarm($timeout); } my $have = eval { local $SIG{ALRM} = sub { die "alarm\n" }; MM->parse_version($parsefile); }; if ($@) { $CPAN::Frontend->mywarn("Error while parsing version number in file '$parsefile'\n"); } alarm(0) if ALARM_IMPLEMENTED; my $leastsanity = eval { defined $have && length $have; }; $have = "undef" unless $leastsanity; $have =~ s/^ //; # since the %vd hack these two lines here are needed $have =~ s/ $//; # trailing whitespace happens all the time $have = CPAN::Version->readable($have); $have =~ s/\s*//g; # stringify to float around floating point issues $have; # no stringify needed, \s* above matches always } #-> sub CPAN::Module::reports sub reports { my($self) = @_; $self->distribution->reports; } 1; CPAN/HTTP/Credentials.pm000044400000005111151112047420010630 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::HTTP::Credentials; use strict; use vars qw($USER $PASSWORD $PROXY_USER $PROXY_PASSWORD); $CPAN::HTTP::Credentials::VERSION = $CPAN::HTTP::Credentials::VERSION = "1.9601"; sub clear_credentials { clear_non_proxy_credentials(); clear_proxy_credentials(); } sub clear_non_proxy_credentials { undef $USER; undef $PASSWORD; } sub clear_proxy_credentials { undef $PROXY_USER; undef $PROXY_PASSWORD; } sub get_proxy_credentials { my $self = shift; if ($PROXY_USER && $PROXY_PASSWORD) { return ($PROXY_USER, $PROXY_PASSWORD); } if ( defined $CPAN::Config->{proxy_user} && $CPAN::Config->{proxy_user} ) { $PROXY_USER = $CPAN::Config->{proxy_user}; $PROXY_PASSWORD = $CPAN::Config->{proxy_pass} || ""; return ($PROXY_USER, $PROXY_PASSWORD); } my $username_prompt = "\nProxy authentication needed! (Note: to permanently configure username and password run o conf proxy_user your_username o conf proxy_pass your_password )\nUsername:"; ($PROXY_USER, $PROXY_PASSWORD) = _get_username_and_password_from_user($username_prompt); return ($PROXY_USER,$PROXY_PASSWORD); } sub get_non_proxy_credentials { my $self = shift; if ($USER && $PASSWORD) { return ($USER, $PASSWORD); } if ( defined $CPAN::Config->{username} ) { $USER = $CPAN::Config->{username}; $PASSWORD = $CPAN::Config->{password} || ""; return ($USER, $PASSWORD); } my $username_prompt = "\nAuthentication needed! (Note: to permanently configure username and password run o conf username your_username o conf password your_password )\nUsername:"; ($USER, $PASSWORD) = _get_username_and_password_from_user($username_prompt); return ($USER,$PASSWORD); } sub _get_username_and_password_from_user { my $username_message = shift; my ($username,$password); ExtUtils::MakeMaker->import(qw(prompt)); $username = prompt($username_message); if ($CPAN::META->has_inst("Term::ReadKey")) { Term::ReadKey::ReadMode("noecho"); } else { $CPAN::Frontend->mywarn( "Warning: Term::ReadKey seems not to be available, your password will be echoed to the terminal!\n" ); } $password = prompt("Password:"); if ($CPAN::META->has_inst("Term::ReadKey")) { Term::ReadKey::ReadMode("restore"); } $CPAN::Frontend->myprint("\n\n"); return ($username,$password); } 1; CPAN/HTTP/Client.pm000044400000017450151112047420007622 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::HTTP::Client; use strict; use vars qw(@ISA); use CPAN::HTTP::Credentials; use HTTP::Tiny 0.005; $CPAN::HTTP::Client::VERSION = $CPAN::HTTP::Client::VERSION = "1.9602"; # CPAN::HTTP::Client is adapted from parts of cpanm by Tatsuhiko Miyagawa # and parts of LWP by Gisle Aas sub new { my $class = shift; my %args = @_; for my $k ( keys %args ) { $args{$k} = '' unless defined $args{$k}; } $args{no_proxy} = [split(",", $args{no_proxy}) ] if $args{no_proxy}; return bless \%args, $class; } # This executes a request with redirection (up to 5) and returns the # response structure generated by HTTP::Tiny # # If authentication fails, it will attempt to get new authentication # information and repeat up to 5 times sub mirror { my($self, $uri, $path) = @_; my $want_proxy = $self->_want_proxy($uri); my $http = HTTP::Tiny->new( verify_SSL => 1, $want_proxy ? (proxy => $self->{proxy}) : () ); my ($response, %headers); my $retries = 0; while ( $retries++ < 5 ) { $response = $http->mirror( $uri, $path, {headers => \%headers} ); if ( $response->{status} eq '401' ) { last unless $self->_get_auth_params( $response, 'non_proxy' ); } elsif ( $response->{status} eq '407' ) { last unless $self->_get_auth_params( $response, 'proxy' ); } else { last; # either success or failure } my %headers = ( $self->_auth_headers( $uri, 'non_proxy' ), ( $want_proxy ? $self->_auth_headers($uri, 'proxy') : () ), ); } return $response; } sub _want_proxy { my ($self, $uri) = @_; return unless $self->{proxy}; my($host) = $uri =~ m|://([^/:]+)|; return ! grep { $host =~ /\Q$_\E$/ } @{ $self->{no_proxy} || [] }; } # Generates the authentication headers for a given mode # C is 'proxy' or 'non_proxy' # C<_${mode}_type> is 'basic' or 'digest' # C<_${mode}_params> will be the challenge parameters from the 401/407 headers sub _auth_headers { my ($self, $uri, $mode) = @_; # Get names for our mode-specific attributes my ($type_key, $param_key) = map {"_" . $mode . $_} qw/_type _params/; # If _prepare_auth has not been called, we can't prepare headers return unless $self->{$type_key}; # Get user credentials for mode my $cred_method = "get_" . ($mode ? "proxy" : "non_proxy") ."_credentials"; my ($user, $pass) = CPAN::HTTP::Credentials->$cred_method; # Generate the header for the mode & type my $header = $mode eq 'proxy' ? 'Proxy-Authorization' : 'Authorization'; my $value_method = "_" . $self->{$type_key} . "_auth"; my $value = $self->$value_method($user, $pass, $self->{$param_key}, $uri); # If we didn't get a value, we didn't have the right modules available return $value ? ( $header, $value ) : (); } # Extract authentication parameters from headers, but clear any prior # credentials if we failed (so we might prompt user for password again) sub _get_auth_params { my ($self, $response, $mode) = @_; my $prefix = $mode eq 'proxy' ? 'Proxy' : 'WWW'; my ($type_key, $param_key) = map {"_" . $mode . $_} qw/_type _params/; if ( ! $response->{success} ) { # auth failed my $method = "clear_${mode}_credentials"; CPAN::HTTP::Credentials->$method; delete $self->{$_} for $type_key, $param_key; } ($self->{$type_key}, $self->{$param_key}) = $self->_get_challenge( $response, "${prefix}-Authenticate"); return $self->{$type_key}; } # Extract challenge type and parameters for a challenge list sub _get_challenge { my ($self, $response, $auth_header) = @_; my $auth_list = $response->{headers}(lc $auth_header); return unless defined $auth_list; $auth_list = [$auth_list] unless ref $auth_list; for my $challenge (@$auth_list) { $challenge =~ tr/,/;/; # "," is used to separate auth-params!! ($challenge) = $self->split_header_words($challenge); my $scheme = shift(@$challenge); shift(@$challenge); # no value $challenge = { @$challenge }; # make rest into a hash unless ($scheme =~ /^(basic|digest)$/) { next; # bad scheme } $scheme = $1; # untainted now return ($scheme, $challenge); } return; } # Generate a basic authentication header value sub _basic_auth { my ($self, $user, $pass) = @_; unless ( $CPAN::META->has_usable('MIME::Base64') ) { $CPAN::Frontend->mywarn( "MIME::Base64 is required for 'Basic' style authentication" ); return; } return "Basic " . MIME::Base64::encode_base64("$user\:$pass", q{}); } # Generate a digest authentication header value sub _digest_auth { my ($self, $user, $pass, $auth_param, $uri) = @_; unless ( $CPAN::META->has_usable('Digest::MD5') ) { $CPAN::Frontend->mywarn( "Digest::MD5 is required for 'Digest' style authentication" ); return; } my $nc = sprintf "%08X", ++$self->{_nonce_count}{$auth_param->{nonce}}; my $cnonce = sprintf "%8x", time; my ($path) = $uri =~ m{^\w+?://[^/]+(/.*)$}; $path = "/" unless defined $path; my $md5 = Digest::MD5->new; my(@digest); $md5->add(join(":", $user, $auth_param->{realm}, $pass)); push(@digest, $md5->hexdigest); $md5->reset; push(@digest, $auth_param->{nonce}); if ($auth_param->{qop}) { push(@digest, $nc, $cnonce, ($auth_param->{qop} =~ m|^auth[,;]auth-int$|) ? 'auth' : $auth_param->{qop}); } $md5->add(join(":", 'GET', $path)); push(@digest, $md5->hexdigest); $md5->reset; $md5->add(join(":", @digest)); my($digest) = $md5->hexdigest; $md5->reset; my %resp = map { $_ => $auth_param->{$_} } qw(realm nonce opaque); @resp{qw(username uri response algorithm)} = ($user, $path, $digest, "MD5"); if (($auth_param->{qop} || "") =~ m|^auth([,;]auth-int)?$|) { @resp{qw(qop cnonce nc)} = ("auth", $cnonce, $nc); } my(@order) = qw(username realm qop algorithm uri nonce nc cnonce response opaque); my @pairs; for (@order) { next unless defined $resp{$_}; push(@pairs, "$_=" . qq("$resp{$_}")); } my $auth_value = "Digest " . join(", ", @pairs); return $auth_value; } # split_header_words adapted from HTTP::Headers::Util sub split_header_words { my ($self, @words) = @_; my @res = $self->_split_header_words(@words); for my $arr (@res) { for (my $i = @$arr - 2; $i >= 0; $i -= 2) { $arr->[$i] = lc($arr->[$i]); } } return @res; } sub _split_header_words { my($self, @val) = @_; my @res; for (@val) { my @cur; while (length) { if (s/^\s*(=*[^\s=;,]+)//) { # 'token' or parameter 'attribute' push(@cur, $1); # a quoted value if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) { my $val = $1; $val =~ s/\\(.)/$1/g; push(@cur, $val); # some unquoted value } elsif (s/^\s*=\s*([^;,\s]*)//) { my $val = $1; $val =~ s/\s+$//; push(@cur, $val); # no value, a lone token } else { push(@cur, undef); } } elsif (s/^\s*,//) { push(@res, [@cur]) if @cur; @cur = (); } elsif (s/^\s*;// || s/^\s+//) { # continue } else { die "This should not happen: '$_'"; } } push(@res, \@cur) if @cur; } @res; } 1; CPAN/Index.pm000044400000053334151112047420006675 0ustar00package CPAN::Index; use strict; use vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03 $HAVE_REANIMATED $VERSION); $VERSION = "2.29"; @CPAN::Index::ISA = qw(CPAN::Debug); $LAST_TIME ||= 0; $DATE_OF_03 ||= 0; # use constant PROTOCOL => "2.0"; # commented out to avoid warning on upgrade from 1.57 sub PROTOCOL { 2.0 } #-> sub CPAN::Index::force_reload ; sub force_reload { my($class) = @_; $CPAN::Index::LAST_TIME = 0; $class->reload(1); } my @indexbundle = ( { reader => "rd_authindex", dir => "authors", remotefile => '01mailrc.txt.gz', shortlocalfile => '01mailrc.gz', }, { reader => "rd_modpacks", dir => "modules", remotefile => '02packages.details.txt.gz', shortlocalfile => '02packag.gz', }, { reader => "rd_modlist", dir => "modules", remotefile => '03modlist.data.gz', shortlocalfile => '03mlist.gz', }, ); #-> sub CPAN::Index::reload ; sub reload { my($self,$force) = @_; my $time = time; # XXX check if a newer one is available. (We currently read it # from time to time) for ($CPAN::Config->{index_expire}) { $_ = 0.001 unless $_ && $_ > 0.001; } unless (1 || $CPAN::Have_warned->{readmetadatacache}++) { # debug here when CPAN doesn't seem to read the Metadata require Carp; Carp::cluck("META-PROTOCOL[$CPAN::META->{PROTOCOL}]"); } unless ($CPAN::META->{PROTOCOL}) { $self->read_metadata_cache; $CPAN::META->{PROTOCOL} ||= "1.0"; } if ( $CPAN::META->{PROTOCOL} < PROTOCOL ) { # warn "Setting last_time to 0"; $LAST_TIME = 0; # No warning necessary } if ($LAST_TIME + $CPAN::Config->{index_expire}*86400 > $time and ! $force) { # called too often # CPAN->debug("LAST_TIME[$LAST_TIME]index_expire[$CPAN::Config->{index_expire}]time[$time]"); } elsif (0) { # IFF we are developing, it helps to wipe out the memory # between reloads, otherwise it is not what a user expects. undef $CPAN::META; # Neue Gruendlichkeit since v1.52(r1.274) $CPAN::META = CPAN->new; } else { my($debug,$t2); local $LAST_TIME = $time; local $CPAN::META->{PROTOCOL} = PROTOCOL; my $needshort = $^O eq "dos"; INX: for my $indexbundle (@indexbundle) { my $reader = $indexbundle->{reader}; my $localfile = $needshort ? $indexbundle->{shortlocalfile} : $indexbundle->{remotefile}; my $localpath = File::Spec->catfile($indexbundle->{dir}, $localfile); my $remote = join "/", $indexbundle->{dir}, $indexbundle->{remotefile}; my $localized = $self->reload_x($remote, $localpath, $force); $self->$reader($localized); # may die but we let the shell catch it if ($CPAN::DEBUG){ $t2 = time; $debug = "timing reading 01[".($t2 - $time)."]"; $time = $t2; } return if $CPAN::Signal; # this is sometimes lengthy } $self->write_metadata_cache; if ($CPAN::DEBUG){ $t2 = time; $debug .= "03[".($t2 - $time)."]"; $time = $t2; } CPAN->debug($debug) if $CPAN::DEBUG; } if ($CPAN::Config->{build_dir_reuse}) { $self->reanimate_build_dir; } if (CPAN::_sqlite_running()) { $CPAN::SQLite->reload(time => $time, force => $force) if not $LAST_TIME; } $LAST_TIME = $time; $CPAN::META->{PROTOCOL} = PROTOCOL; } #-> sub CPAN::Index::reanimate_build_dir ; sub reanimate_build_dir { my($self) = @_; unless ($CPAN::META->has_inst($CPAN::Config->{yaml_module}||"YAML")) { return; } return if $HAVE_REANIMATED++; my $d = $CPAN::Config->{build_dir}; my $dh = DirHandle->new; opendir $dh, $d or return; # does not exist my $dirent; my $i = 0; my $painted = 0; my $restored = 0; my $start = CPAN::FTP::_mytime(); my @candidates = map { $_->[0] } sort { $b->[1] <=> $a->[1] } map { [ $_, -M File::Spec->catfile($d,$_) ] } grep {/(.+)\.yml$/ && -d File::Spec->catfile($d,$1)} readdir $dh; if ( @candidates ) { $CPAN::Frontend->myprint (sprintf("Reading %d yaml file%s from %s/\n", scalar @candidates, @candidates==1 ? "" : "s", $CPAN::Config->{build_dir} )); DISTRO: for $i (0..$#candidates) { my $dirent = $candidates[$i]; my $y = eval {CPAN->_yaml_loadfile(File::Spec->catfile($d,$dirent), {loadblessed => 1})}; if ($@) { warn "Error while parsing file '$dirent'; error: '$@'"; next DISTRO; } my $c = $y->[0]; if ($c && $c->{perl} && $c->{distribution} && CPAN->_perl_fingerprint($c->{perl})) { my $key = $c->{distribution}{ID}; for my $k (keys %{$c->{distribution}}) { if ($c->{distribution}{$k} && ref $c->{distribution}{$k} && UNIVERSAL::isa($c->{distribution}{$k},"CPAN::Distrostatus")) { $c->{distribution}{$k}{COMMANDID} = $i - @candidates; } } #we tried to restore only if element already #exists; but then we do not work with metadata #turned off. my $do = $CPAN::META->{readwrite}{'CPAN::Distribution'}{$key} = $c->{distribution}; for my $skipper (qw( badtestcnt configure_requires_later configure_requires_later_for force_update later later_for notest should_report sponsored_mods prefs negative_prefs_cache )) { delete $do->{$skipper}; } if ($do->can("tested_ok_but_not_installed")) { if ($do->tested_ok_but_not_installed) { $CPAN::META->is_tested($do->{build_dir},$do->{make_test}{TIME}); } else { next DISTRO; } } $restored++; } $i++; while (($painted/76) < ($i/@candidates)) { $CPAN::Frontend->myprint("."); $painted++; } } } else { $CPAN::Frontend->myprint("Build_dir empty, nothing to restore\n"); } my $took = CPAN::FTP::_mytime() - $start; $CPAN::Frontend->myprint(sprintf( "DONE\nRestored the state of %s (in %.4f secs)\n", $restored || "none", $took, )); } #-> sub CPAN::Index::reload_x ; sub reload_x { my($cl,$wanted,$localname,$force) = @_; $force |= 2; # means we're dealing with an index here CPAN::HandleConfig->load; # we should guarantee loading wherever # we rely on Config XXX $localname ||= $wanted; my $abs_wanted = File::Spec->catfile($CPAN::Config->{'keep_source_where'}, $localname); if ( -f $abs_wanted && -M $abs_wanted < $CPAN::Config->{'index_expire'} && !($force & 1) ) { my $s = $CPAN::Config->{'index_expire'} == 1 ? "" : "s"; $cl->debug(qq{$abs_wanted younger than $CPAN::Config->{'index_expire'} }. qq{day$s. I\'ll use that.}); return $abs_wanted; } else { $force |= 1; # means we're quite serious about it. } return CPAN::FTP->localize($wanted,$abs_wanted,$force); } #-> sub CPAN::Index::rd_authindex ; sub rd_authindex { my($cl, $index_target) = @_; return unless defined $index_target; return if CPAN::_sqlite_running(); my @lines; $CPAN::Frontend->myprint("Reading '$index_target'\n"); local(*FH); tie *FH, 'CPAN::Tarzip', $index_target; local($/) = "\n"; local($_); push @lines, split /\012/ while ; my $i = 0; my $painted = 0; foreach (@lines) { my($userid,$fullname,$email) = m/alias\s+(\S+)\s+\"([^\"\<]*)\s+\<(.*)\>\"/; $fullname ||= $email; if ($userid && $fullname && $email) { my $userobj = $CPAN::META->instance('CPAN::Author',$userid); $userobj->set('FULLNAME' => $fullname, 'EMAIL' => $email); } else { CPAN->debug(sprintf "line[%s]", $_) if $CPAN::DEBUG; } $i++; while (($painted/76) < ($i/@lines)) { $CPAN::Frontend->myprint("."); $painted++; } return if $CPAN::Signal; } $CPAN::Frontend->myprint("DONE\n"); } sub userid { my($self,$dist) = @_; $dist = $self->{'id'} unless defined $dist; my($ret) = $dist =~ m|(?:\w/\w\w/)?([^/]+)/|; $ret; } #-> sub CPAN::Index::rd_modpacks ; sub rd_modpacks { my($self, $index_target) = @_; return unless defined $index_target; return if CPAN::_sqlite_running(); $CPAN::Frontend->myprint("Reading '$index_target'\n"); my $fh = CPAN::Tarzip->TIEHANDLE($index_target); local $_; CPAN->debug(sprintf "start[%d]", time) if $CPAN::DEBUG; my $slurp = ""; my $chunk; while (my $bytes = $fh->READ(\$chunk,8192)) { $slurp.=$chunk; } my @lines = split /\012/, $slurp; CPAN->debug(sprintf "end[%d]", time) if $CPAN::DEBUG; undef $fh; # read header my($line_count,$last_updated); while (@lines) { my $shift = shift(@lines); last if $shift =~ /^\s*$/; $shift =~ /^Line-Count:\s+(\d+)/ and $line_count = $1; $shift =~ /^Last-Updated:\s+(.+)/ and $last_updated = $1; } CPAN->debug("line_count[$line_count]last_updated[$last_updated]") if $CPAN::DEBUG; my $errors = 0; if (not defined $line_count) { $CPAN::Frontend->mywarn(qq{Warning: Your $index_target does not contain a Line-Count header. Please check the validity of the index file by comparing it to more than one CPAN mirror. I'll continue but problems seem likely to happen.\a }); $errors++; $CPAN::Frontend->mysleep(5); } elsif ($line_count != scalar @lines) { $CPAN::Frontend->mywarn(sprintf qq{Warning: Your %s contains a Line-Count header of %d but I see %d lines there. Please check the validity of the index file by comparing it to more than one CPAN mirror. I'll continue but problems seem likely to happen.\a\n}, $index_target, $line_count, scalar(@lines)); } if (not defined $last_updated) { $CPAN::Frontend->mywarn(qq{Warning: Your $index_target does not contain a Last-Updated header. Please check the validity of the index file by comparing it to more than one CPAN mirror. I'll continue but problems seem likely to happen.\a }); $errors++; $CPAN::Frontend->mysleep(5); } else { $CPAN::Frontend ->myprint(sprintf qq{ Database was generated on %s\n}, $last_updated); $DATE_OF_02 = $last_updated; my $age = time; if ($CPAN::META->has_inst('HTTP::Date')) { require HTTP::Date; $age -= HTTP::Date::str2time($last_updated); } else { $CPAN::Frontend->mywarn(" HTTP::Date not available\n"); require Time::Local; my(@d) = $last_updated =~ / (\d+) (\w+) (\d+) (\d+):(\d+):(\d+) /; $d[1] = index("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", $d[1])/4; $age -= $d[1]>=0 ? Time::Local::timegm(@d[5,4,3,0,1,2]) : 0; } $age /= 3600*24; if ($age > 30) { $CPAN::Frontend ->mywarn(sprintf qq{Warning: This index file is %d days old. Please check the host you chose as your CPAN mirror for staleness. I'll continue but problems seem likely to happen.\a\n}, $age); } elsif ($age < -1) { $CPAN::Frontend ->mywarn(sprintf qq{Warning: Your system date is %d days behind this index file! System time: %s Timestamp index file: %s Please fix your system time, problems with the make command expected.\n}, -$age, scalar gmtime, $DATE_OF_02, ); } } # A necessity since we have metadata_cache: delete what isn't # there anymore my $secondtime = $CPAN::META->exists("CPAN::Module","CPAN"); CPAN->debug("secondtime[$secondtime]") if $CPAN::DEBUG; my(%exists); my $i = 0; my $painted = 0; LINE: foreach (@lines) { # before 1.56 we split into 3 and discarded the rest. From # 1.57 we assign remaining text to $comment thus allowing to # influence isa_perl my($mod,$version,$dist,$comment) = split " ", $_, 4; unless ($mod && defined $version && $dist) { require Dumpvalue; my $dv = Dumpvalue->new(tick => '"'); $CPAN::Frontend->mywarn(sprintf "Could not split line[%s]\n", $dv->stringify($_)); if ($errors++ >= 5){ $CPAN::Frontend->mydie("Giving up parsing your $index_target, too many errors"); } next LINE; } my($bundle,$id,$userid); if ($mod eq 'CPAN' && ! ( CPAN::Queue->exists('Bundle::CPAN') || CPAN::Queue->exists('CPAN') ) ) { local($^W)= 0; if ($version > $CPAN::VERSION) { $CPAN::Frontend->mywarn(qq{ New CPAN.pm version (v$version) available. [Currently running version is v$CPAN::VERSION] You might want to try install CPAN reload cpan to both upgrade CPAN.pm and run the new version without leaving the current session. }); #}); $CPAN::Frontend->mysleep(2); $CPAN::Frontend->myprint(qq{\n}); } last if $CPAN::Signal; } elsif ($mod =~ /^Bundle::(.*)/) { $bundle = $1; } if ($bundle) { $id = $CPAN::META->instance('CPAN::Bundle',$mod); # Let's make it a module too, because bundles have so much # in common with modules. # Changed in 1.57_63: seems like memory bloat now without # any value, so commented out # $CPAN::META->instance('CPAN::Module',$mod); } else { # instantiate a module object $id = $CPAN::META->instance('CPAN::Module',$mod); } # Although CPAN prohibits same name with different version the # indexer may have changed the version for the same distro # since the last time ("Force Reindexing" feature) if ($id->cpan_file ne $dist || $id->cpan_version ne $version ) { $userid = $id->userid || $self->userid($dist); $id->set( 'CPAN_USERID' => $userid, 'CPAN_VERSION' => $version, 'CPAN_FILE' => $dist, ); } # instantiate a distribution object if ($CPAN::META->exists('CPAN::Distribution',$dist)) { # we do not need CONTAINSMODS unless we do something with # this dist, so we better produce it on demand. ## my $obj = $CPAN::META->instance( ## 'CPAN::Distribution' => $dist ## ); ## $obj->{CONTAINSMODS}{$mod} = undef; # experimental } else { $CPAN::META->instance( 'CPAN::Distribution' => $dist )->set( 'CPAN_USERID' => $userid, 'CPAN_COMMENT' => $comment, ); } if ($secondtime) { for my $name ($mod,$dist) { # $self->debug("exists name[$name]") if $CPAN::DEBUG; $exists{$name} = undef; } } $i++; while (($painted/76) < ($i/@lines)) { $CPAN::Frontend->myprint("."); $painted++; } return if $CPAN::Signal; } $CPAN::Frontend->myprint("DONE\n"); if ($secondtime) { for my $class (qw(CPAN::Module CPAN::Bundle CPAN::Distribution)) { for my $o ($CPAN::META->all_objects($class)) { next if exists $exists{$o->{ID}}; $CPAN::META->delete($class,$o->{ID}); # CPAN->debug("deleting ID[$o->{ID}] in class[$class]") # if $CPAN::DEBUG; } } } } #-> sub CPAN::Index::rd_modlist ; sub rd_modlist { my($cl,$index_target) = @_; return unless defined $index_target; return if CPAN::_sqlite_running(); $CPAN::Frontend->myprint("Reading '$index_target'\n"); my $fh = CPAN::Tarzip->TIEHANDLE($index_target); local $_; my $slurp = ""; my $chunk; while (my $bytes = $fh->READ(\$chunk,8192)) { $slurp.=$chunk; } my @eval2 = split /\012/, $slurp; while (@eval2) { my $shift = shift(@eval2); if ($shift =~ /^Date:\s+(.*)/) { if ($DATE_OF_03 eq $1) { $CPAN::Frontend->myprint("Unchanged.\n"); return; } ($DATE_OF_03) = $1; } last if $shift =~ /^\s*$/; } push @eval2, q{CPAN::Modulelist->data;}; local($^W) = 0; my($compmt) = Safe->new("CPAN::Safe1"); my($eval2) = join("\n", @eval2); CPAN->debug(sprintf "length of eval2[%d]", length $eval2) if $CPAN::DEBUG; my $ret = $compmt->reval($eval2); Carp::confess($@) if $@; return if $CPAN::Signal; my $i = 0; my $until = keys(%$ret); my $painted = 0; CPAN->debug(sprintf "until[%d]", $until) if $CPAN::DEBUG; for (sort keys %$ret) { my $obj = $CPAN::META->instance("CPAN::Module",$_); delete $ret->{$_}{modid}; # not needed here, maybe elsewhere $obj->set(%{$ret->{$_}}); $i++; while (($painted/76) < ($i/$until)) { $CPAN::Frontend->myprint("."); $painted++; } return if $CPAN::Signal; } $CPAN::Frontend->myprint("DONE\n"); } #-> sub CPAN::Index::write_metadata_cache ; sub write_metadata_cache { my($self) = @_; return unless $CPAN::Config->{'cache_metadata'}; return if CPAN::_sqlite_running(); return unless $CPAN::META->has_usable("Storable"); my $cache; foreach my $k (qw(CPAN::Bundle CPAN::Author CPAN::Module CPAN::Distribution)) { $cache->{$k} = $CPAN::META->{readonly}{$k}; # unsafe meta access, ok } my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata"); $cache->{last_time} = $LAST_TIME; $cache->{DATE_OF_02} = $DATE_OF_02; $cache->{PROTOCOL} = PROTOCOL; $CPAN::Frontend->myprint("Writing $metadata_file\n"); eval { Storable::nstore($cache, $metadata_file) }; $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ?? } #-> sub CPAN::Index::read_metadata_cache ; sub read_metadata_cache { my($self) = @_; return unless $CPAN::Config->{'cache_metadata'}; return if CPAN::_sqlite_running(); return unless $CPAN::META->has_usable("Storable"); my $metadata_file = File::Spec->catfile($CPAN::Config->{cpan_home},"Metadata"); return unless -r $metadata_file and -f $metadata_file; $CPAN::Frontend->myprint("Reading '$metadata_file'\n"); my $cache; eval { $cache = Storable::retrieve($metadata_file) }; $CPAN::Frontend->mywarn($@) if $@; # ?? missing "\n" after $@ in mywarn ?? if (!$cache || !UNIVERSAL::isa($cache, 'HASH')) { $LAST_TIME = 0; return; } if (exists $cache->{PROTOCOL}) { if (PROTOCOL > $cache->{PROTOCOL}) { $CPAN::Frontend->mywarn(sprintf("Ignoring Metadata cache written ". "with protocol v%s, requiring v%s\n", $cache->{PROTOCOL}, PROTOCOL) ); return; } } else { $CPAN::Frontend->mywarn("Ignoring Metadata cache written ". "with protocol v1.0\n"); return; } my $clcnt = 0; my $idcnt = 0; while(my($class,$v) = each %$cache) { next unless $class =~ /^CPAN::/; $CPAN::META->{readonly}{$class} = $v; # unsafe meta access, ok while (my($id,$ro) = each %$v) { $CPAN::META->{readwrite}{$class}{$id} ||= $class->new(ID=>$id, RO=>$ro); $idcnt++; } $clcnt++; } unless ($clcnt) { # sanity check $CPAN::Frontend->myprint("Warning: Found no data in $metadata_file\n"); return; } if ($idcnt < 1000) { $CPAN::Frontend->myprint("Warning: Found only $idcnt objects ". "in $metadata_file\n"); return; } $CPAN::META->{PROTOCOL} ||= $cache->{PROTOCOL}; # reading does not up or downgrade, but it # does initialize to some protocol $LAST_TIME = $cache->{last_time}; $DATE_OF_02 = $cache->{DATE_OF_02}; $CPAN::Frontend->myprint(" Database was generated on $DATE_OF_02\n") if defined $DATE_OF_02; # An old cache may not contain DATE_OF_02 return; } 1; CPAN/Tarzip.pm000044400000040400151112047420007065 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- package CPAN::Tarzip; use strict; use vars qw($VERSION @ISA $BUGHUNTING); use CPAN::Debug; use File::Basename qw(basename); $VERSION = "5.5013"; # module is internal to CPAN.pm @ISA = qw(CPAN::Debug); ## no critic $BUGHUNTING ||= 0; # released code must have turned off # it's ok if file doesn't exist, it just matters if it is .gz or .bz2 sub new { my($class,$file) = @_; $CPAN::Frontend->mydie("CPAN::Tarzip->new called without arg") unless defined $file; my $me = { FILE => $file }; if ($file =~ /\.(bz2|gz|zip|tbz|tgz)$/i) { $me->{ISCOMPRESSED} = 1; } else { $me->{ISCOMPRESSED} = 0; } if (0) { } elsif ($file =~ /\.(?:bz2|tbz)$/i) { unless ($me->{UNGZIPPRG} = $CPAN::Config->{bzip2}) { my $bzip2 = _my_which("bzip2"); if ($bzip2) { $me->{UNGZIPPRG} = $bzip2; } else { $CPAN::Frontend->mydie(qq{ CPAN.pm needs the external program bzip2 in order to handle '$file'. Please install it now and run 'o conf init bzip2' from the CPAN shell prompt to register it as external program. }); } } } else { $me->{UNGZIPPRG} = _my_which("gzip"); } $me->{TARPRG} = _my_which("tar") || _my_which("gtar"); bless $me, $class; } sub _zlib_ok () { $CPAN::META->has_inst("Compress::Zlib") or return; Compress::Zlib->can('gzopen'); } sub _my_which { my($what) = @_; if ($CPAN::Config->{$what}) { return $CPAN::Config->{$what}; } if ($CPAN::META->has_inst("File::Which")) { return File::Which::which($what); } my @cand = MM->maybe_command($what); return $cand[0] if @cand; require File::Spec; my $component; PATH_COMPONENT: foreach $component (File::Spec->path()) { next unless defined($component) && $component; my($abs) = File::Spec->catfile($component,$what); if (MM->maybe_command($abs)) { return $abs; } } return; } sub gzip { my($self,$read) = @_; my $write = $self->{FILE}; if (_zlib_ok) { my($buffer,$fhw); $fhw = FileHandle->new($read) or $CPAN::Frontend->mydie("Could not open $read: $!"); my $cwd = `pwd`; my $gz = Compress::Zlib::gzopen($write, "wb") or $CPAN::Frontend->mydie("Cannot gzopen $write: $! (pwd is $cwd)\n"); binmode($fhw); $gz->gzwrite($buffer) while read($fhw,$buffer,4096) > 0 ; $gz->gzclose() ; $fhw->close; return 1; } else { my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); system(qq{$command -c "$read" > "$write"})==0; } } sub gunzip { my($self,$write) = @_; my $read = $self->{FILE}; if (_zlib_ok) { my($buffer,$fhw); $fhw = FileHandle->new(">$write") or $CPAN::Frontend->mydie("Could not open >$write: $!"); my $gz = Compress::Zlib::gzopen($read, "rb") or $CPAN::Frontend->mydie("Cannot gzopen $read: $!\n"); binmode($fhw); $fhw->print($buffer) while $gz->gzread($buffer) > 0 ; $CPAN::Frontend->mydie("Error reading from $read: $!\n") if $gz->gzerror != Compress::Zlib::Z_STREAM_END(); $gz->gzclose() ; $fhw->close; return 1; } else { my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); system(qq{$command -d -c "$read" > "$write"})==0; } } sub gtest { my($self) = @_; return $self->{GTEST} if exists $self->{GTEST}; defined $self->{FILE} or $CPAN::Frontend->mydie("gtest called but no FILE specified"); my $read = $self->{FILE}; my $success; if ($read=~/\.(?:bz2|tbz)$/ && $CPAN::META->has_inst("Compress::Bzip2")) { my($buffer,$len); $len = 0; my $gz = Compress::Bzip2::bzopen($read, "rb") or $CPAN::Frontend->mydie(sprintf("Cannot bzopen %s: %s\n", $read, $Compress::Bzip2::bzerrno)); while ($gz->bzread($buffer) > 0 ) { $len += length($buffer); $buffer = ""; } my $err = $gz->bzerror; $success = ! $err || $err == Compress::Bzip2::BZ_STREAM_END(); if ($len == -s $read) { $success = 0; CPAN->debug("hit an uncompressed file") if $CPAN::DEBUG; } $gz->gzclose(); CPAN->debug("err[$err]success[$success]") if $CPAN::DEBUG; } elsif ( $read=~/\.(?:gz|tgz)$/ && _zlib_ok ) { # After I had reread the documentation in zlib.h, I discovered that # uncompressed files do not lead to an gzerror (anymore?). my($buffer,$len); $len = 0; my $gz = Compress::Zlib::gzopen($read, "rb") or $CPAN::Frontend->mydie(sprintf("Cannot gzopen %s: %s\n", $read, $Compress::Zlib::gzerrno)); while ($gz->gzread($buffer) > 0 ) { $len += length($buffer); $buffer = ""; } my $err = $gz->gzerror; $success = ! $err || $err == Compress::Zlib::Z_STREAM_END(); if ($len == -s $read) { $success = 0; CPAN->debug("hit an uncompressed file") if $CPAN::DEBUG; } $gz->gzclose(); CPAN->debug("err[$err]success[$success]") if $CPAN::DEBUG; } elsif (!$self->{ISCOMPRESSED}) { $success = 0; } else { my $command = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); $success = 0==system(qq{$command -qdt "$read"}); } return $self->{GTEST} = $success; } sub TIEHANDLE { my($class,$file) = @_; my $ret; $class->debug("file[$file]"); my $self = $class->new($file); if (0) { } elsif (!$self->gtest) { my $fh = FileHandle->new($file) or $CPAN::Frontend->mydie("Could not open file[$file]: $!"); binmode $fh; $self->{FH} = $fh; $class->debug("via uncompressed FH"); } elsif ($file =~ /\.(?:bz2|tbz)$/ && $CPAN::META->has_inst("Compress::Bzip2")) { my $gz = Compress::Bzip2::bzopen($file,"rb") or $CPAN::Frontend->mydie("Could not bzopen $file"); $self->{GZ} = $gz; $class->debug("via Compress::Bzip2"); } elsif ($file =~/\.(?:gz|tgz)$/ && _zlib_ok) { my $gz = Compress::Zlib::gzopen($file,"rb") or $CPAN::Frontend->mydie("Could not gzopen $file"); $self->{GZ} = $gz; $class->debug("via Compress::Zlib"); } else { my $gzip = CPAN::HandleConfig->safe_quote($self->{UNGZIPPRG}); my $pipe = "$gzip -d -c $file |"; my $fh = FileHandle->new($pipe) or $CPAN::Frontend->mydie("Could not pipe[$pipe]: $!"); binmode $fh; $self->{FH} = $fh; $class->debug("via external $gzip"); } $self; } sub READLINE { my($self) = @_; if (exists $self->{GZ}) { my $gz = $self->{GZ}; my($line,$bytesread); $bytesread = $gz->gzreadline($line); return undef if $bytesread <= 0; return $line; } else { my $fh = $self->{FH}; return scalar <$fh>; } } sub READ { my($self,$ref,$length,$offset) = @_; $CPAN::Frontend->mydie("read with offset not implemented") if defined $offset; if (exists $self->{GZ}) { my $gz = $self->{GZ}; my $byteread = $gz->gzread($$ref,$length);# 30eaf79e8b446ef52464b5422da328a8 return $byteread; } else { my $fh = $self->{FH}; return read($fh,$$ref,$length); } } sub DESTROY { my($self) = @_; if (exists $self->{GZ}) { my $gz = $self->{GZ}; $gz->gzclose() if defined $gz; # hard to say if it is allowed # to be undef ever. AK, 2000-09 } else { my $fh = $self->{FH}; $fh->close if defined $fh; } undef $self; } sub untar { my($self) = @_; my $file = $self->{FILE}; my($prefer) = 0; my $exttar = $self->{TARPRG} || ""; $exttar = "" if $exttar =~ /^\s+$/; # user refuses to use it my $extgzip = $self->{UNGZIPPRG} || ""; $extgzip = "" if $extgzip =~ /^\s+$/; # user refuses to use it if (0) { # makes changing order easier } elsif ($BUGHUNTING) { $prefer=2; } elsif ($CPAN::Config->{prefer_external_tar}) { $prefer = 1; } elsif ( $CPAN::META->has_usable("Archive::Tar") && _zlib_ok ) { my $prefer_external_tar = $CPAN::Config->{prefer_external_tar}; unless (defined $prefer_external_tar) { if ($^O =~ /(MSWin32|solaris)/) { $prefer_external_tar = 0; } else { $prefer_external_tar = 1; } } $prefer = $prefer_external_tar ? 1 : 2; } elsif ($exttar && $extgzip) { # no modules and not bz2 $prefer = 1; # but solaris binary tar is a problem if ($^O eq 'solaris' && qx($exttar --version 2>/dev/null) !~ /gnu/i) { $CPAN::Frontend->mywarn(<< 'END_WARN'); WARNING: Many CPAN distributions were archived with GNU tar and some of them may be incompatible with Solaris tar. We respectfully suggest you configure CPAN to use a GNU tar instead ("o conf init tar") or install a recent Archive::Tar instead; END_WARN } } else { my $foundtar = $exttar ? "'$exttar'" : "nothing"; my $foundzip = $extgzip ? "'$extgzip'" : $foundtar ? "nothing" : "also nothing"; my $foundAT; if ($CPAN::META->has_usable("Archive::Tar")) { $foundAT = sprintf "'%s'", "Archive::Tar::"->VERSION; } else { $foundAT = "nothing"; } my $foundCZ; if (_zlib_ok) { $foundCZ = sprintf "'%s'", "Compress::Zlib::"->VERSION; } elsif ($foundAT) { $foundCZ = "nothing"; } else { $foundCZ = "also nothing"; } $CPAN::Frontend->mydie(qq{ CPAN.pm needs either the external programs tar and gzip -or- both modules Archive::Tar and Compress::Zlib installed. For tar I found $foundtar, for gzip $foundzip. For Archive::Tar I found $foundAT, for Compress::Zlib $foundCZ; Can't continue cutting file '$file'. }); } my $tar_verb = "v"; if (defined $CPAN::Config->{tar_verbosity}) { $tar_verb = $CPAN::Config->{tar_verbosity} eq "none" ? "" : $CPAN::Config->{tar_verbosity}; } if ($prefer==1) { # 1 => external gzip+tar my($system); my $is_compressed = $self->gtest(); my $tarcommand = CPAN::HandleConfig->safe_quote($exttar); if ($is_compressed) { my $command = CPAN::HandleConfig->safe_quote($extgzip); $system = qq{$command -d -c }. qq{< "$file" | $tarcommand x${tar_verb}f -}; } else { $system = qq{$tarcommand x${tar_verb}f "$file"}; } if (system($system) != 0) { # people find the most curious tar binaries that cannot handle # pipes if ($is_compressed) { (my $ungzf = $file) =~ s/\.gz(?!\n)\Z//; $ungzf = basename $ungzf; my $ct = CPAN::Tarzip->new($file); if ($ct->gunzip($ungzf)) { $CPAN::Frontend->myprint(qq{Uncompressed $file successfully\n}); } else { $CPAN::Frontend->mydie(qq{Couldn\'t uncompress $file\n}); } $file = $ungzf; } $system = qq{$tarcommand x${tar_verb}f "$file"}; $CPAN::Frontend->myprint(qq{Using Tar:$system:\n}); my $ret = system($system); if ($ret==0) { $CPAN::Frontend->myprint(qq{Untarred $file successfully\n}); } else { if ($? == -1) { $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: '%s'\n}, $file, $!); } elsif ($? & 127) { $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: child died with signal %d, %s coredump\n}, $file, ($? & 127), ($? & 128) ? 'with' : 'without'); } else { $CPAN::Frontend->mydie(sprintf qq{Couldn\'t untar %s: child exited with value %d\n}, $file, $? >> 8); } } return 1; } else { return 1; } } elsif ($prefer==2) { # 2 => modules unless ($CPAN::META->has_usable("Archive::Tar")) { $CPAN::Frontend->mydie("Archive::Tar not installed, please install it to continue"); } # Make sure AT does not use uid/gid/permissions in the archive # This leaves it to the user's umask instead local $Archive::Tar::CHMOD = 1; local $Archive::Tar::SAME_PERMISSIONS = 0; # Make sure AT leaves current user as owner local $Archive::Tar::CHOWN = 0; my $tar = Archive::Tar->new($file,1); my $af; # archive file my @af; if ($BUGHUNTING) { # RCS 1.337 had this code, it turned out unacceptable slow but # it revealed a bug in Archive::Tar. Code is only here to hunt # the bug again. It should never be enabled in published code. # GDGraph3d-0.53 was an interesting case according to Larry # Virden. warn(">>>Bughunting code enabled<<< " x 20); for $af ($tar->list_files) { if ($af =~ m!^(/|\.\./)!) { $CPAN::Frontend->mydie("ALERT: Archive contains ". "illegal member [$af]"); } $CPAN::Frontend->myprint("$af\n"); $tar->extract($af); # slow but effective for finding the bug return if $CPAN::Signal; } } else { for $af ($tar->list_files) { if ($af =~ m!^(/|\.\./)!) { $CPAN::Frontend->mydie("ALERT: Archive contains ". "illegal member [$af]"); } if ($tar_verb eq "v" || $tar_verb eq "vv") { $CPAN::Frontend->myprint("$af\n"); } push @af, $af; return if $CPAN::Signal; } $tar->extract(@af) or $CPAN::Frontend->mydie("Could not untar with Archive::Tar."); } Mac::BuildTools::convert_files([$tar->list_files], 1) if ($^O eq 'MacOS'); return 1; } } sub unzip { my($self) = @_; my $file = $self->{FILE}; if ($CPAN::META->has_inst("Archive::Zip")) { # blueprint of the code from Archive::Zip::Tree::extractTree(); my $zip = Archive::Zip->new(); my $status; $status = $zip->read($file); $CPAN::Frontend->mydie("Read of file[$file] failed\n") if $status != Archive::Zip::AZ_OK(); $CPAN::META->debug("Successfully read file[$file]") if $CPAN::DEBUG; my @members = $zip->members(); for my $member ( @members ) { my $af = $member->fileName(); if ($af =~ m!^(/|\.\./)!) { $CPAN::Frontend->mydie("ALERT: Archive contains ". "illegal member [$af]"); } $status = $member->extractToFileNamed( $af ); $CPAN::META->debug("af[$af]status[$status]") if $CPAN::DEBUG; $CPAN::Frontend->mydie("Extracting of file[$af] from zipfile[$file] failed\n") if $status != Archive::Zip::AZ_OK(); return if $CPAN::Signal; } return 1; } elsif ( my $unzip = $CPAN::Config->{unzip} ) { my @system = ($unzip, $file); return system(@system) == 0; } else { $CPAN::Frontend->mydie(<<"END"); Can't unzip '$file': You have not configured an 'unzip' program and do not have Archive::Zip installed. Please either install Archive::Zip or else configure 'unzip' by running the command 'o conf init unzip' from the CPAN shell prompt. END } } 1; __END__ =head1 NAME CPAN::Tarzip - internal handling of tar archives for CPAN.pm =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut CPAN/Bundle.pm000044400000023640151112047420007034 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Bundle; use strict; use CPAN::Module; @CPAN::Bundle::ISA = qw(CPAN::Module); use vars qw( $VERSION ); $VERSION = "5.5005"; sub look { my $self = shift; $CPAN::Frontend->myprint($self->as_string); } #-> CPAN::Bundle::undelay sub undelay { my $self = shift; delete $self->{later}; for my $c ( $self->contains ) { my $obj = CPAN::Shell->expandany($c) or next; if ($obj->id eq $self->id){ my $id = $obj->id; $CPAN::Frontend->mywarn("$id seems to contain itself, skipping\n"); next; } $obj->undelay; } } # mark as dirty/clean #-> sub CPAN::Bundle::color_cmd_tmps ; sub color_cmd_tmps { my($self) = shift; my($depth) = shift || 0; my($color) = shift || 0; my($ancestors) = shift || []; # a module needs to recurse to its cpan_file, a distribution needs # to recurse into its prereq_pms, a bundle needs to recurse into its modules return if exists $self->{incommandcolor} && $color==1 && $self->{incommandcolor}==$color; if ($depth>=$CPAN::MAX_RECURSION) { my $e = CPAN::Exception::RecursiveDependency->new($ancestors); if ($e->is_resolvable) { return $self->{incommandcolor}=2; } else { die $e; } } # warn "color_cmd_tmps $depth $color " . $self->id; # sleep 1; for my $c ( $self->contains ) { my $obj = CPAN::Shell->expandany($c) or next; CPAN->debug("c[$c]obj[$obj]") if $CPAN::DEBUG; $obj->color_cmd_tmps($depth+1,$color,[@$ancestors, $self->id]); } # never reached code? #if ($color==0) { #delete $self->{badtestcnt}; #} $self->{incommandcolor} = $color; } #-> sub CPAN::Bundle::as_string ; sub as_string { my($self) = @_; $self->contains; # following line must be "=", not "||=" because we have a moving target $self->{INST_VERSION} = $self->inst_version; return $self->SUPER::as_string; } #-> sub CPAN::Bundle::contains ; sub contains { my($self) = @_; my($inst_file) = $self->inst_file || ""; my($id) = $self->id; $self->debug("inst_file[$inst_file]id[$id]") if $CPAN::DEBUG; if ($inst_file && CPAN::Version->vlt($self->inst_version,$self->cpan_version)) { undef $inst_file; } unless ($inst_file) { # Try to get at it in the cpan directory $self->debug("no inst_file") if $CPAN::DEBUG; my $cpan_file; $CPAN::Frontend->mydie("I don't know a bundle with ID '$id'\n") unless $cpan_file = $self->cpan_file; if ($cpan_file eq "N/A") { $CPAN::Frontend->mywarn("Bundle '$id' not found on disk and not on CPAN. Maybe stale symlink? Maybe removed during session?\n"); return; } my $dist = $CPAN::META->instance('CPAN::Distribution', $self->cpan_file); $self->debug("before get id[$dist->{ID}]") if $CPAN::DEBUG; $dist->get; $self->debug("after get id[$dist->{ID}]") if $CPAN::DEBUG; my($todir) = $CPAN::Config->{'cpan_home'}; my(@me,$from,$to,$me); @me = split /::/, $self->id; $me[-1] .= ".pm"; $me = File::Spec->catfile(@me); my $build_dir; unless ($build_dir = $dist->{build_dir}) { $CPAN::Frontend->mywarn("Warning: cannot determine bundle content without a build_dir.\n"); return; } $from = $self->find_bundle_file($build_dir,join('/',@me)); $to = File::Spec->catfile($todir,$me); File::Path::mkpath(File::Basename::dirname($to)); File::Copy::copy($from, $to) or Carp::confess("Couldn't copy $from to $to: $!"); $inst_file = $to; } my @result; my $fh = FileHandle->new; local $/ = "\n"; open($fh,$inst_file) or die "Could not open '$inst_file': $!"; my $in_cont = 0; $self->debug("inst_file[$inst_file]") if $CPAN::DEBUG; while (<$fh>) { $in_cont = m/^=(?!head1\s+(?i-xsm:CONTENTS))/ ? 0 : m/^=head1\s+(?i-xsm:CONTENTS)/ ? 1 : $in_cont; next unless $in_cont; next if /^=/; s/\#.*//; next if /^\s+$/; chomp; push @result, (split " ", $_, 2)[0]; } close $fh; delete $self->{STATUS}; $self->{CONTAINS} = \@result; $self->debug("CONTAINS[@result]") if $CPAN::DEBUG; unless (@result) { $CPAN::Frontend->mywarn(qq{ The bundle file "$inst_file" may be a broken bundlefile. It seems not to contain any bundle definition. Please check the file and if it is bogus, please delete it. Sorry for the inconvenience. }); } @result; } #-> sub CPAN::Bundle::find_bundle_file # $where is in local format, $what is in unix format sub find_bundle_file { my($self,$where,$what) = @_; $self->debug("where[$where]what[$what]") if $CPAN::DEBUG; ### The following two lines let CPAN.pm become Bundle/CPAN.pm :-( ### my $bu = File::Spec->catfile($where,$what); ### return $bu if -f $bu; my $manifest = File::Spec->catfile($where,"MANIFEST"); unless (-f $manifest) { require ExtUtils::Manifest; my $cwd = CPAN::anycwd(); $self->safe_chdir($where); ExtUtils::Manifest::mkmanifest(); $self->safe_chdir($cwd); } my $fh = FileHandle->new($manifest) or Carp::croak("Couldn't open $manifest: $!"); local($/) = "\n"; my $bundle_filename = $what; $bundle_filename =~ s|Bundle.*/||; my $bundle_unixpath; while (<$fh>) { next if /^\s*\#/; my($file) = /(\S+)/; if ($file =~ m|\Q$what\E$|) { $bundle_unixpath = $file; # return File::Spec->catfile($where,$bundle_unixpath); # bad last; } # retry if she managed to have no Bundle directory $bundle_unixpath = $file if $file =~ m|\Q$bundle_filename\E$|; } return File::Spec->catfile($where, split /\//, $bundle_unixpath) if $bundle_unixpath; Carp::croak("Couldn't find a Bundle file in $where"); } # needs to work quite differently from Module::inst_file because of # cpan_home/Bundle/ directory and the possibility that we have # shadowing effect. As it makes no sense to take the first in @INC for # Bundles, we parse them all for $VERSION and take the newest. #-> sub CPAN::Bundle::inst_file ; sub inst_file { my($self) = @_; my($inst_file); my(@me); @me = split /::/, $self->id; $me[-1] .= ".pm"; my($incdir,$bestv); foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) { my $parsefile = File::Spec->catfile($incdir, @me); CPAN->debug("parsefile[$parsefile]") if $CPAN::DEBUG; next unless -f $parsefile; my $have = eval { MM->parse_version($parsefile); }; if ($@) { $CPAN::Frontend->mywarn("Error while parsing version number in file '$parsefile'\n"); } if (!$bestv || CPAN::Version->vgt($have,$bestv)) { $self->{INST_FILE} = $parsefile; $self->{INST_VERSION} = $bestv = $have; } } $self->{INST_FILE}; } #-> sub CPAN::Bundle::inst_version ; sub inst_version { my($self) = @_; $self->inst_file; # finds INST_VERSION as side effect $self->{INST_VERSION}; } #-> sub CPAN::Bundle::rematein ; sub rematein { my($self,$meth) = @_; $self->debug("self[$self] meth[$meth]") if $CPAN::DEBUG; my($id) = $self->id; Carp::croak( "Can't $meth $id, don't have an associated bundle file. :-(\n" ) unless $self->inst_file || $self->cpan_file; my($s,%fail); for $s ($self->contains) { my($type) = $s =~ m|/| ? 'CPAN::Distribution' : $s =~ m|^Bundle::| ? 'CPAN::Bundle' : 'CPAN::Module'; if ($type eq 'CPAN::Distribution') { $CPAN::Frontend->mywarn(qq{ The Bundle }.$self->id.qq{ contains explicitly a file '$s'. Going to $meth that. }); $CPAN::Frontend->mysleep(5); } # possibly noisy action: $self->debug("type[$type] s[$s]") if $CPAN::DEBUG; my $obj = $CPAN::META->instance($type,$s); $obj->{reqtype} = $self->{reqtype}; $obj->{viabundle} ||= { id => $id, reqtype => $self->{reqtype}, optional => !$self->{mandatory}}; # $obj->$meth(); # XXX should optional be based on whether bundle was optional? -- xdg, 2012-04-01 # A: Sure, what could demand otherwise? --andk, 2013-11-25 CPAN::Queue->queue_item(qmod => $obj->id, reqtype => $self->{reqtype}, optional => !$self->{mandatory}); } } # If a bundle contains another that contains an xs_file we have here, # we just don't bother I suppose #-> sub CPAN::Bundle::xs_file sub xs_file { return 0; } #-> sub CPAN::Bundle::force ; sub fforce { shift->rematein('fforce',@_); } #-> sub CPAN::Bundle::force ; sub force { shift->rematein('force',@_); } #-> sub CPAN::Bundle::notest ; sub notest { shift->rematein('notest',@_); } #-> sub CPAN::Bundle::get ; sub get { shift->rematein('get',@_); } #-> sub CPAN::Bundle::make ; sub make { shift->rematein('make',@_); } #-> sub CPAN::Bundle::test ; sub test { my $self = shift; # $self->{badtestcnt} ||= 0; $self->rematein('test',@_); } #-> sub CPAN::Bundle::install ; sub install { my $self = shift; $self->rematein('install',@_); } #-> sub CPAN::Bundle::clean ; sub clean { shift->rematein('clean',@_); } #-> sub CPAN::Bundle::uptodate ; sub uptodate { my($self) = @_; return 0 unless $self->SUPER::uptodate; # we must have the current Bundle def my $c; foreach $c ($self->contains) { my $obj = CPAN::Shell->expandany($c); return 0 unless $obj->uptodate; } return 1; } #-> sub CPAN::Bundle::readme ; sub readme { my($self) = @_; my($file) = $self->cpan_file or $CPAN::Frontend->myprint(qq{ No File found for bundle } . $self->id . qq{\n}), return; $self->debug("self[$self] file[$file]") if $CPAN::DEBUG; $CPAN::META->instance('CPAN::Distribution',$file)->readme; } 1; CPAN/Kwalify/distroprefs.yml000044400000003071151112047420011756 0ustar00--- type: map mapping: comment: type: text depends: type: map mapping: configure_requires: &requires_common type: map mapping: =: type: text build_requires: *requires_common requires: *requires_common match: type: map mapping: distribution: type: text module: type: text perl: type: text perlconfig: &matchhash_common type: map mapping: =: type: text env: *matchhash_common install: &args_env_expect type: map mapping: args: type: seq sequence: - type: text commandline: type: text env: type: map mapping: =: type: text expect: type: seq sequence: - type: text eexpect: type: map mapping: mode: type: text enum: - deterministic - anyorder timeout: type: number reuse: type: int talk: type: seq sequence: - type: text make: *args_env_expect pl: *args_env_expect test: *args_env_expect patches: type: seq sequence: - type: text disabled: type: int enum: - 0 - 1 goto: type: text cpanconfig: type: map mapping: =: type: text features: type: seq sequence: - type: text reminder: type: text CPAN/Kwalify/distroprefs.dd000044400000006233151112047420011547 0ustar00$VAR1 = { "mapping" => { "comment" => { "type" => "text" }, "cpanconfig" => { "mapping" => { "=" => { "type" => "text" } }, "type" => "map" }, "depends" => { "mapping" => { "build_requires" => { "mapping" => { "=" => { "type" => "text" } }, "type" => "map" }, "configure_requires" => {}, "requires" => {} }, "type" => "map" }, "disabled" => { "enum" => [ 0, 1 ], "type" => "int" }, "features" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" }, "goto" => { "type" => "text" }, "install" => { "mapping" => { "args" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" }, "commandline" => { "type" => "text" }, "eexpect" => { "mapping" => { "mode" => { "enum" => [ "deterministic", "anyorder" ], "type" => "text" }, "reuse" => { "type" => "int" }, "talk" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" }, "timeout" => { "type" => "number" } }, "type" => "map" }, "env" => { "mapping" => { "=" => { "type" => "text" } }, "type" => "map" }, "expect" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" } }, "type" => "map" }, "make" => {}, "match" => { "mapping" => { "distribution" => { "type" => "text" }, "env" => { "mapping" => { "=" => { "type" => "text" } }, "type" => "map" }, "module" => { "type" => "text" }, "perl" => { "type" => "text" }, "perlconfig" => {} }, "type" => "map" }, "patches" => { "sequence" => [ { "type" => "text" } ], "type" => "seq" }, "pl" => {}, "reminder" => { "type" => "text" }, "test" => {} }, "type" => "map" }; $VAR1->{"mapping"}{"depends"}{"mapping"}{"configure_requires"} = $VAR1->{"mapping"}{"depends"}{"mapping"}{"build_requires"}; $VAR1->{"mapping"}{"depends"}{"mapping"}{"requires"} = $VAR1->{"mapping"}{"depends"}{"mapping"}{"build_requires"}; $VAR1->{"mapping"}{"make"} = $VAR1->{"mapping"}{"install"}; $VAR1->{"mapping"}{"match"}{"mapping"}{"perlconfig"} = $VAR1->{"mapping"}{"match"}{"mapping"}{"env"}; $VAR1->{"mapping"}{"pl"} = $VAR1->{"mapping"}{"install"}; $VAR1->{"mapping"}{"test"} = $VAR1->{"mapping"}{"install"}; CPAN/API/HOWTO.pod000044400000002053151112047420007275 0ustar00=head1 NAME CPAN::API::HOWTO - a recipe book for programming with CPAN.pm =head1 RECIPES All of these recipes assume that you have put "use CPAN" at the top of your program. =head2 What distribution contains a particular module? my $distribution = CPAN::Shell->expand( "Module", "Data::UUID" )->distribution()->pretty_id(); This returns a string of the form "AUTHORID/TARBALL". If you want the full path and filename to this distribution on a CPAN mirror, then it is C<.../authors/id/A/AU/AUTHORID/TARBALL>. =head2 What modules does a particular distribution contain? CPAN::Index->reload(); my @modules = CPAN::Shell->expand( "Distribution", "JHI/Graph-0.83.tar.gz" )->containsmods(); You may also refer to a distribution in the form A/AU/AUTHORID/TARBALL. =head1 SEE ALSO the main CPAN.pm documentation =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L =head1 AUTHOR David Cantrell =cut CPAN/Queue.pm000044400000015724151112047420006713 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- use strict; package CPAN::Queue::Item; # CPAN::Queue::Item::new ; sub new { my($class,@attr) = @_; my $self = bless { @attr }, $class; return $self; } sub as_string { my($self) = @_; $self->{qmod}; } # r => requires, b => build_requires, c => commandline sub reqtype { my($self) = @_; $self->{reqtype}; } sub optional { my($self) = @_; $self->{optional}; } package CPAN::Queue; # One use of the queue is to determine if we should or shouldn't # announce the availability of a new CPAN module # Now we try to use it for dependency tracking. For that to happen # we need to draw a dependency tree and do the leaves first. This can # easily be reached by running CPAN.pm recursively, but we don't want # to waste memory and run into deep recursion. So what we can do is # this: # CPAN::Queue is the package where the queue is maintained. Dependencies # often have high priority and must be brought to the head of the queue, # possibly by jumping the queue if they are already there. My first code # attempt tried to be extremely correct. Whenever a module needed # immediate treatment, I either unshifted it to the front of the queue, # or, if it was already in the queue, I spliced and let it bypass the # others. This became a too correct model that made it impossible to put # an item more than once into the queue. Why would you need that? Well, # you need temporary duplicates as the manager of the queue is a loop # that # # (1) looks at the first item in the queue without shifting it off # # (2) cares for the item # # (3) removes the item from the queue, *even if its agenda failed and # even if the item isn't the first in the queue anymore* (that way # protecting against never ending queues) # # So if an item has prerequisites, the installation fails now, but we # want to retry later. That's easy if we have it twice in the queue. # # I also expect insane dependency situations where an item gets more # than two lives in the queue. Simplest example is triggered by 'install # Foo Foo Foo'. People make this kind of mistakes and I don't want to # get in the way. I wanted the queue manager to be a dumb servant, not # one that knows everything. # # Who would I tell in this model that the user wants to be asked before # processing? I can't attach that information to the module object, # because not modules are installed but distributions. So I'd have to # tell the distribution object that it should ask the user before # processing. Where would the question be triggered then? Most probably # in CPAN::Distribution::rematein. use vars qw{ @All $VERSION }; $VERSION = "5.5003"; # CPAN::Queue::queue_item ; sub queue_item { my($class,@attr) = @_; my $item = "$class\::Item"->new(@attr); $class->qpush($item); return 1; } # CPAN::Queue::qpush ; sub qpush { my($class,$obj) = @_; push @All, $obj; CPAN->debug(sprintf("in new All[%s]", join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All), )) if $CPAN::DEBUG; } # CPAN::Queue::first ; sub first { my $obj = $All[0]; $obj; } # CPAN::Queue::delete_first ; sub delete_first { my($class,$what) = @_; my $i; for my $i (0..$#All) { if ( $All[$i]->{qmod} eq $what ) { splice @All, $i, 1; last; } } CPAN->debug(sprintf("after delete_first mod[%s] All[%s]", $what, join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) )) if $CPAN::DEBUG; } # CPAN::Queue::jumpqueue ; sub jumpqueue { my $class = shift; my @what = @_; CPAN->debug(sprintf("before jumpqueue All[%s] what[%s]", join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All), join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @what), )) if $CPAN::DEBUG; unless (defined $what[0]{reqtype}) { # apparently it was not the Shell that sent us this enquiry, # treat it as commandline $what[0]{reqtype} = "c"; } my $inherit_reqtype = $what[0]{reqtype} =~ /^(c|r)$/ ? "r" : "b"; WHAT: for my $what_tuple (@what) { my($qmod,$reqtype,$optional) = @$what_tuple{qw(qmod reqtype optional)}; if ($reqtype eq "r" && $inherit_reqtype eq "b" ) { $reqtype = "b"; } my $jumped = 0; for (my $i=0; $i<$#All;$i++) { #prevent deep recursion if ($All[$i]{qmod} eq $qmod) { $jumped++; } } # high jumped values are normal for popular modules when # dealing with large bundles: XML::Simple, # namespace::autoclean, UNIVERSAL::require CPAN->debug("qmod[$qmod]jumped[$jumped]") if $CPAN::DEBUG; my $obj = "$class\::Item"->new( qmod => $qmod, reqtype => $reqtype, optional => !! $optional, ); unshift @All, $obj; } CPAN->debug(sprintf("after jumpqueue All[%s]", join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) )) if $CPAN::DEBUG; } # CPAN::Queue::exists ; sub exists { my($self,$what) = @_; my @all = map { $_->{qmod} } @All; my $exists = grep { $_->{qmod} eq $what } @All; # warn "in exists what[$what] all[@all] exists[$exists]"; $exists; } # CPAN::Queue::delete ; sub delete { my($self,$mod) = @_; @All = grep { $_->{qmod} ne $mod } @All; CPAN->debug(sprintf("after delete mod[%s] All[%s]", $mod, join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All) )) if $CPAN::DEBUG; } # CPAN::Queue::nullify_queue ; sub nullify_queue { @All = (); } # CPAN::Queue::size ; sub size { return scalar @All; } sub reqtype_of { my($self,$mod) = @_; my $best = ""; for my $item (grep { $_->{qmod} eq $mod } @All) { my $c = $item->{reqtype}; if ($c eq "c") { $best = $c; last; } elsif ($c eq "r") { $best = $c; } elsif ($c eq "b") { if ($best eq "") { $best = $c; } } else { die "Panic: in reqtype_of: reqtype[$c] seen, should never happen"; } } return $best; } sub iterator { my $i = 0; return sub { until ($All[$i] || $i > $#All) { $i++; } return if $i > $#All; return $All[$i++] }; } 1; __END__ =head1 NAME CPAN::Queue - internal queue support for CPAN.pm =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut CPAN/URL.pm000044400000001114151112047420006255 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::URL; use overload '""' => "as_string", fallback => 1; # accessors: TEXT(the url string), FROM(DEF=>defaultlist,USER=>urllist), # planned are things like age or quality use vars qw( $VERSION ); $VERSION = "5.5"; sub new { my($class,%args) = @_; bless { %args }, $class; } sub as_string { my($self) = @_; $self->text; } sub text { my($self,$set) = @_; if (defined $set) { $self->{TEXT} = $set; } $self->{TEXT}; } 1; CPAN/Meta/Requirements.pm000044400000052506151112047420011177 0ustar00use v5.10; use strict; use warnings; package CPAN::Meta::Requirements; # ABSTRACT: a set of version requirements for a CPAN dist our $VERSION = '2.143'; use CPAN::Meta::Requirements::Range; #pod =head1 SYNOPSIS #pod #pod use CPAN::Meta::Requirements; #pod #pod my $build_requires = CPAN::Meta::Requirements->new; #pod #pod $build_requires->add_minimum('Library::Foo' => 1.208); #pod #pod $build_requires->add_minimum('Library::Foo' => 2.602); #pod #pod $build_requires->add_minimum('Module::Bar' => 'v1.2.3'); #pod #pod $METAyml->{build_requires} = $build_requires->as_string_hash; #pod #pod =head1 DESCRIPTION #pod #pod A CPAN::Meta::Requirements object models a set of version constraints like #pod those specified in the F or F files in CPAN distributions, #pod and as defined by L. #pod It can be built up by adding more and more constraints, and it will reduce them #pod to the simplest representation. #pod #pod Logically impossible constraints will be identified immediately by thrown #pod exceptions. #pod #pod =cut use Carp (); #pod =method new #pod #pod my $req = CPAN::Meta::Requirements->new; #pod #pod This returns a new CPAN::Meta::Requirements object. It takes an optional #pod hash reference argument. Currently, only one key is supported: #pod #pod =for :list #pod * C -- if provided, when a version cannot be parsed into #pod a version object, this code reference will be called with the invalid #pod version string as first argument, and the module name as second #pod argument. It must return a valid version object. #pod #pod All other keys are ignored. #pod #pod =cut my @valid_options = qw( bad_version_hook ); sub new { my ($class, $options) = @_; $options ||= {}; Carp::croak "Argument to $class\->new() must be a hash reference" unless ref $options eq 'HASH'; my %self = map {; $_ => $options->{$_}} @valid_options; return bless \%self => $class; } #pod =method add_minimum #pod #pod $req->add_minimum( $module => $version ); #pod #pod This adds a new minimum version requirement. If the new requirement is #pod redundant to the existing specification, this has no effect. #pod #pod Minimum requirements are inclusive. C<$version> is required, along with any #pod greater version number. #pod #pod This method returns the requirements object. #pod #pod =method add_maximum #pod #pod $req->add_maximum( $module => $version ); #pod #pod This adds a new maximum version requirement. If the new requirement is #pod redundant to the existing specification, this has no effect. #pod #pod Maximum requirements are inclusive. No version strictly greater than the given #pod version is allowed. #pod #pod This method returns the requirements object. #pod #pod =method add_exclusion #pod #pod $req->add_exclusion( $module => $version ); #pod #pod This adds a new excluded version. For example, you might use these three #pod method calls: #pod #pod $req->add_minimum( $module => '1.00' ); #pod $req->add_maximum( $module => '1.82' ); #pod #pod $req->add_exclusion( $module => '1.75' ); #pod #pod Any version between 1.00 and 1.82 inclusive would be acceptable, except for #pod 1.75. #pod #pod This method returns the requirements object. #pod #pod =method exact_version #pod #pod $req->exact_version( $module => $version ); #pod #pod This sets the version required for the given module to I the given #pod version. No other version would be considered acceptable. #pod #pod This method returns the requirements object. #pod #pod =cut BEGIN { for my $type (qw(maximum exclusion exact_version)) { my $method = "with_$type"; my $to_add = $type eq 'exact_version' ? $type : "add_$type"; my $code = sub { my ($self, $name, $version) = @_; $self->__modify_entry_for($name, $method, $version); return $self; }; no strict 'refs'; *$to_add = $code; } } # add_minimum is optimized compared to generated subs above because # it is called frequently and with "0" or equivalent input sub add_minimum { my ($self, $name, $version) = @_; # stringify $version so that version->new("0.00")->stringify ne "0" # which preserves the user's choice of "0.00" as the requirement if (not defined $version or "$version" eq '0') { return $self if $self->__entry_for($name); Carp::croak("can't add new requirements to finalized requirements") if $self->is_finalized; $self->{requirements}{ $name } = CPAN::Meta::Requirements::Range->with_minimum('0', $name); } else { $self->__modify_entry_for($name, 'with_minimum', $version); } return $self; } #pod =method version_range_for_module #pod #pod $req->version_range_for_module( $another_req_object ); #pod #pod =cut sub version_range_for_module { my ($self, $module) = @_; return $self->{requirements}{$module}; } #pod =method add_requirements #pod #pod $req->add_requirements( $another_req_object ); #pod #pod This method adds all the requirements in the given CPAN::Meta::Requirements #pod object to the requirements object on which it was called. If there are any #pod conflicts, an exception is thrown. #pod #pod This method returns the requirements object. #pod #pod =cut sub add_requirements { my ($self, $req) = @_; for my $module ($req->required_modules) { my $new_range = $req->version_range_for_module($module); $self->__modify_entry_for($module, 'with_range', $new_range); } return $self; } #pod =method accepts_module #pod #pod my $bool = $req->accepts_module($module => $version); #pod #pod Given an module and version, this method returns true if the version #pod specification for the module accepts the provided version. In other words, #pod given: #pod #pod Module => '>= 1.00, < 2.00' #pod #pod We will accept 1.00 and 1.75 but not 0.50 or 2.00. #pod #pod For modules that do not appear in the requirements, this method will return #pod true. #pod #pod =cut sub accepts_module { my ($self, $module, $version) = @_; return 1 unless my $range = $self->__entry_for($module); return $range->accepts($version); } #pod =method clear_requirement #pod #pod $req->clear_requirement( $module ); #pod #pod This removes the requirement for a given module from the object. #pod #pod This method returns the requirements object. #pod #pod =cut sub clear_requirement { my ($self, $module) = @_; return $self unless $self->__entry_for($module); Carp::croak("can't clear requirements on finalized requirements") if $self->is_finalized; delete $self->{requirements}{ $module }; return $self; } #pod =method requirements_for_module #pod #pod $req->requirements_for_module( $module ); #pod #pod This returns a string containing the version requirements for a given module in #pod the format described in L or undef if the given module has no #pod requirements. This should only be used for informational purposes such as error #pod messages and should not be interpreted or used for comparison (see #pod L instead). #pod #pod =cut sub requirements_for_module { my ($self, $module) = @_; my $entry = $self->__entry_for($module); return unless $entry; return $entry->as_string; } #pod =method structured_requirements_for_module #pod #pod $req->structured_requirements_for_module( $module ); #pod #pod This returns a data structure containing the version requirements for a given #pod module or undef if the given module has no requirements. This should #pod not be used for version checks (see L instead). #pod #pod Added in version 2.134. #pod #pod =cut sub structured_requirements_for_module { my ($self, $module) = @_; my $entry = $self->__entry_for($module); return unless $entry; return $entry->as_struct; } #pod =method required_modules #pod #pod This method returns a list of all the modules for which requirements have been #pod specified. #pod #pod =cut sub required_modules { keys %{ $_[0]{requirements} } } #pod =method clone #pod #pod $req->clone; #pod #pod This method returns a clone of the invocant. The clone and the original object #pod can then be changed independent of one another. #pod #pod =cut sub clone { my ($self) = @_; my $new = (ref $self)->new; return $new->add_requirements($self); } sub __entry_for { $_[0]{requirements}{ $_[1] } } sub __modify_entry_for { my ($self, $name, $method, $version) = @_; my $fin = $self->is_finalized; my $old = $self->__entry_for($name); Carp::croak("can't add new requirements to finalized requirements") if $fin and not $old; my $new = ($old || 'CPAN::Meta::Requirements::Range') ->$method($version, $name, $self->{bad_version_hook}); Carp::croak("can't modify finalized requirements") if $fin and $old->as_string ne $new->as_string; $self->{requirements}{ $name } = $new; } #pod =method is_simple #pod #pod This method returns true if and only if all requirements are inclusive minimums #pod -- that is, if their string expression is just the version number. #pod #pod =cut sub is_simple { my ($self) = @_; for my $module ($self->required_modules) { # XXX: This is a complete hack, but also entirely correct. return if not $self->__entry_for($module)->is_simple; } return 1; } #pod =method is_finalized #pod #pod This method returns true if the requirements have been finalized by having the #pod C method called on them. #pod #pod =cut sub is_finalized { $_[0]{finalized} } #pod =method finalize #pod #pod This method marks the requirements finalized. Subsequent attempts to change #pod the requirements will be fatal, I they would result in a change. If they #pod would not alter the requirements, they have no effect. #pod #pod If a finalized set of requirements is cloned, the cloned requirements are not #pod also finalized. #pod #pod =cut sub finalize { $_[0]{finalized} = 1 } #pod =method as_string_hash #pod #pod This returns a reference to a hash describing the requirements using the #pod strings in the L specification. #pod #pod For example after the following program: #pod #pod my $req = CPAN::Meta::Requirements->new; #pod #pod $req->add_minimum('CPAN::Meta::Requirements' => 0.102); #pod #pod $req->add_minimum('Library::Foo' => 1.208); #pod #pod $req->add_maximum('Library::Foo' => 2.602); #pod #pod $req->add_minimum('Module::Bar' => 'v1.2.3'); #pod #pod $req->add_exclusion('Module::Bar' => 'v1.2.8'); #pod #pod $req->exact_version('Xyzzy' => '6.01'); #pod #pod my $hashref = $req->as_string_hash; #pod #pod C<$hashref> would contain: #pod #pod { #pod 'CPAN::Meta::Requirements' => '0.102', #pod 'Library::Foo' => '>= 1.208, <= 2.206', #pod 'Module::Bar' => '>= v1.2.3, != v1.2.8', #pod 'Xyzzy' => '== 6.01', #pod } #pod #pod =cut sub as_string_hash { my ($self) = @_; my %hash = map {; $_ => $self->{requirements}{$_}->as_string } $self->required_modules; return \%hash; } #pod =method add_string_requirement #pod #pod $req->add_string_requirement('Library::Foo' => '>= 1.208, <= 2.206'); #pod $req->add_string_requirement('Library::Foo' => v1.208); #pod #pod This method parses the passed in string and adds the appropriate requirement #pod for the given module. A version can be a Perl "v-string". It understands #pod version ranges as described in the L. For #pod example: #pod #pod =over 4 #pod #pod =item 1.3 #pod #pod =item >= 1.3 #pod #pod =item <= 1.3 #pod #pod =item == 1.3 #pod #pod =item != 1.3 #pod #pod =item > 1.3 #pod #pod =item < 1.3 #pod #pod =item >= 1.3, != 1.5, <= 2.0 #pod #pod A version number without an operator is equivalent to specifying a minimum #pod (C=>). Extra whitespace is allowed. #pod #pod =back #pod #pod =cut sub add_string_requirement { my ($self, $module, $req) = @_; $self->__modify_entry_for($module, 'with_string_requirement', $req); } #pod =method from_string_hash #pod #pod my $req = CPAN::Meta::Requirements->from_string_hash( \%hash ); #pod my $req = CPAN::Meta::Requirements->from_string_hash( \%hash, \%opts ); #pod #pod This is an alternate constructor for a CPAN::Meta::Requirements #pod object. It takes a hash of module names and version requirement #pod strings and returns a new CPAN::Meta::Requirements object. As with #pod add_string_requirement, a version can be a Perl "v-string". Optionally, #pod you can supply a hash-reference of options, exactly as with the L #pod method. #pod #pod =cut sub from_string_hash { my ($class, $hash, $options) = @_; my $self = $class->new($options); for my $module (keys %$hash) { my $req = $hash->{$module}; $self->add_string_requirement($module, $req); } return $self; } 1; # vim: ts=2 sts=2 sw=2 et: __END__ =pod =encoding UTF-8 =head1 NAME CPAN::Meta::Requirements - a set of version requirements for a CPAN dist =head1 VERSION version 2.143 =head1 SYNOPSIS use CPAN::Meta::Requirements; my $build_requires = CPAN::Meta::Requirements->new; $build_requires->add_minimum('Library::Foo' => 1.208); $build_requires->add_minimum('Library::Foo' => 2.602); $build_requires->add_minimum('Module::Bar' => 'v1.2.3'); $METAyml->{build_requires} = $build_requires->as_string_hash; =head1 DESCRIPTION A CPAN::Meta::Requirements object models a set of version constraints like those specified in the F or F files in CPAN distributions, and as defined by L. It can be built up by adding more and more constraints, and it will reduce them to the simplest representation. Logically impossible constraints will be identified immediately by thrown exceptions. =head1 METHODS =head2 new my $req = CPAN::Meta::Requirements->new; This returns a new CPAN::Meta::Requirements object. It takes an optional hash reference argument. Currently, only one key is supported: =over 4 =item * C -- if provided, when a version cannot be parsed into a version object, this code reference will be called with the invalid version string as first argument, and the module name as second argument. It must return a valid version object. =back All other keys are ignored. =head2 add_minimum $req->add_minimum( $module => $version ); This adds a new minimum version requirement. If the new requirement is redundant to the existing specification, this has no effect. Minimum requirements are inclusive. C<$version> is required, along with any greater version number. This method returns the requirements object. =head2 add_maximum $req->add_maximum( $module => $version ); This adds a new maximum version requirement. If the new requirement is redundant to the existing specification, this has no effect. Maximum requirements are inclusive. No version strictly greater than the given version is allowed. This method returns the requirements object. =head2 add_exclusion $req->add_exclusion( $module => $version ); This adds a new excluded version. For example, you might use these three method calls: $req->add_minimum( $module => '1.00' ); $req->add_maximum( $module => '1.82' ); $req->add_exclusion( $module => '1.75' ); Any version between 1.00 and 1.82 inclusive would be acceptable, except for 1.75. This method returns the requirements object. =head2 exact_version $req->exact_version( $module => $version ); This sets the version required for the given module to I the given version. No other version would be considered acceptable. This method returns the requirements object. =head2 version_range_for_module $req->version_range_for_module( $another_req_object ); =head2 add_requirements $req->add_requirements( $another_req_object ); This method adds all the requirements in the given CPAN::Meta::Requirements object to the requirements object on which it was called. If there are any conflicts, an exception is thrown. This method returns the requirements object. =head2 accepts_module my $bool = $req->accepts_module($module => $version); Given an module and version, this method returns true if the version specification for the module accepts the provided version. In other words, given: Module => '>= 1.00, < 2.00' We will accept 1.00 and 1.75 but not 0.50 or 2.00. For modules that do not appear in the requirements, this method will return true. =head2 clear_requirement $req->clear_requirement( $module ); This removes the requirement for a given module from the object. This method returns the requirements object. =head2 requirements_for_module $req->requirements_for_module( $module ); This returns a string containing the version requirements for a given module in the format described in L or undef if the given module has no requirements. This should only be used for informational purposes such as error messages and should not be interpreted or used for comparison (see L instead). =head2 structured_requirements_for_module $req->structured_requirements_for_module( $module ); This returns a data structure containing the version requirements for a given module or undef if the given module has no requirements. This should not be used for version checks (see L instead). Added in version 2.134. =head2 required_modules This method returns a list of all the modules for which requirements have been specified. =head2 clone $req->clone; This method returns a clone of the invocant. The clone and the original object can then be changed independent of one another. =head2 is_simple This method returns true if and only if all requirements are inclusive minimums -- that is, if their string expression is just the version number. =head2 is_finalized This method returns true if the requirements have been finalized by having the C method called on them. =head2 finalize This method marks the requirements finalized. Subsequent attempts to change the requirements will be fatal, I they would result in a change. If they would not alter the requirements, they have no effect. If a finalized set of requirements is cloned, the cloned requirements are not also finalized. =head2 as_string_hash This returns a reference to a hash describing the requirements using the strings in the L specification. For example after the following program: my $req = CPAN::Meta::Requirements->new; $req->add_minimum('CPAN::Meta::Requirements' => 0.102); $req->add_minimum('Library::Foo' => 1.208); $req->add_maximum('Library::Foo' => 2.602); $req->add_minimum('Module::Bar' => 'v1.2.3'); $req->add_exclusion('Module::Bar' => 'v1.2.8'); $req->exact_version('Xyzzy' => '6.01'); my $hashref = $req->as_string_hash; C<$hashref> would contain: { 'CPAN::Meta::Requirements' => '0.102', 'Library::Foo' => '>= 1.208, <= 2.206', 'Module::Bar' => '>= v1.2.3, != v1.2.8', 'Xyzzy' => '== 6.01', } =head2 add_string_requirement $req->add_string_requirement('Library::Foo' => '>= 1.208, <= 2.206'); $req->add_string_requirement('Library::Foo' => v1.208); This method parses the passed in string and adds the appropriate requirement for the given module. A version can be a Perl "v-string". It understands version ranges as described in the L. For example: =over 4 =item 1.3 =item >= 1.3 =item <= 1.3 =item == 1.3 =item != 1.3 =item > 1.3 =item < 1.3 =item >= 1.3, != 1.5, <= 2.0 A version number without an operator is equivalent to specifying a minimum (C=>). Extra whitespace is allowed. =back =head2 from_string_hash my $req = CPAN::Meta::Requirements->from_string_hash( \%hash ); my $req = CPAN::Meta::Requirements->from_string_hash( \%hash, \%opts ); This is an alternate constructor for a CPAN::Meta::Requirements object. It takes a hash of module names and version requirement strings and returns a new CPAN::Meta::Requirements object. As with add_string_requirement, a version can be a Perl "v-string". Optionally, you can supply a hash-reference of options, exactly as with the L method. =for :stopwords cpan testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan =head1 SUPPORT =head2 Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at L. You will be notified automatically of any progress on your issue. =head2 Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. L git clone https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements.git =head1 AUTHORS =over 4 =item * David Golden =item * Ricardo Signes =back =head1 CONTRIBUTORS =for stopwords Ed J Graham Knop Karen Etheridge Leon Timmermans Paul Howarth Ricardo Signes robario Tatsuhiko Miyagawa =over 4 =item * Ed J =item * Graham Knop =item * Karen Etheridge =item * Leon Timmermans =item * Paul Howarth =item * Ricardo Signes =item * robario =item * Tatsuhiko Miyagawa =item * Tatsuhiko Miyagawa =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2010 by David Golden and Ricardo Signes. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut CPAN/Meta/YAML.pm000044400000064652151112047420007263 0ustar00use 5.008001; # sane UTF-8 support use strict; use warnings; package CPAN::Meta::YAML; # git description: v1.75-3-g85169f1 # XXX-INGY is 5.8.1 too old/broken for utf8? # XXX-XDG Lancaster consensus was that it was sufficient until # proven otherwise $CPAN::Meta::YAML::VERSION = '0.020'; ; # original $VERSION removed by Doppelgaenger ##################################################################### # The CPAN::Meta::YAML API. # # These are the currently documented API functions/methods and # exports: use Exporter; our @ISA = qw{ Exporter }; our @EXPORT = qw{ Load Dump }; our @EXPORT_OK = qw{ LoadFile DumpFile freeze thaw }; ### # Functional/Export API: sub Dump { return CPAN::Meta::YAML->new(@_)->_dump_string; } # XXX-INGY Returning last document seems a bad behavior. # XXX-XDG I think first would seem more natural, but I don't know # that it's worth changing now sub Load { my $self = CPAN::Meta::YAML->_load_string(@_); if ( wantarray ) { return @$self; } else { # To match YAML.pm, return the last document return $self->[-1]; } } # XXX-INGY Do we really need freeze and thaw? # XXX-XDG I don't think so. I'd support deprecating them. BEGIN { *freeze = \&Dump; *thaw = \&Load; } sub DumpFile { my $file = shift; return CPAN::Meta::YAML->new(@_)->_dump_file($file); } sub LoadFile { my $file = shift; my $self = CPAN::Meta::YAML->_load_file($file); if ( wantarray ) { return @$self; } else { # Return only the last document to match YAML.pm, return $self->[-1]; } } ### # Object Oriented API: # Create an empty CPAN::Meta::YAML object # XXX-INGY Why do we use ARRAY object? # NOTE: I get it now, but I think it's confusing and not needed. # Will change it on a branch later, for review. # # XXX-XDG I don't support changing it yet. It's a very well-documented # "API" of CPAN::Meta::YAML. I'd support deprecating it, but Adam suggested # we not change it until YAML.pm's own OO API is established so that # users only have one API change to digest, not two sub new { my $class = shift; bless [ @_ ], $class; } # XXX-INGY It probably doesn't matter, and it's probably too late to # change, but 'read/write' are the wrong names. Read and Write # are actions that take data from storage to memory # characters/strings. These take the data to/from storage to native # Perl objects, which the terms dump and load are meant. As long as # this is a legacy quirk to CPAN::Meta::YAML it's ok, but I'd prefer not # to add new {read,write}_* methods to this API. sub read_string { my $self = shift; $self->_load_string(@_); } sub write_string { my $self = shift; $self->_dump_string(@_); } sub read { my $self = shift; $self->_load_file(@_); } sub write { my $self = shift; $self->_dump_file(@_); } ##################################################################### # Constants # Printed form of the unprintable characters in the lowest range # of ASCII characters, listed by ASCII ordinal position. my @UNPRINTABLE = qw( 0 x01 x02 x03 x04 x05 x06 a b t n v f r x0E x0F x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x1A e x1C x1D x1E x1F ); # Printable characters for escapes my %UNESCAPES = ( 0 => "\x00", z => "\x00", N => "\x85", a => "\x07", b => "\x08", t => "\x09", n => "\x0a", v => "\x0b", f => "\x0c", r => "\x0d", e => "\x1b", '\\' => '\\', ); # XXX-INGY # I(ngy) need to decide if these values should be quoted in # CPAN::Meta::YAML or not. Probably yes. # These 3 values have special meaning when unquoted and using the # default YAML schema. They need quotes if they are strings. my %QUOTE = map { $_ => 1 } qw{ null true false }; # The commented out form is simpler, but overloaded the Perl regex # engine due to recursion and backtracking problems on strings # larger than 32,000ish characters. Keep it for reference purposes. # qr/\"((?:\\.|[^\"])*)\"/ my $re_capture_double_quoted = qr/\"([^\\"]*(?:\\.[^\\"]*)*)\"/; my $re_capture_single_quoted = qr/\'([^\']*(?:\'\'[^\']*)*)\'/; # unquoted re gets trailing space that needs to be stripped my $re_capture_unquoted_key = qr/([^:]+(?::+\S(?:[^:]*|.*?(?=:)))*)(?=\s*\:(?:\s+|$))/; my $re_trailing_comment = qr/(?:\s+\#.*)?/; my $re_key_value_separator = qr/\s*:(?:\s+(?:\#.*)?|$)/; ##################################################################### # CPAN::Meta::YAML Implementation. # # These are the private methods that do all the work. They may change # at any time. ### # Loader functions: # Create an object from a file sub _load_file { my $class = ref $_[0] ? ref shift : shift; # Check the file my $file = shift or $class->_error( 'You did not specify a file name' ); $class->_error( "File '$file' does not exist" ) unless -e $file; $class->_error( "'$file' is a directory, not a file" ) unless -f _; $class->_error( "Insufficient permissions to read '$file'" ) unless -r _; # Open unbuffered with strict UTF-8 decoding and no translation layers open( my $fh, "<:unix:encoding(UTF-8)", $file ); unless ( $fh ) { $class->_error("Failed to open file '$file': $!"); } # flock if available (or warn if not possible for OS-specific reasons) if ( _can_flock() ) { flock( $fh, Fcntl::LOCK_SH() ) or warn "Couldn't lock '$file' for reading: $!"; } # slurp the contents my $contents = eval { use warnings FATAL => 'utf8'; local $/; <$fh> }; if ( my $err = $@ ) { $class->_error("Error reading from file '$file': $err"); } # close the file (release the lock) unless ( close $fh ) { $class->_error("Failed to close file '$file': $!"); } $class->_load_string( $contents ); } # Create an object from a string sub _load_string { my $class = ref $_[0] ? ref shift : shift; my $self = bless [], $class; my $string = $_[0]; eval { unless ( defined $string ) { die \"Did not provide a string to load"; } # Check if Perl has it marked as characters, but it's internally # inconsistent. E.g. maybe latin1 got read on a :utf8 layer if ( utf8::is_utf8($string) && ! utf8::valid($string) ) { die \<<'...'; Read an invalid UTF-8 string (maybe mixed UTF-8 and 8-bit character set). Did you decode with lax ":utf8" instead of strict ":encoding(UTF-8)"? ... } # Ensure Unicode character semantics, even for 0x80-0xff utf8::upgrade($string); # Check for and strip any leading UTF-8 BOM $string =~ s/^\x{FEFF}//; # Check for some special cases return $self unless length $string; # Split the file into lines my @lines = grep { ! /^\s*(?:\#.*)?\z/ } split /(?:\015{1,2}\012|\015|\012)/, $string; # Strip the initial YAML header @lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines; # A nibbling parser my $in_document = 0; while ( @lines ) { # Do we have a document header? if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) { # Handle scalar documents shift @lines; if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) { push @$self, $self->_load_scalar( "$1", [ undef ], \@lines ); next; } $in_document = 1; } if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) { # A naked document push @$self, undef; while ( @lines and $lines[0] !~ /^---/ ) { shift @lines; } $in_document = 0; # XXX The final '-+$' is to look for -- which ends up being an # error later. } elsif ( ! $in_document && @$self ) { # only the first document can be explicit die \"CPAN::Meta::YAML failed to classify the line '$lines[0]'"; } elsif ( $lines[0] =~ /^\s*\-(?:\s|$|-+$)/ ) { # An array at the root my $document = [ ]; push @$self, $document; $self->_load_array( $document, [ 0 ], \@lines ); } elsif ( $lines[0] =~ /^(\s*)\S/ ) { # A hash at the root my $document = { }; push @$self, $document; $self->_load_hash( $document, [ length($1) ], \@lines ); } else { # Shouldn't get here. @lines have whitespace-only lines # stripped, and previous match is a line with any # non-whitespace. So this clause should only be reachable via # a perlbug where \s is not symmetric with \S # uncoverable statement die \"CPAN::Meta::YAML failed to classify the line '$lines[0]'"; } } }; my $err = $@; if ( ref $err eq 'SCALAR' ) { $self->_error(${$err}); } elsif ( $err ) { $self->_error($err); } return $self; } sub _unquote_single { my ($self, $string) = @_; return '' unless length $string; $string =~ s/\'\'/\'/g; return $string; } sub _unquote_double { my ($self, $string) = @_; return '' unless length $string; $string =~ s/\\"/"/g; $string =~ s{\\([Nnever\\fartz0b]|x([0-9a-fA-F]{2}))} {(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}}gex; return $string; } # Load a YAML scalar string to the actual Perl scalar sub _load_scalar { my ($self, $string, $indent, $lines) = @_; # Trim trailing whitespace $string =~ s/\s*\z//; # Explitic null/undef return undef if $string eq '~'; # Single quote if ( $string =~ /^$re_capture_single_quoted$re_trailing_comment\z/ ) { return $self->_unquote_single($1); } # Double quote. if ( $string =~ /^$re_capture_double_quoted$re_trailing_comment\z/ ) { return $self->_unquote_double($1); } # Special cases if ( $string =~ /^[\'\"!&]/ ) { die \"CPAN::Meta::YAML does not support a feature in line '$string'"; } return {} if $string =~ /^{}(?:\s+\#.*)?\z/; return [] if $string =~ /^\[\](?:\s+\#.*)?\z/; # Regular unquoted string if ( $string !~ /^[>|]/ ) { die \"CPAN::Meta::YAML found illegal characters in plain scalar: '$string'" if $string =~ /^(?:-(?:\s|$)|[\@\%\`])/ or $string =~ /:(?:\s|$)/; $string =~ s/\s+#.*\z//; return $string; } # Error die \"CPAN::Meta::YAML failed to find multi-line scalar content" unless @$lines; # Check the indent depth $lines->[0] =~ /^(\s*)/; $indent->[-1] = length("$1"); if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) { die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; } # Pull the lines my @multiline = (); while ( @$lines ) { $lines->[0] =~ /^(\s*)/; last unless length($1) >= $indent->[-1]; push @multiline, substr(shift(@$lines), $indent->[-1]); } my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n"; my $t = (substr($string, 1, 1) eq '-') ? '' : "\n"; return join( $j, @multiline ) . $t; } # Load an array sub _load_array { my ($self, $array, $indent, $lines) = @_; while ( @$lines ) { # Check for a new document if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) { while ( @$lines and $lines->[0] !~ /^---/ ) { shift @$lines; } return 1; } # Check the indent level $lines->[0] =~ /^(\s*)/; if ( length($1) < $indent->[-1] ) { return 1; } elsif ( length($1) > $indent->[-1] ) { die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; } if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) { # Inline nested hash my $indent2 = length("$1"); $lines->[0] =~ s/-/ /; push @$array, { }; $self->_load_hash( $array->[-1], [ @$indent, $indent2 ], $lines ); } elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) { shift @$lines; unless ( @$lines ) { push @$array, undef; return 1; } if ( $lines->[0] =~ /^(\s*)\-/ ) { my $indent2 = length("$1"); if ( $indent->[-1] == $indent2 ) { # Null array entry push @$array, undef; } else { # Naked indenter push @$array, [ ]; $self->_load_array( $array->[-1], [ @$indent, $indent2 ], $lines ); } } elsif ( $lines->[0] =~ /^(\s*)\S/ ) { push @$array, { }; $self->_load_hash( $array->[-1], [ @$indent, length("$1") ], $lines ); } else { die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; } } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) { # Array entry with a value shift @$lines; push @$array, $self->_load_scalar( "$2", [ @$indent, undef ], $lines ); } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) { # This is probably a structure like the following... # --- # foo: # - list # bar: value # # ... so lets return and let the hash parser handle it return 1; } else { die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; } } return 1; } # Load a hash sub _load_hash { my ($self, $hash, $indent, $lines) = @_; while ( @$lines ) { # Check for a new document if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) { while ( @$lines and $lines->[0] !~ /^---/ ) { shift @$lines; } return 1; } # Check the indent level $lines->[0] =~ /^(\s*)/; if ( length($1) < $indent->[-1] ) { return 1; } elsif ( length($1) > $indent->[-1] ) { die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'"; } # Find the key my $key; # Quoted keys if ( $lines->[0] =~ s/^\s*$re_capture_single_quoted$re_key_value_separator// ) { $key = $self->_unquote_single($1); } elsif ( $lines->[0] =~ s/^\s*$re_capture_double_quoted$re_key_value_separator// ) { $key = $self->_unquote_double($1); } elsif ( $lines->[0] =~ s/^\s*$re_capture_unquoted_key$re_key_value_separator// ) { $key = $1; $key =~ s/\s+$//; } elsif ( $lines->[0] =~ /^\s*\?/ ) { die \"CPAN::Meta::YAML does not support a feature in line '$lines->[0]'"; } else { die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'"; } if ( exists $hash->{$key} ) { warn "CPAN::Meta::YAML found a duplicate key '$key' in line '$lines->[0]'"; } # Do we have a value? if ( length $lines->[0] ) { # Yes $hash->{$key} = $self->_load_scalar( shift(@$lines), [ @$indent, undef ], $lines ); } else { # An indent shift @$lines; unless ( @$lines ) { $hash->{$key} = undef; return 1; } if ( $lines->[0] =~ /^(\s*)-/ ) { $hash->{$key} = []; $self->_load_array( $hash->{$key}, [ @$indent, length($1) ], $lines ); } elsif ( $lines->[0] =~ /^(\s*)./ ) { my $indent2 = length("$1"); if ( $indent->[-1] >= $indent2 ) { # Null hash entry $hash->{$key} = undef; } else { $hash->{$key} = {}; $self->_load_hash( $hash->{$key}, [ @$indent, length($1) ], $lines ); } } } } return 1; } ### # Dumper functions: # Save an object to a file sub _dump_file { my $self = shift; require Fcntl; # Check the file my $file = shift or $self->_error( 'You did not specify a file name' ); my $fh; # flock if available (or warn if not possible for OS-specific reasons) if ( _can_flock() ) { # Open without truncation (truncate comes after lock) my $flags = Fcntl::O_WRONLY()|Fcntl::O_CREAT(); sysopen( $fh, $file, $flags ) or $self->_error("Failed to open file '$file' for writing: $!"); # Use no translation and strict UTF-8 binmode( $fh, ":raw:encoding(UTF-8)"); flock( $fh, Fcntl::LOCK_EX() ) or warn "Couldn't lock '$file' for reading: $!"; # truncate and spew contents truncate $fh, 0; seek $fh, 0, 0; } else { open $fh, ">:unix:encoding(UTF-8)", $file; } # serialize and spew to the handle print {$fh} $self->_dump_string; # close the file (release the lock) unless ( close $fh ) { $self->_error("Failed to close file '$file': $!"); } return 1; } # Save an object to a string sub _dump_string { my $self = shift; return '' unless ref $self && @$self; # Iterate over the documents my $indent = 0; my @lines = (); eval { foreach my $cursor ( @$self ) { push @lines, '---'; # An empty document if ( ! defined $cursor ) { # Do nothing # A scalar document } elsif ( ! ref $cursor ) { $lines[-1] .= ' ' . $self->_dump_scalar( $cursor ); # A list at the root } elsif ( ref $cursor eq 'ARRAY' ) { unless ( @$cursor ) { $lines[-1] .= ' []'; next; } push @lines, $self->_dump_array( $cursor, $indent, {} ); # A hash at the root } elsif ( ref $cursor eq 'HASH' ) { unless ( %$cursor ) { $lines[-1] .= ' {}'; next; } push @lines, $self->_dump_hash( $cursor, $indent, {} ); } else { die \("Cannot serialize " . ref($cursor)); } } }; if ( ref $@ eq 'SCALAR' ) { $self->_error(${$@}); } elsif ( $@ ) { $self->_error($@); } join '', map { "$_\n" } @lines; } sub _has_internal_string_value { my $value = shift; my $b_obj = B::svref_2object(\$value); # for round trip problem return $b_obj->FLAGS & B::SVf_POK(); } sub _dump_scalar { my $string = $_[1]; my $is_key = $_[2]; # Check this before checking length or it winds up looking like a string! my $has_string_flag = _has_internal_string_value($string); return '~' unless defined $string; return "''" unless length $string; if (Scalar::Util::looks_like_number($string)) { # keys and values that have been used as strings get quoted if ( $is_key || $has_string_flag ) { return qq['$string']; } else { return $string; } } if ( $string =~ /[\x00-\x09\x0b-\x0d\x0e-\x1f\x7f-\x9f\'\n]/ ) { $string =~ s/\\/\\\\/g; $string =~ s/"/\\"/g; $string =~ s/\n/\\n/g; $string =~ s/[\x85]/\\N/g; $string =~ s/([\x00-\x1f])/\\$UNPRINTABLE[ord($1)]/g; $string =~ s/([\x7f-\x9f])/'\x' . sprintf("%X",ord($1))/ge; return qq|"$string"|; } if ( $string =~ /(?:^[~!@#%&*|>?:,'"`{}\[\]]|^-+$|\s|:\z)/ or $QUOTE{$string} ) { return "'$string'"; } return $string; } sub _dump_array { my ($self, $array, $indent, $seen) = @_; if ( $seen->{refaddr($array)}++ ) { die \"CPAN::Meta::YAML does not support circular references"; } my @lines = (); foreach my $el ( @$array ) { my $line = (' ' x $indent) . '-'; my $type = ref $el; if ( ! $type ) { $line .= ' ' . $self->_dump_scalar( $el ); push @lines, $line; } elsif ( $type eq 'ARRAY' ) { if ( @$el ) { push @lines, $line; push @lines, $self->_dump_array( $el, $indent + 1, $seen ); } else { $line .= ' []'; push @lines, $line; } } elsif ( $type eq 'HASH' ) { if ( keys %$el ) { push @lines, $line; push @lines, $self->_dump_hash( $el, $indent + 1, $seen ); } else { $line .= ' {}'; push @lines, $line; } } else { die \"CPAN::Meta::YAML does not support $type references"; } } @lines; } sub _dump_hash { my ($self, $hash, $indent, $seen) = @_; if ( $seen->{refaddr($hash)}++ ) { die \"CPAN::Meta::YAML does not support circular references"; } my @lines = (); foreach my $name ( sort keys %$hash ) { my $el = $hash->{$name}; my $line = (' ' x $indent) . $self->_dump_scalar($name, 1) . ":"; my $type = ref $el; if ( ! $type ) { $line .= ' ' . $self->_dump_scalar( $el ); push @lines, $line; } elsif ( $type eq 'ARRAY' ) { if ( @$el ) { push @lines, $line; push @lines, $self->_dump_array( $el, $indent + 1, $seen ); } else { $line .= ' []'; push @lines, $line; } } elsif ( $type eq 'HASH' ) { if ( keys %$el ) { push @lines, $line; push @lines, $self->_dump_hash( $el, $indent + 1, $seen ); } else { $line .= ' {}'; push @lines, $line; } } else { die \"CPAN::Meta::YAML does not support $type references"; } } @lines; } ##################################################################### # DEPRECATED API methods: # Error storage (DEPRECATED as of 1.57) our $errstr = ''; # Set error sub _error { require Carp; $errstr = $_[1]; $errstr =~ s/ at \S+ line \d+.*//; Carp::croak( $errstr ); } # Retrieve error my $errstr_warned; sub errstr { require Carp; Carp::carp( "CPAN::Meta::YAML->errstr and \$CPAN::Meta::YAML::errstr is deprecated" ) unless $errstr_warned++; $errstr; } ##################################################################### # Helper functions. Possibly not needed. # Use to detect nv or iv use B; # XXX-INGY Is flock CPAN::Meta::YAML's responsibility? # Some platforms can't flock :-( # XXX-XDG I think it is. When reading and writing files, we ought # to be locking whenever possible. People (foolishly) use YAML # files for things like session storage, which has race issues. my $HAS_FLOCK; sub _can_flock { if ( defined $HAS_FLOCK ) { return $HAS_FLOCK; } else { require Config; my $c = \%Config::Config; $HAS_FLOCK = grep { $c->{$_} } qw/d_flock d_fcntl_can_lock d_lockf/; require Fcntl if $HAS_FLOCK; return $HAS_FLOCK; } } # XXX-INGY Is this core in 5.8.1? Can we remove this? # XXX-XDG Scalar::Util 1.18 didn't land until 5.8.8, so we need this ##################################################################### # Use Scalar::Util if possible, otherwise emulate it use Scalar::Util (); BEGIN { local $@; if ( eval { Scalar::Util->VERSION(1.18); } ) { *refaddr = *Scalar::Util::refaddr; } else { eval <<'END_PERL'; # Scalar::Util failed to load or too old sub refaddr { my $pkg = ref($_[0]) or return undef; if ( !! UNIVERSAL::can($_[0], 'can') ) { bless $_[0], 'Scalar::Util::Fake'; } else { $pkg = undef; } "$_[0]" =~ /0x(\w+)/; my $i = do { no warnings 'portable'; hex $1 }; bless $_[0], $pkg if defined $pkg; $i; } END_PERL } } delete $CPAN::Meta::YAML::{refaddr}; 1; # XXX-INGY Doc notes I'm putting up here. Changing the doc when it's wrong # but leaving grey area stuff up here. # # I would like to change Read/Write to Load/Dump below without # changing the actual API names. # # It might be better to put Load/Dump API in the SYNOPSIS instead of the # dubious OO API. # # null and bool explanations may be outdated. =pod =encoding UTF-8 =head1 NAME CPAN::Meta::YAML - Read and write a subset of YAML for CPAN Meta files =head1 VERSION version 0.020 =head1 SYNOPSIS use CPAN::Meta::YAML; # reading a META file open $fh, "<:utf8", "META.yml"; $yaml_text = do { local $/; <$fh> }; $yaml = CPAN::Meta::YAML->read_string($yaml_text) or die CPAN::Meta::YAML->errstr; # finding the metadata $meta = $yaml->[0]; # writing a META file $yaml_text = $yaml->write_string or die CPAN::Meta::YAML->errstr; open $fh, ">:utf8", "META.yml"; print $fh $yaml_text; =head1 DESCRIPTION This module implements a subset of the YAML specification for use in reading and writing CPAN metadata files like F and F. It should not be used for any other general YAML parsing or generation task. NOTE: F (and F) files should be UTF-8 encoded. Users are responsible for proper encoding and decoding. In particular, the C and C methods do B support UTF-8 and should not be used. =head1 SUPPORT This module is currently derived from L by Adam Kennedy. If there are bugs in how it parses a particular META.yml file, please file a bug report in the YAML::Tiny bugtracker: L =head1 SEE ALSO L, L, L =head1 AUTHORS =over 4 =item * Adam Kennedy =item * David Golden =back =head1 CONTRIBUTOR =for stopwords Karen Etheridge Karen Etheridge =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2010 by Adam Kennedy. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut __END__ # ABSTRACT: Read and write a subset of YAML for CPAN Meta files CPAN/Meta/Requirements/Range.pm000044400000046472151112047420012240 0ustar00use v5.10; use strict; use warnings; package CPAN::Meta::Requirements::Range; # ABSTRACT: a set of version requirements for a CPAN dist our $VERSION = '2.143'; use Carp (); #pod =head1 SYNOPSIS #pod #pod use CPAN::Meta::Requirements::Range; #pod #pod my $range = CPAN::Meta::Requirements::Range->with_minimum(1); #pod #pod $range = $range->with_maximum('v2.2'); #pod #pod my $stringified = $range->as_string; #pod #pod =head1 DESCRIPTION #pod #pod A CPAN::Meta::Requirements::Range object models a set of version constraints like #pod those specified in the F or F files in CPAN distributions, #pod and as defined by L; #pod It can be built up by adding more and more constraints, and it will reduce them #pod to the simplest representation. #pod #pod Logically impossible constraints will be identified immediately by thrown #pod exceptions. #pod #pod =cut use Carp (); package CPAN::Meta::Requirements::Range::_Base; # To help ExtUtils::MakeMaker bootstrap CPAN::Meta::Requirements on perls # before 5.10, we fall back to the EUMM bundled compatibility version module if # that's the only thing available. This shouldn't ever happen in a normal CPAN # install of CPAN::Meta::Requirements, as version.pm will be picked up from # prereqs and be available at runtime. BEGIN { eval "use version ()"; ## no critic if ( my $err = $@ ) { eval "use ExtUtils::MakeMaker::version" or die $err; ## no critic } } # from version::vpp sub _find_magic_vstring { my $value = shift; my $tvalue = ''; require B; my $sv = B::svref_2object(\$value); my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef; while ( $magic ) { if ( $magic->TYPE eq 'V' ) { $tvalue = $magic->PTR; $tvalue =~ s/^v?(.+)$/v$1/; last; } else { $magic = $magic->MOREMAGIC; } } return $tvalue; } # Perl 5.10.0 didn't have "is_qv" in version.pm *_is_qv = version->can('is_qv') ? sub { $_[0]->is_qv } : sub { exists $_[0]->{qv} }; # construct once, reuse many times my $V0 = version->new(0); # safe if given an unblessed reference sub _isa_version { UNIVERSAL::isa( $_[0], 'UNIVERSAL' ) && $_[0]->isa('version') } sub _version_object { my ($self, $version, $module, $bad_version_hook) = @_; my ($vobj, $err); if (not defined $version or (!ref($version) && $version eq '0')) { return $V0; } elsif ( ref($version) eq 'version' || ( ref($version) && _isa_version($version) ) ) { $vobj = $version; } else { # hack around version::vpp not handling <3 character vstring literals if ( $INC{'version/vpp.pm'} || $INC{'ExtUtils/MakeMaker/version/vpp.pm'} ) { my $magic = _find_magic_vstring( $version ); $version = $magic if length $magic; } # pad to 3 characters if before 5.8.1 and appears to be a v-string if ( $] < 5.008001 && $version !~ /\A[0-9]/ && substr($version,0,1) ne 'v' && length($version) < 3 ) { $version .= "\0" x (3 - length($version)); } eval { local $SIG{__WARN__} = sub { die "Invalid version: $_[0]" }; # avoid specific segfault on some older version.pm versions die "Invalid version: $version" if $version eq 'version'; $vobj = version->new($version); }; if ( my $err = $@ ) { $vobj = eval { $bad_version_hook->($version, $module) } if ref $bad_version_hook eq 'CODE'; unless (eval { $vobj->isa("version") }) { $err =~ s{ at .* line \d+.*$}{}; die "Can't convert '$version': $err"; } } } # ensure no leading '.' if ( $vobj =~ m{\A\.} ) { $vobj = version->new("0$vobj"); } # ensure normal v-string form if ( _is_qv($vobj) ) { $vobj = version->new($vobj->normal); } return $vobj; } #pod =method with_string_requirement #pod #pod $req->with_string_requirement('>= 1.208, <= 2.206'); #pod $req->with_string_requirement(v1.208); #pod #pod This method parses the passed in string and adds the appropriate requirement. #pod A version can be a Perl "v-string". It understands version ranges as described #pod in the L. For example: #pod #pod =over 4 #pod #pod =item 1.3 #pod #pod =item >= 1.3 #pod #pod =item <= 1.3 #pod #pod =item == 1.3 #pod #pod =item != 1.3 #pod #pod =item > 1.3 #pod #pod =item < 1.3 #pod #pod =item >= 1.3, != 1.5, <= 2.0 #pod #pod A version number without an operator is equivalent to specifying a minimum #pod (C=>). Extra whitespace is allowed. #pod #pod =back #pod #pod =cut my %methods_for_op = ( '==' => [ qw(with_exact_version) ], '!=' => [ qw(with_exclusion) ], '>=' => [ qw(with_minimum) ], '<=' => [ qw(with_maximum) ], '>' => [ qw(with_minimum with_exclusion) ], '<' => [ qw(with_maximum with_exclusion) ], ); sub with_string_requirement { my ($self, $req, $module, $bad_version_hook) = @_; $module //= 'module'; unless ( defined $req && length $req ) { $req = 0; Carp::carp("Undefined requirement for $module treated as '0'"); } my $magic = _find_magic_vstring( $req ); if (length $magic) { return $self->with_minimum($magic, $module, $bad_version_hook); } my @parts = split qr{\s*,\s*}, $req; for my $part (@parts) { my ($op, $ver) = $part =~ m{\A\s*(==|>=|>|<=|<|!=)\s*(.*)\z}; if (! defined $op) { $self = $self->with_minimum($part, $module, $bad_version_hook); } else { Carp::croak("illegal requirement string: $req") unless my $methods = $methods_for_op{ $op }; $self = $self->$_($ver, $module, $bad_version_hook) for @$methods; } } return $self; } #pod =method with_range #pod #pod $range->with_range($other_range) #pod #pod This creates a new range object that is a merge two others. #pod #pod =cut sub with_range { my ($self, $other, $module, $bad_version_hook) = @_; for my $modifier($other->_as_modifiers) { my ($method, $arg) = @$modifier; $self = $self->$method($arg, $module, $bad_version_hook); } return $self; } package CPAN::Meta::Requirements::Range; our @ISA = 'CPAN::Meta::Requirements::Range::_Base'; sub _clone { return (bless { } => $_[0]) unless ref $_[0]; my ($s) = @_; my %guts = ( (exists $s->{minimum} ? (minimum => version->new($s->{minimum})) : ()), (exists $s->{maximum} ? (maximum => version->new($s->{maximum})) : ()), (exists $s->{exclusions} ? (exclusions => [ map { version->new($_) } @{ $s->{exclusions} } ]) : ()), ); bless \%guts => ref($s); } #pod =method with_exact_version #pod #pod $range->with_exact_version( $version ); #pod #pod This sets the version required to I the given #pod version. No other version would be considered acceptable. #pod #pod This method returns the version range object. #pod #pod =cut sub with_exact_version { my ($self, $version, $module, $bad_version_hook) = @_; $module //= 'module'; $self = $self->_clone; $version = $self->_version_object($version, $module, $bad_version_hook); unless ($self->accepts($version)) { $self->_reject_requirements( $module, "exact specification $version outside of range " . $self->as_string ); } return CPAN::Meta::Requirements::Range::_Exact->_new($version); } sub _simplify { my ($self, $module) = @_; if (defined $self->{minimum} and defined $self->{maximum}) { if ($self->{minimum} == $self->{maximum}) { if (grep { $_ == $self->{minimum} } @{ $self->{exclusions} || [] }) { $self->_reject_requirements( $module, "minimum and maximum are both $self->{minimum}, which is excluded", ); } return CPAN::Meta::Requirements::Range::_Exact->_new($self->{minimum}); } if ($self->{minimum} > $self->{maximum}) { $self->_reject_requirements( $module, "minimum $self->{minimum} exceeds maximum $self->{maximum}", ); } } # eliminate irrelevant exclusions if ($self->{exclusions}) { my %seen; @{ $self->{exclusions} } = grep { (! defined $self->{minimum} or $_ >= $self->{minimum}) and (! defined $self->{maximum} or $_ <= $self->{maximum}) and ! $seen{$_}++ } @{ $self->{exclusions} }; } return $self; } #pod =method with_minimum #pod #pod $range->with_minimum( $version ); #pod #pod This adds a new minimum version requirement. If the new requirement is #pod redundant to the existing specification, this has no effect. #pod #pod Minimum requirements are inclusive. C<$version> is required, along with any #pod greater version number. #pod #pod This method returns the version range object. #pod #pod =cut sub with_minimum { my ($self, $minimum, $module, $bad_version_hook) = @_; $module //= 'module'; $self = $self->_clone; $minimum = $self->_version_object( $minimum, $module, $bad_version_hook ); if (defined (my $old_min = $self->{minimum})) { $self->{minimum} = (sort { $b cmp $a } ($minimum, $old_min))[0]; } else { $self->{minimum} = $minimum; } return $self->_simplify($module); } #pod =method with_maximum #pod #pod $range->with_maximum( $version ); #pod #pod This adds a new maximum version requirement. If the new requirement is #pod redundant to the existing specification, this has no effect. #pod #pod Maximum requirements are inclusive. No version strictly greater than the given #pod version is allowed. #pod #pod This method returns the version range object. #pod #pod =cut sub with_maximum { my ($self, $maximum, $module, $bad_version_hook) = @_; $module //= 'module'; $self = $self->_clone; $maximum = $self->_version_object( $maximum, $module, $bad_version_hook ); if (defined (my $old_max = $self->{maximum})) { $self->{maximum} = (sort { $a cmp $b } ($maximum, $old_max))[0]; } else { $self->{maximum} = $maximum; } return $self->_simplify($module); } #pod =method with_exclusion #pod #pod $range->with_exclusion( $version ); #pod #pod This adds a new excluded version. For example, you might use these three #pod method calls: #pod #pod $range->with_minimum( '1.00' ); #pod $range->with_maximum( '1.82' ); #pod #pod $range->with_exclusion( '1.75' ); #pod #pod Any version between 1.00 and 1.82 inclusive would be acceptable, except for #pod 1.75. #pod #pod This method returns the requirements object. #pod #pod =cut sub with_exclusion { my ($self, $exclusion, $module, $bad_version_hook) = @_; $module //= 'module'; $self = $self->_clone; $exclusion = $self->_version_object( $exclusion, $module, $bad_version_hook ); push @{ $self->{exclusions} ||= [] }, $exclusion; return $self->_simplify($module); } sub _as_modifiers { my ($self) = @_; my @mods; push @mods, [ with_minimum => $self->{minimum} ] if exists $self->{minimum}; push @mods, [ with_maximum => $self->{maximum} ] if exists $self->{maximum}; push @mods, map {; [ with_exclusion => $_ ] } @{$self->{exclusions} || []}; return @mods; } #pod =method as_struct #pod #pod $range->as_struct( $module ); #pod #pod This returns a data structure containing the version requirements. This should #pod not be used for version checks (see L instead). #pod #pod =cut sub as_struct { my ($self) = @_; return 0 if ! keys %$self; my @exclusions = @{ $self->{exclusions} || [] }; my @parts; for my $tuple ( [ qw( >= > minimum ) ], [ qw( <= < maximum ) ], ) { my ($op, $e_op, $k) = @$tuple; if (exists $self->{$k}) { my @new_exclusions = grep { $_ != $self->{ $k } } @exclusions; if (@new_exclusions == @exclusions) { push @parts, [ $op, "$self->{ $k }" ]; } else { push @parts, [ $e_op, "$self->{ $k }" ]; @exclusions = @new_exclusions; } } } push @parts, map {; [ "!=", "$_" ] } @exclusions; return \@parts; } #pod =method as_string #pod #pod $range->as_string; #pod #pod This returns a string containing the version requirements in the format #pod described in L. This should only be used for informational #pod purposes such as error messages and should not be interpreted or used for #pod comparison (see L instead). #pod #pod =cut sub as_string { my ($self) = @_; my @parts = @{ $self->as_struct }; return $parts[0][1] if @parts == 1 and $parts[0][0] eq '>='; return join q{, }, map {; join q{ }, @$_ } @parts; } sub _reject_requirements { my ($self, $module, $error) = @_; Carp::croak("illegal requirements for $module: $error") } #pod =method accepts #pod #pod my $bool = $range->accepts($version); #pod #pod Given a version, this method returns true if the version specification #pod accepts the provided version. In other words, given: #pod #pod '>= 1.00, < 2.00' #pod #pod We will accept 1.00 and 1.75 but not 0.50 or 2.00. #pod #pod =cut sub accepts { my ($self, $version) = @_; return if defined $self->{minimum} and $version < $self->{minimum}; return if defined $self->{maximum} and $version > $self->{maximum}; return if defined $self->{exclusions} and grep { $version == $_ } @{ $self->{exclusions} }; return 1; } #pod =method is_simple #pod #pod This method returns true if and only if the range is an inclusive minimum #pod -- that is, if their string expression is just the version number. #pod #pod =cut sub is_simple { my ($self) = @_; # XXX: This is a complete hack, but also entirely correct. return if $self->as_string =~ /\s/; return 1; } package CPAN::Meta::Requirements::Range::_Exact; our @ISA = 'CPAN::Meta::Requirements::Range::_Base'; our $VERSION = '2.141'; BEGIN { eval "use version ()"; ## no critic if ( my $err = $@ ) { eval "use ExtUtils::MakeMaker::version" or die $err; ## no critic } } sub _new { bless { version => $_[1] } => $_[0] } sub accepts { return $_[0]{version} == $_[1] } sub _reject_requirements { my ($self, $module, $error) = @_; Carp::croak("illegal requirements for $module: $error") } sub _clone { (ref $_[0])->_new( version->new( $_[0]{version} ) ) } sub with_exact_version { my ($self, $version, $module, $bad_version_hook) = @_; $module //= 'module'; $version = $self->_version_object($version, $module, $bad_version_hook); return $self->_clone if $self->accepts($version); $self->_reject_requirements( $module, "can't be exactly $version when exact requirement is already $self->{version}", ); } sub with_minimum { my ($self, $minimum, $module, $bad_version_hook) = @_; $module //= 'module'; $minimum = $self->_version_object( $minimum, $module, $bad_version_hook ); return $self->_clone if $self->{version} >= $minimum; $self->_reject_requirements( $module, "minimum $minimum exceeds exact specification $self->{version}", ); } sub with_maximum { my ($self, $maximum, $module, $bad_version_hook) = @_; $module //= 'module'; $maximum = $self->_version_object( $maximum, $module, $bad_version_hook ); return $self->_clone if $self->{version} <= $maximum; $self->_reject_requirements( $module, "maximum $maximum below exact specification $self->{version}", ); } sub with_exclusion { my ($self, $exclusion, $module, $bad_version_hook) = @_; $module //= 'module'; $exclusion = $self->_version_object( $exclusion, $module, $bad_version_hook ); return $self->_clone unless $exclusion == $self->{version}; $self->_reject_requirements( $module, "tried to exclude $exclusion, which is already exactly specified", ); } sub as_string { return "== $_[0]{version}" } sub as_struct { return [ [ '==', "$_[0]{version}" ] ] } sub _as_modifiers { return [ with_exact_version => $_[0]{version} ] } 1; # vim: ts=2 sts=2 sw=2 et: __END__ =pod =encoding UTF-8 =head1 NAME CPAN::Meta::Requirements::Range - a set of version requirements for a CPAN dist =head1 VERSION version 2.143 =head1 SYNOPSIS use CPAN::Meta::Requirements::Range; my $range = CPAN::Meta::Requirements::Range->with_minimum(1); $range = $range->with_maximum('v2.2'); my $stringified = $range->as_string; =head1 DESCRIPTION A CPAN::Meta::Requirements::Range object models a set of version constraints like those specified in the F or F files in CPAN distributions, and as defined by L; It can be built up by adding more and more constraints, and it will reduce them to the simplest representation. Logically impossible constraints will be identified immediately by thrown exceptions. =head1 METHODS =head2 with_string_requirement $req->with_string_requirement('>= 1.208, <= 2.206'); $req->with_string_requirement(v1.208); This method parses the passed in string and adds the appropriate requirement. A version can be a Perl "v-string". It understands version ranges as described in the L. For example: =over 4 =item 1.3 =item >= 1.3 =item <= 1.3 =item == 1.3 =item != 1.3 =item > 1.3 =item < 1.3 =item >= 1.3, != 1.5, <= 2.0 A version number without an operator is equivalent to specifying a minimum (C=>). Extra whitespace is allowed. =back =head2 with_range $range->with_range($other_range) This creates a new range object that is a merge two others. =head2 with_exact_version $range->with_exact_version( $version ); This sets the version required to I the given version. No other version would be considered acceptable. This method returns the version range object. =head2 with_minimum $range->with_minimum( $version ); This adds a new minimum version requirement. If the new requirement is redundant to the existing specification, this has no effect. Minimum requirements are inclusive. C<$version> is required, along with any greater version number. This method returns the version range object. =head2 with_maximum $range->with_maximum( $version ); This adds a new maximum version requirement. If the new requirement is redundant to the existing specification, this has no effect. Maximum requirements are inclusive. No version strictly greater than the given version is allowed. This method returns the version range object. =head2 with_exclusion $range->with_exclusion( $version ); This adds a new excluded version. For example, you might use these three method calls: $range->with_minimum( '1.00' ); $range->with_maximum( '1.82' ); $range->with_exclusion( '1.75' ); Any version between 1.00 and 1.82 inclusive would be acceptable, except for 1.75. This method returns the requirements object. =head2 as_struct $range->as_struct( $module ); This returns a data structure containing the version requirements. This should not be used for version checks (see L instead). =head2 as_string $range->as_string; This returns a string containing the version requirements in the format described in L. This should only be used for informational purposes such as error messages and should not be interpreted or used for comparison (see L instead). =head2 accepts my $bool = $range->accepts($version); Given a version, this method returns true if the version specification accepts the provided version. In other words, given: '>= 1.00, < 2.00' We will accept 1.00 and 1.75 but not 0.50 or 2.00. =head2 is_simple This method returns true if and only if the range is an inclusive minimum -- that is, if their string expression is just the version number. =head1 AUTHORS =over 4 =item * David Golden =item * Ricardo Signes =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2010 by David Golden and Ricardo Signes. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut CPAN/Plugin/Specfile.pm000044400000020751151112047420010613 0ustar00=head1 NAME CPAN::Plugin::Specfile - Proof of concept implementation of a trivial CPAN::Plugin =head1 SYNOPSIS # once in the cpan shell o conf plugin_list push CPAN::Plugin::Specfile # make permanent o conf commit # any time in the cpan shell to write a spec file test Acme::Meta # disable # if it is the last in plugin_list: o conf plugin_list pop # otherwise, determine the index to splice: o conf plugin_list # and then use splice, e.g. to splice position 3: o conf plugin_list splice 3 1 =head1 DESCRIPTION Implemented as a post-test hook, this plugin writes a specfile after every successful test run. The content is also written to the terminal. As a side effect, the timestamps of the written specfiles reflect the linear order of all dependencies. B This code is just a small demo how to use the plugin system of the CPAN shell, not a full fledged spec file writer. Do not expect new features in this plugin. =head2 OPTIONS The target directory to store the spec files in can be set using C as in o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/specfiles-000042 The default directory for this is the C directory in the I directory. =head1 AUTHOR Andreas Koenig , Branislav Zahradnik =cut package CPAN::Plugin::Specfile; our $VERSION = '0.02'; use File::Path; use File::Spec; sub __accessor { my ($class, $key) = @_; no strict 'refs'; *{$class . '::' . $key} = sub { my $self = shift; if (@_) { $self->{$key} = shift; } return $self->{$key}; }; } BEGIN { __PACKAGE__->__accessor($_) for qw(dir dir_default) } sub new { my($class, @rest) = @_; my $self = bless {}, $class; while (my($arg,$val) = splice @rest, 0, 2) { $self->$arg($val); } $self->dir_default(File::Spec->catdir($CPAN::Config->{cpan_home},"plugins",__PACKAGE__)); $self; } sub post_test { my $self = shift; my $distribution_object = shift; my $distribution = $distribution_object->pretty_id; unless ($CPAN::META->has_inst("CPAN::DistnameInfo")){ $CPAN::Frontend->mydie("CPAN::DistnameInfo not installed; cannot continue"); } my $d = CPAN::Shell->expand("Distribution",$distribution) or $CPAN::Frontend->mydie("Unknowns distribution '$distribution'\n"); my $build_dir = $d->{build_dir} or $CPAN::Frontend->mydie("Distribution has not been built yet, cannot proceed"); my %contains = map {($_ => undef)} $d->containsmods; my @m; my $width = 16; my $header = sub { my($header,$value) = @_; push @m, sprintf("%-s:%*s%s\n", $header, $width-length($header), "", $value); }; my $dni = CPAN::DistnameInfo->new($distribution); my $dist = $dni->dist; my $summary = CPAN::Shell->_guess_manpage($d,\%contains,$dist); $header->("Name", "perl-$dist"); my $version = $dni->version; $header->("Version", $version); $header->("Release", "1%{?dist}"); #Summary: Template processing system #Group: Development/Libraries #License: GPL+ or Artistic #URL: http://www.template-toolkit.org/ #Source0: http://search.cpan.org/CPAN/authors/id/A/AB/ABW/Template-Toolkit-%{version}.tar.gz #Patch0: Template-2.22-SREZIC-01.patch #BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) for my $h_tuple ([Summary => $summary], [Group => "Development/Libraries"], [License =>], [URL =>], [BuildRoot => "%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)"], [Requires => "perl(:MODULE_COMPAT_%(eval \"`%{__perl} -V:version`\"; echo \$version))"], ) { my($h,$v) = @$h_tuple; $v = "unknown" unless defined $v; $header->($h, $v); } $header->("Source0", sprintf( "http://search.cpan.org/CPAN/authors/id/%s/%s/%s", substr($distribution,0,1), substr($distribution,0,2), $distribution )); require POSIX; my @xs = glob "$build_dir/*.xs"; # quick try unless (@xs) { require ExtUtils::Manifest; my $manifest_file = "$build_dir/MANIFEST"; my $manifest = ExtUtils::Manifest::maniread($manifest_file); @xs = grep /\.xs$/, keys %$manifest; } if (! @xs) { $header->('BuildArch', 'noarch'); } for my $k (sort keys %contains) { my $m = CPAN::Shell->expand("Module",$k); my $v = $contains{$k} = $m->cpan_version; my $vspec = $v eq "undef" ? "" : " = $v"; $header->("Provides", "perl($k)$vspec"); } if (my $prereq_pm = $d->{prereq_pm}) { my %req; for my $reqkey (keys %$prereq_pm) { while (my($k,$v) = each %{$prereq_pm->{$reqkey}}) { $req{$k} = $v; } } if (-e "$build_dir/Build.PL" && ! exists $req{"Module::Build"}) { $req{"Module::Build"} = 0; } for my $k (sort keys %req) { next if $k eq "perl"; my $v = $req{$k}; my $vspec = defined $v && length $v && $v > 0 ? " >= $v" : ""; $header->(BuildRequires => "perl($k)$vspec"); next if $k =~ /^(Module::Build)$/; # MB is always only a # BuildRequires; if we # turn it into a # Requires, then we # would have to make it # a BuildRequires # everywhere we depend # on *one* MB built # module. $header->(Requires => "perl($k)$vspec"); } } push @m, "\n%define _use_internal_dependency_generator 0 %define __find_requires %{nil} %define __find_provides %{nil} "; push @m, "\n%description\n%{summary}.\n"; push @m, "\n%prep\n%setup -q -n $dist-%{version}\n"; if (-e "$build_dir/Build.PL") { # see http://www.redhat.com/archives/rpm-list/2002-July/msg00110.html about RPM_BUILD_ROOT vs %{buildroot} push @m, <<'EOF'; %build %{__perl} Build.PL --installdirs=vendor --libdoc installvendorman3dir ./Build %install rm -rf $RPM_BUILD_ROOT ./Build install destdir=$RPM_BUILD_ROOT create_packlist=0 find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null \; %{_fixperms} $RPM_BUILD_ROOT/* %check ./Build test EOF } elsif (-e "$build_dir/Makefile.PL") { push @m, <<'EOF'; %build %{__perl} Makefile.PL INSTALLDIRS=vendor make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make pure_install DESTDIR=$RPM_BUILD_ROOT find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';' find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null ';' %{_fixperms} $RPM_BUILD_ROOT/* %check make test EOF } else { $CPAN::Frontend->mydie("'$distribution' has neither a Build.PL nor a Makefile.PL\n"); } push @m, "\n%clean\nrm -rf \$RPM_BUILD_ROOT\n"; my $vendorlib = @xs ? "vendorarch" : "vendorlib"; my $date = POSIX::strftime("%a %b %d %Y", gmtime); my @doc = grep { -e "$build_dir/$_" } qw(README Changes); my $exe_stanza = "\n"; if (my $exe_files = $d->_exe_files) { if (@$exe_files) { $exe_stanza = "%{_mandir}/man1/*.1*\n"; for my $e (@$exe_files) { unless (CPAN->has_inst("File::Basename")) { $CPAN::Frontend->mydie("File::Basename not installed, cannot continue"); } my $basename = File::Basename::basename($e); $exe_stanza .= "/usr/bin/$basename\n"; } } } push @m, < - $version-1 - autogenerated by CPAN::Plugin::Specfile() EOF my $ret = join "", @m; $CPAN::Frontend->myprint($ret); my $target_dir = $self->dir || $self->dir_default; File::Path::mkpath($target_dir); my $outfile = File::Spec->catfile($target_dir, "perl-$dist.spec"); open my $specout, ">", $outfile or $CPAN::Frontend->mydie("Could not open >$outfile: $!"); print $specout $ret; $CPAN::Frontend->myprint("Wrote $outfile"); $ret; } 1; CPAN/InfoObj.pm000044400000015375151112047420007157 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::InfoObj; use strict; use CPAN::Debug; @CPAN::InfoObj::ISA = qw(CPAN::Debug); use Cwd qw(chdir); use vars qw( $VERSION ); $VERSION = "5.5"; sub ro { my $self = shift; exists $self->{RO} and return $self->{RO}; } #-> sub CPAN::InfoObj::cpan_userid sub cpan_userid { my $self = shift; my $ro = $self->ro; if ($ro) { return $ro->{CPAN_USERID} || "N/A"; } else { $self->debug("ID[$self->{ID}]"); # N/A for bundles found locally return "N/A"; } } sub id { shift->{ID}; } #-> sub CPAN::InfoObj::new ; sub new { my $this = bless {}, shift; %$this = @_; $this } # The set method may only be used by code that reads index data or # otherwise "objective" data from the outside world. All session # related material may do anything else with instance variables but # must not touch the hash under the RO attribute. The reason is that # the RO hash gets written to Metadata file and is thus persistent. #-> sub CPAN::InfoObj::safe_chdir ; sub safe_chdir { my($self,$todir) = @_; # we die if we cannot chdir and we are debuggable Carp::confess("safe_chdir called without todir argument") unless defined $todir and length $todir; if (chdir $todir) { $self->debug(sprintf "changed directory to %s", CPAN::anycwd()) if $CPAN::DEBUG; } else { if (-e $todir) { unless (-x $todir) { unless (chmod 0755, $todir) { my $cwd = CPAN::anycwd(); $CPAN::Frontend->mywarn("I have neither the -x permission nor the ". "permission to change the permission; cannot ". "chdir to '$todir'\n"); $CPAN::Frontend->mysleep(5); $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] }. qq{to todir[$todir]: $!}); } } } else { $CPAN::Frontend->mydie("Directory '$todir' has gone. Cannot continue.\n"); } if (chdir $todir) { $self->debug(sprintf "changed directory to %s", CPAN::anycwd()) if $CPAN::DEBUG; } else { my $cwd = CPAN::anycwd(); $CPAN::Frontend->mydie(qq{Could not chdir from cwd[$cwd] }. qq{to todir[$todir] (a chmod has been issued): $!}); } } } #-> sub CPAN::InfoObj::set ; sub set { my($self,%att) = @_; my $class = ref $self; # This must be ||=, not ||, because only if we write an empty # reference, only then the set method will write into the readonly # area. But for Distributions that spring into existence, maybe # because of a typo, we do not like it that they are written into # the readonly area and made permanent (at least for a while) and # that is why we do not "allow" other places to call ->set. unless ($self->id) { CPAN->debug("Bug? Empty ID, rejecting"); return; } my $ro = $self->{RO} = $CPAN::META->{readonly}{$class}{$self->id} ||= {}; while (my($k,$v) = each %att) { $ro->{$k} = $v; } } #-> sub CPAN::InfoObj::as_glimpse ; sub as_glimpse { my($self) = @_; my(@m); my $class = ref($self); $class =~ s/^CPAN:://; my $id = $self->can("pretty_id") ? $self->pretty_id : $self->{ID}; push @m, sprintf "%-15s %s\n", $class, $id; join "", @m; } #-> sub CPAN::InfoObj::as_string ; sub as_string { my($self) = @_; my(@m); my $class = ref($self); $class =~ s/^CPAN:://; push @m, $class, " id = $self->{ID}\n"; my $ro; unless ($ro = $self->ro) { if (substr($self->{ID},-1,1) eq ".") { # directory $ro = +{}; } else { $CPAN::Frontend->mywarn("Unknown object $self->{ID}\n"); $CPAN::Frontend->mysleep(5); return; } } for (sort keys %$ro) { # next if m/^(ID|RO)$/; my $extra = ""; if ($_ eq "CPAN_USERID") { $extra .= " ("; $extra .= $self->fullname; my $email; # old perls! if ($email = $CPAN::META->instance("CPAN::Author", $self->cpan_userid )->email) { $extra .= " <$email>"; } else { $extra .= " "; } $extra .= ")"; } elsif ($_ eq "FULLNAME") { # potential UTF-8 conversion push @m, sprintf " %-12s %s\n", $_, $self->fullname; next; } next unless defined $ro->{$_}; push @m, sprintf " %-12s %s%s\n", $_, $ro->{$_}, $extra; } KEY: for (sort keys %$self) { next if m/^(ID|RO)$/; unless (defined $self->{$_}) { delete $self->{$_}; next KEY; } if (ref($self->{$_}) eq "ARRAY") { push @m, sprintf " %-12s %s\n", $_, "@{$self->{$_}}"; } elsif (ref($self->{$_}) eq "HASH") { my $value; if (/^CONTAINSMODS$/) { $value = join(" ",sort keys %{$self->{$_}}); } elsif (/^prereq_pm$/) { my @value; my $v = $self->{$_}; for my $x (sort keys %$v) { my @svalue; for my $y (sort keys %{$v->{$x}}) { push @svalue, "$y=>$v->{$x}{$y}"; } push @value, "$x\:" . join ",", @svalue if @svalue; } $value = join ";", @value; } else { $value = $self->{$_}; } push @m, sprintf( " %-12s %s\n", $_, $value, ); } else { push @m, sprintf " %-12s %s\n", $_, $self->{$_}; } } join "", @m, "\n"; } #-> sub CPAN::InfoObj::fullname ; sub fullname { my($self) = @_; $CPAN::META->instance("CPAN::Author",$self->cpan_userid)->fullname; } #-> sub CPAN::InfoObj::dump ; sub dump { my($self, $what) = @_; unless ($CPAN::META->has_inst("Data::Dumper")) { $CPAN::Frontend->mydie("dump command requires Data::Dumper installed"); } local $Data::Dumper::Sortkeys; $Data::Dumper::Sortkeys = 1; my $out = Data::Dumper::Dumper($what ? eval $what : $self); if (length $out > 100000) { my $fh_pager = FileHandle->new; local($SIG{PIPE}) = "IGNORE"; my $pager = $CPAN::Config->{'pager'} || "cat"; $fh_pager->open("|$pager") or die "Could not open pager $pager\: $!"; $fh_pager->print($out); close $fh_pager; } else { $CPAN::Frontend->myprint($out); } } 1; CPAN/Nox.pm000044400000001671151112047420006367 0ustar00package CPAN::Nox; use strict; use vars qw($VERSION @EXPORT); BEGIN{ $CPAN::Suppress_readline=1 unless defined $CPAN::term; } use Exporter (); @CPAN::ISA = ('Exporter'); use CPAN; $VERSION = "5.5001"; $CPAN::META->has_inst('Digest::MD5','no'); $CPAN::META->has_inst('LWP','no'); $CPAN::META->has_inst('Compress::Zlib','no'); @EXPORT = @CPAN::EXPORT; *AUTOLOAD = \&CPAN::AUTOLOAD; 1; __END__ =head1 NAME CPAN::Nox - Wrapper around CPAN.pm without using any XS module =head1 SYNOPSIS Interactive mode: perl -MCPAN::Nox -e shell; =head1 DESCRIPTION This package has the same functionality as CPAN.pm, but tries to prevent the usage of compiled extensions during its own execution. Its primary purpose is a rescue in case you upgraded perl and broke binary compatibility somehow. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L =cut CPAN/FTP.pm000044400000141267151112047420006262 0ustar00# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::FTP; use strict; use Errno (); use Fcntl qw(:flock); use File::Basename qw(dirname); use File::Path qw(mkpath); use CPAN::FTP::netrc; use vars qw($connect_to_internet_ok $Ua $Thesite $ThesiteURL $Themethod); @CPAN::FTP::ISA = qw(CPAN::Debug); use vars qw( $VERSION ); $VERSION = "5.5016"; sub _plus_append_open { my($fh, $file) = @_; my $parent_dir = dirname $file; mkpath $parent_dir; my($cnt); until (open $fh, "+>>$file") { next if exists &Errno::EAGAIN && $! == &Errno::EAGAIN; # don't increment on EAGAIN $CPAN::Frontend->mydie("Could not open '$file' after 10000 tries: $!") if ++$cnt > 100000; sleep 0.0001; mkpath $parent_dir; } } #-> sub CPAN::FTP::ftp_statistics # if they want to rewrite, they need to pass in a filehandle sub _ftp_statistics { my($self,$fh) = @_; my $ftpstats_size = $CPAN::Config->{ftpstats_size}; return if defined $ftpstats_size && $ftpstats_size <= 0; my $locktype = $fh ? LOCK_EX : LOCK_SH; # XXX On Windows flock() implements mandatory locking, so we can # XXX only use shared locking to still allow _yaml_loadfile() to # XXX read from the file using a different filehandle. $locktype = LOCK_SH if $^O eq "MSWin32"; $fh ||= FileHandle->new; my $file = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); _plus_append_open($fh,$file); my $sleep = 1; my $waitstart; while (!CPAN::_flock($fh, $locktype|LOCK_NB)) { $waitstart ||= localtime(); if ($sleep>3) { my $now = localtime(); $CPAN::Frontend->mywarn("$now: waiting for read lock on '$file' (since $waitstart)\n"); } sleep($sleep); # this sleep must not be overridden; # Frontend->mysleep with AUTOMATED_TESTING has # provoked complete lock contention on my NFS if ($sleep <= 6) { $sleep+=0.5; } else { # retry to get a fresh handle. If it is NFS and the handle is stale, we will never get an flock _plus_append_open($fh, $file); } } my $stats = eval { CPAN->_yaml_loadfile($file, {loadblessed => 1}); }; if ($@) { if (ref $@) { if (ref $@ eq "CPAN::Exception::yaml_not_installed") { chomp $@; $CPAN::Frontend->myprintonce("Warning (usually harmless): $@\n"); return; } elsif (ref $@ eq "CPAN::Exception::yaml_process_error") { my $time = time; my $to = "$file.$time"; $CPAN::Frontend->mywarn("Error reading '$file': $@ Trying to stash it away as '$to' to prevent further interruptions. You may want to remove that file later.\n"); # may fail because somebody else has moved it away in the meantime: rename $file, $to or $CPAN::Frontend->mywarn("Could not rename '$file' to '$to': $!\n"); return; } } else { $CPAN::Frontend->mydie($@); } } CPAN::_flock($fh, LOCK_UN); return $stats->[0]; } #-> sub CPAN::FTP::_mytime sub _mytime () { if (CPAN->has_inst("Time::HiRes")) { return Time::HiRes::time(); } else { return time; } } #-> sub CPAN::FTP::_new_stats sub _new_stats { my($self,$file) = @_; my $ret = { file => $file, attempts => [], start => _mytime, }; $ret; } #-> sub CPAN::FTP::_add_to_statistics sub _add_to_statistics { my($self,$stats) = @_; my $yaml_module = CPAN::_yaml_module(); $self->debug("yaml_module[$yaml_module]") if $CPAN::DEBUG; if ($CPAN::META->has_inst($yaml_module)) { $stats->{thesiteurl} = $ThesiteURL; $stats->{end} = CPAN::FTP::_mytime(); my $fh = FileHandle->new; my $time = time; my $sdebug = 0; my @debug; @debug = $time if $sdebug; my $fullstats = $self->_ftp_statistics($fh); close $fh if $fh && defined(fileno($fh)); $fullstats->{history} ||= []; push @debug, scalar @{$fullstats->{history}} if $sdebug; push @debug, time if $sdebug; push @{$fullstats->{history}}, $stats; # YAML.pm 0.62 is unacceptably slow with 999; # YAML::Syck 0.82 has no noticable performance problem with 999; my $ftpstats_size = $CPAN::Config->{ftpstats_size}; $ftpstats_size = 99 unless defined $ftpstats_size; my $ftpstats_period = $CPAN::Config->{ftpstats_period} || 14; while ( @{$fullstats->{history} || []} && ( @{$fullstats->{history}} > $ftpstats_size || $time - $fullstats->{history}[0]{start} > 86400*$ftpstats_period ) ) { shift @{$fullstats->{history}} } push @debug, scalar @{$fullstats->{history}} if $sdebug; push @debug, time if $sdebug; push @debug, scalar localtime($fullstats->{history}[0]{start}) if $sdebug; # need no eval because if this fails, it is serious my $sfile = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); CPAN->_yaml_dumpfile("$sfile.$$",$fullstats); if ( $sdebug ) { local $CPAN::DEBUG = 512; # FTP push @debug, time; CPAN->debug(sprintf("DEBUG history: before_read[%d]before[%d]at[%d]". "after[%d]at[%d]oldest[%s]dumped backat[%d]", @debug, )); } # Win32 cannot rename a file to an existing filename unlink($sfile) if ($^O eq 'MSWin32' or $^O eq 'os2'); _copy_stat($sfile, "$sfile.$$") if -e $sfile; rename "$sfile.$$", $sfile or $CPAN::Frontend->mywarn("Could not rename '$sfile.$$' to '$sfile': $!\nGiving up\n"); } } # Copy some stat information (owner, group, mode and) from one file to # another. # This is a utility function which might be moved to a utility repository. #-> sub CPAN::FTP::_copy_stat sub _copy_stat { my($src, $dest) = @_; my @stat = stat($src); if (!@stat) { $CPAN::Frontend->mywarn("Can't stat '$src': $!\n"); return; } eval { chmod $stat[2], $dest or $CPAN::Frontend->mywarn("Can't chmod '$dest' to " . sprintf("0%o", $stat[2]) . ": $!\n"); }; warn $@ if $@; eval { chown $stat[4], $stat[5], $dest or do { my $save_err = $!; # otherwise it's lost in the get... calls $CPAN::Frontend->mywarn("Can't chown '$dest' to " . (getpwuid($stat[4]))[0] . "/" . (getgrgid($stat[5]))[0] . ": $save_err\n" ); }; }; warn $@ if $@; } # if file is CHECKSUMS, suggest the place where we got the file to be # checked from, maybe only for young files? #-> sub CPAN::FTP::_recommend_url_for sub _recommend_url_for { my($self, $file, $urllist) = @_; if ($file =~ s|/CHECKSUMS(.gz)?$||) { my $fullstats = $self->_ftp_statistics(); my $history = $fullstats->{history} || []; while (my $last = pop @$history) { last if $last->{end} - time > 3600; # only young results are interesting next unless $last->{file}; # dirname of nothing dies! next unless $file eq dirname($last->{file}); return $last->{thesiteurl}; } } if ($CPAN::Config->{randomize_urllist} && rand(1) < $CPAN::Config->{randomize_urllist} ) { $urllist->[int rand scalar @$urllist]; } else { return (); } } #-> sub CPAN::FTP::_get_urllist sub _get_urllist { my($self, $with_defaults) = @_; $with_defaults ||= 0; CPAN->debug("with_defaults[$with_defaults]") if $CPAN::DEBUG; $CPAN::Config->{urllist} ||= []; unless (ref $CPAN::Config->{urllist} eq 'ARRAY') { $CPAN::Frontend->mywarn("Malformed urllist; ignoring. Configuration file corrupt?\n"); $CPAN::Config->{urllist} = []; } my @urllist = grep { defined $_ and length $_ } @{$CPAN::Config->{urllist}}; push @urllist, @CPAN::Defaultsites if $with_defaults; for my $u (@urllist) { CPAN->debug("u[$u]") if $CPAN::DEBUG; if (UNIVERSAL::can($u,"text")) { $u->{TEXT} .= "/" unless substr($u->{TEXT},-1) eq "/"; } else { $u .= "/" unless substr($u,-1) eq "/"; $u = CPAN::URL->new(TEXT => $u, FROM => "USER"); } } \@urllist; } #-> sub CPAN::FTP::ftp_get ; sub ftp_get { my($class,$host,$dir,$file,$target) = @_; $class->debug( qq[Going to fetch file [$file] from dir [$dir] on host [$host] as local [$target]\n] ) if $CPAN::DEBUG; my $ftp = Net::FTP->new($host); unless ($ftp) { $CPAN::Frontend->mywarn(" Could not connect to host '$host' with Net::FTP\n"); return; } return 0 unless defined $ftp; $ftp->debug(1) if $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG; $class->debug(qq[Going to login("anonymous","$Config::Config{cf_email}")]); unless ( $ftp->login("anonymous",$Config::Config{'cf_email'}) ) { my $msg = $ftp->message; $CPAN::Frontend->mywarn(" Couldn't login on $host: $msg\n"); return; } unless ( $ftp->cwd($dir) ) { my $msg = $ftp->message; $CPAN::Frontend->mywarn(" Couldn't cwd $dir: $msg\n"); return; } $ftp->binary; $class->debug(qq[Going to ->get("$file","$target")\n]) if $CPAN::DEBUG; unless ( $ftp->get($file,$target) ) { my $msg = $ftp->message; $CPAN::Frontend->mywarn(" Couldn't fetch $file from $host: $msg\n"); return; } $ftp->quit; # it's ok if this fails return 1; } # If more accuracy is wanted/needed, Chris Leach sent me this patch... # > *** /install/perl/live/lib/CPAN.pm- Wed Sep 24 13:08:48 1997 # > --- /tmp/cp Wed Sep 24 13:26:40 1997 # > *************** # > *** 1562,1567 **** # > --- 1562,1580 ---- # > return 1 if substr($url,0,4) eq "file"; # > return 1 unless $url =~ m|://([^/]+)|; # > my $host = $1; # > + my $proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'}; # > + if ($proxy) { # > + $proxy =~ m|://([^/:]+)|; # > + $proxy = $1; # > + my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'}; # > + if ($noproxy) { # > + if ($host !~ /$noproxy$/) { # > + $host = $proxy; # > + } # > + } else { # > + $host = $proxy; # > + } # > + } # > require Net::Ping; # > return 1 unless $Net::Ping::VERSION >= 2; # > my $p; #-> sub CPAN::FTP::localize ; sub localize { my($self,$file,$aslocal,$force,$with_defaults) = @_; $force ||= 0; Carp::croak( "Usage: ->localize(cpan_file,as_local_file[,\$force])" ) unless defined $aslocal; if ($CPAN::DEBUG){ require Carp; my $longmess = Carp::longmess(); $self->debug("file[$file] aslocal[$aslocal] force[$force] carplongmess[$longmess]"); } for ($CPAN::Config->{connect_to_internet_ok}) { $connect_to_internet_ok = $_ if not defined $connect_to_internet_ok and defined $_; } my $ph = $CPAN::Config->{pushy_https}; if (!defined $ph || $ph) { return $self->localize_2021($file,$aslocal,$force,$with_defaults); } else { return $self->localize_1995ff($file,$aslocal,$force,$with_defaults); } } sub have_promising_aslocal { my($self, $aslocal, $force) = @_; if (-f $aslocal && -r _ && !($force & 1)) { my $size; if ($size = -s $aslocal) { $self->debug("aslocal[$aslocal]size[$size]") if $CPAN::DEBUG; return 1; } else { # empty file from a previous unsuccessful attempt to download it unlink $aslocal or $CPAN::Frontend->mydie("Found a zero-length '$aslocal' that I ". "could not remove."); } } return; } #-> sub CPAN::FTP::localize ; sub localize_2021 { my($self,$file,$aslocal,$force,$with_defaults) = @_; return $aslocal if $self->have_promising_aslocal($aslocal, $force); my($aslocal_dir) = dirname($aslocal); my $ret; $self->mymkpath($aslocal_dir); my $aslocal_tempfile = $aslocal . ".tmp" . $$; my $base; if ( ($CPAN::META->has_usable('HTTP::Tiny') && $CPAN::META->has_usable('Net::SSLeay') && $CPAN::META->has_usable('IO::Socket::SSL') ) || $CPAN::Config->{curl} || $CPAN::Config->{wget} ) { for my $prx (qw(https_proxy no_proxy)) { $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; } $base = "https://cpan.org/"; } else { my @missing_modules = grep { ! $CPAN::META->has_usable($_) } qw(HTTP::Tiny Net::SSLeay IO::Socket::SSL); my $miss = join ", ", map { "'$_'" } @missing_modules; my $modules = @missing_modules == 1 ? "module" : "modules"; $CPAN::Frontend->mywarn("Missing or unusable $modules $miss, and found neither curl nor wget installed.\n"); if ($CPAN::META->has_usable('HTTP::Tiny')) { $CPAN::Frontend->mywarn("Need to fall back to http.\n") } for my $prx (qw(http_proxy no_proxy)) { $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; } $base = "http://www.cpan.org/"; } $ret = $self->hostdl_2021($base,$file,$aslocal_tempfile); if ($ret) { # c&p from below CPAN->debug("ret[$ret]aslocal[$aslocal]") if $CPAN::DEBUG; if ($ret eq $aslocal_tempfile) { # if we got it exactly as we asked for, only then we # want to rename rename $aslocal_tempfile, $aslocal or $CPAN::Frontend->mydie("Error while trying to rename ". "'$ret' to '$aslocal': $!"); $ret = $aslocal; } } else { unlink $aslocal_tempfile; return; } return $ret; } sub hostdl_2021 { my($self, $base, $file, $aslocal) = @_; # the $aslocal is $aslocal_tempfile in the caller (old convention) my $proxy_vars = $self->_proxy_vars($base); my($proto) = $base =~ /^(https?)/; my $url = "$base$file"; # hostdl_2021 may be called with either http or https urls if ( $CPAN::META->has_usable('HTTP::Tiny') && ( $proto eq "http" || ( $CPAN::META->has_usable('Net::SSLeay') && $CPAN::META->has_usable('IO::Socket::SSL') ) ) ){ # mostly c&p from below require CPAN::HTTP::Client; my $chc = CPAN::HTTP::Client->new( proxy => $CPAN::Config->{http_proxy} || $ENV{http_proxy}, no_proxy => $CPAN::Config->{no_proxy} || $ENV{no_proxy}, ); for my $try ( $url, ( $url !~ /\.gz(?!\n)\Z/ ? "$url.gz" : () ) ) { $CPAN::Frontend->myprint("Fetching with HTTP::Tiny:\n$try\n"); my $res = eval { $chc->mirror($try, $aslocal) }; if ( $res && $res->{success} ) { my $now = time; utime $now, $now, $aslocal; # download time is more # important than upload # time return $aslocal; } elsif ( $res && $res->{status} ne '599') { $CPAN::Frontend->myprint(sprintf( "HTTP::Tiny failed with code[%s] message[%s]\n", $res->{status}, $res->{reason}, ) ); } elsif ( $res && $res->{status} eq '599') { $CPAN::Frontend->myprint(sprintf( "HTTP::Tiny failed with an internal error: %s\n", $res->{content}, ) ); } else { my $err = $@ || 'Unknown error'; $CPAN::Frontend->myprint(sprintf( "Error downloading with HTTP::Tiny: %s\n", $err ) ); } } } elsif ($CPAN::Config->{curl} || $CPAN::Config->{wget}){ # c&p from further down my($src_switch, $stdout_redir); my($devnull) = $CPAN::Config->{devnull} || ""; DLPRG: for my $dlprg (qw(curl wget)) { my $dlprg_configured = $CPAN::Config->{$dlprg}; next unless defined $dlprg_configured && length $dlprg_configured; my $funkyftp = CPAN::HandleConfig->safe_quote($dlprg_configured); if ($dlprg eq "wget") { $src_switch = " -O \"$aslocal\""; $stdout_redir = ""; } elsif ($dlprg eq 'curl') { $src_switch = ' -L -f -s -S --netrc-optional'; $stdout_redir = " > \"$aslocal\""; if ($proxy_vars->{http_proxy}) { $src_switch .= qq{ -U "$proxy_vars->{proxy_user}:$proxy_vars->{proxy_pass}" -x "$proxy_vars->{http_proxy}"}; } } $CPAN::Frontend->myprint( qq[ Trying with $funkyftp$src_switch to get $url ]); my($system) = "$funkyftp$src_switch \"$url\" $devnull$stdout_redir"; $self->debug("system[$system]") if $CPAN::DEBUG; my($wstatus) = system($system); if ($wstatus == 0) { return $aslocal; } else { my $estatus = $wstatus >> 8; my $size = -f $aslocal ? ", left\n$aslocal with size ".-s _ : "\nWarning: expected file [$aslocal] doesn't exist"; $CPAN::Frontend->myprint(qq{ Function system("$system") returned status $estatus (wstat $wstatus)$size }); } } # DLPRG } # curl, wget return; } #-> sub CPAN::FTP::localize ; sub localize_1995ff { my($self,$file,$aslocal,$force,$with_defaults) = @_; if ($^O eq 'MacOS') { # Comment by AK on 2000-09-03: Uniq short filenames would be # available in CHECKSUMS file my($name, $path) = File::Basename::fileparse($aslocal, ''); if (length($name) > 31) { $name =~ s/( \.( readme(\.(gz|Z))? | (tar\.)?(gz|Z) | tgz | zip | pm\.(gz|Z) ) )$//x; my $suf = $1; my $size = 31 - length($suf); while (length($name) > $size) { chop $name; } $name .= $suf; $aslocal = File::Spec->catfile($path, $name); } } return $aslocal if $self->have_promising_aslocal($aslocal, $force); my($maybe_restore) = 0; if (-f $aslocal) { rename $aslocal, "$aslocal.bak$$"; $maybe_restore++; } my($aslocal_dir) = dirname($aslocal); # Inheritance is not easier to manage than a few if/else branches if ($CPAN::META->has_usable('LWP::UserAgent')) { unless ($Ua) { CPAN::LWP::UserAgent->config; eval {$Ua = CPAN::LWP::UserAgent->new;}; # Why is has_usable still not fit enough? if ($@) { $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n") if $CPAN::DEBUG; } else { my($var); $Ua->proxy('ftp', $var) if $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy}; $Ua->proxy('http', $var) if $var = $CPAN::Config->{http_proxy} || $ENV{http_proxy}; $Ua->no_proxy($var) if $var = $CPAN::Config->{no_proxy} || $ENV{no_proxy}; } } } for my $prx (qw(ftp_proxy http_proxy no_proxy)) { $ENV{$prx} = $CPAN::Config->{$prx} if $CPAN::Config->{$prx}; } # Try the list of urls for each single object. We keep a record # where we did get a file from my(@reordered,$last); my $ccurllist = $self->_get_urllist($with_defaults); $last = $#$ccurllist; if ($force & 2) { # local cpans probably out of date, don't reorder @reordered = (0..$last); } else { @reordered = sort { (substr($ccurllist->[$b],0,4) eq "file") <=> (substr($ccurllist->[$a],0,4) eq "file") or defined($ThesiteURL) and ($ccurllist->[$b] eq $ThesiteURL) <=> ($ccurllist->[$a] eq $ThesiteURL) } 0..$last; } my(@levels); $Themethod ||= ""; $self->debug("Themethod[$Themethod]reordered[@reordered]") if $CPAN::DEBUG; my @all_levels = ( ["dleasy", "file"], ["dleasy"], ["dlhard"], ["dlhardest"], ["dleasy", "http","defaultsites"], ["dlhard", "http","defaultsites"], ["dleasy", "ftp", "defaultsites"], ["dlhard", "ftp", "defaultsites"], ["dlhardest","", "defaultsites"], ); if ($Themethod) { @levels = grep {$_->[0] eq $Themethod} @all_levels; push @levels, grep {$_->[0] ne $Themethod} @all_levels; } else { @levels = @all_levels; } @levels = qw/dleasy/ if $^O eq 'MacOS'; my($levelno); local $ENV{FTP_PASSIVE} = exists $CPAN::Config->{ftp_passive} ? $CPAN::Config->{ftp_passive} : 1; my $ret; my $stats = $self->_new_stats($file); LEVEL: for $levelno (0..$#levels) { my $level_tuple = $levels[$levelno]; my($level,$scheme,$sitetag) = @$level_tuple; $self->mymkpath($aslocal_dir) unless $scheme && "file" eq $scheme; my $defaultsites = $sitetag && $sitetag eq "defaultsites" && !@$ccurllist; my @urllist; if ($defaultsites) { unless (defined $connect_to_internet_ok) { $CPAN::Frontend->myprint(sprintf qq{ I would like to connect to one of the following sites to get '%s': %s }, $file, join("",map { " ".$_->text."\n" } @CPAN::Defaultsites), ); my $answer = CPAN::Shell::colorable_makemaker_prompt("Is it OK to try to connect to the Internet?", "yes"); if ($answer =~ /^y/i) { $connect_to_internet_ok = 1; } else { $connect_to_internet_ok = 0; } } if ($connect_to_internet_ok) { @urllist = @CPAN::Defaultsites; } else { my $sleep = 2; # the tricky thing about dying here is that everybody # believes that calls to exists() or all_objects() are # safe. require CPAN::Exception::blocked_urllist; die CPAN::Exception::blocked_urllist->new; } } else { # ! $defaultsites my @host_seq = $level =~ /dleasy/ ? @reordered : 0..$last; # reordered has file and $Thesiteurl first @urllist = map { $ccurllist->[$_] } @host_seq; } $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; my $aslocal_tempfile = $aslocal . ".tmp" . $$; if (my $recommend = $self->_recommend_url_for($file,\@urllist)) { @urllist = grep { $_ ne $recommend } @urllist; unshift @urllist, $recommend; } $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; $ret = $self->hostdlxxx($level,$scheme,\@urllist,$file,$aslocal_tempfile,$stats); if ($ret) { CPAN->debug("ret[$ret]aslocal[$aslocal]") if $CPAN::DEBUG; if ($ret eq $aslocal_tempfile) { # if we got it exactly as we asked for, only then we # want to rename rename $aslocal_tempfile, $aslocal or $CPAN::Frontend->mydie("Error while trying to rename ". "'$ret' to '$aslocal': $!"); $ret = $aslocal; } elsif (-f $ret && $scheme eq 'file' ) { # it's a local file, so there's nothing left to do, we # let them read from where it is } $Themethod = $level; my $now = time; # utime $now, $now, $aslocal; # too bad, if we do that, we # might alter a local mirror $self->debug("level[$level]") if $CPAN::DEBUG; last LEVEL; } else { unlink $aslocal_tempfile; last if $CPAN::Signal; # need to cleanup } } if ($ret) { $stats->{filesize} = -s $ret; } $self->debug("before _add_to_statistics") if $CPAN::DEBUG; $self->_add_to_statistics($stats); $self->debug("after _add_to_statistics") if $CPAN::DEBUG; if ($ret) { unlink "$aslocal.bak$$"; return $ret; } unless ($CPAN::Signal) { my(@mess); local $" = " "; if (@{$CPAN::Config->{urllist}}) { push @mess, qq{Please check, if the URLs I found in your configuration file \(}. join(", ", @{$CPAN::Config->{urllist}}). qq{\) are valid.}; } else { push @mess, qq{Your urllist is empty!}; } push @mess, qq{The urllist can be edited.}, qq{E.g. with 'o conf urllist push ftp://myurl/'}; $CPAN::Frontend->mywarn(Text::Wrap::wrap("","","@mess"). "\n\n"); $CPAN::Frontend->mydie("Could not fetch $file\n"); } if ($maybe_restore) { rename "$aslocal.bak$$", $aslocal; $CPAN::Frontend->myprint("Trying to get away with old file:\n" . $self->ls($aslocal) . "\n"); return $aslocal; } return; } sub mymkpath { my($self, $aslocal_dir) = @_; mkpath($aslocal_dir); $CPAN::Frontend->mywarn(qq{Warning: You are not allowed to write into }. qq{directory "$aslocal_dir". I\'ll continue, but if you encounter problems, they may be due to insufficient permissions.\n}) unless -w $aslocal_dir; } sub hostdlxxx { my $self = shift; my $level = shift; my $scheme = shift; my $h = shift; $h = [ grep /^\Q$scheme\E:/, @$h ] if $scheme; my $method = "host$level"; $self->$method($h, @_); } sub _set_attempt { my($self,$stats,$method,$url) = @_; push @{$stats->{attempts}}, { method => $method, start => _mytime, url => $url, }; } # package CPAN::FTP; sub hostdleasy { #called from hostdlxxx my($self,$host_seq,$file,$aslocal,$stats) = @_; my($ro_url); HOSTEASY: for $ro_url (@$host_seq) { $self->_set_attempt($stats,"dleasy",$ro_url); my $url = "$ro_url$file"; $self->debug("localizing perlish[$url]") if $CPAN::DEBUG; if ($url =~ /^file:/) { my $l; if ($CPAN::META->has_inst('URI::URL')) { my $u = URI::URL->new($url); $l = $u->file; } else { # works only on Unix, is poorly constructed, but # hopefully better than nothing. # RFC 1738 says fileurl BNF is # fileurl = "file://" [ host | "localhost" ] "/" fpath # Thanks to "Mark D. Baushke" for # the code ($l = $url) =~ s|^file://[^/]*/|/|; # discard the host part $l =~ s|^file:||; # assume they # meant # file://localhost $l =~ s|^/||s if ! -f $l && $l =~ m|^/\w:|; # e.g. /P: } $self->debug("local file[$l]") if $CPAN::DEBUG; if ( -f $l && -r _) { $ThesiteURL = $ro_url; return $l; } # If request is for a compressed file and we can find the # uncompressed file also, return the path of the uncompressed file # otherwise, decompress it and return the resulting path if ($l =~ /(.+)\.gz$/) { my $ungz = $1; if ( -f $ungz && -r _) { $ThesiteURL = $ro_url; return $ungz; } elsif (-f $l && -r _) { eval { CPAN::Tarzip->new($l)->gunzip($aslocal) }; if ( -f $aslocal && -s _) { $ThesiteURL = $ro_url; return $aslocal; } elsif (! -s $aslocal) { unlink $aslocal; } elsif (-f $l) { $CPAN::Frontend->mywarn("Error decompressing '$l': $@\n") if $@; return; } } } # Otherwise, return the local file path if it exists elsif ( -f $l && -r _) { $ThesiteURL = $ro_url; return $l; } # If we can't find it, but there is a compressed version # of it, then decompress it elsif (-f "$l.gz") { $self->debug("found compressed $l.gz") if $CPAN::DEBUG; eval { CPAN::Tarzip->new("$l.gz")->gunzip($aslocal) }; if ( -f $aslocal) { $ThesiteURL = $ro_url; return $aslocal; } else { $CPAN::Frontend->mywarn("Error decompressing '$l': $@\n") if $@; return; } } $CPAN::Frontend->mywarn("Could not find '$l'\n"); } $self->debug("it was not a file URL") if $CPAN::DEBUG; if ($CPAN::META->has_usable('LWP')) { $CPAN::Frontend->myprint("Fetching with LWP:\n$url\n"); unless ($Ua) { CPAN::LWP::UserAgent->config; eval { $Ua = CPAN::LWP::UserAgent->new; }; if ($@) { $CPAN::Frontend->mywarn("CPAN::LWP::UserAgent->new dies with $@\n"); } } my $res = $Ua->mirror($url, $aslocal); if ($res->is_success) { $ThesiteURL = $ro_url; my $now = time; utime $now, $now, $aslocal; # download time is more # important than upload # time return $aslocal; } elsif ($url !~ /\.gz(?!\n)\Z/) { my $gzurl = "$url.gz"; $CPAN::Frontend->myprint("Fetching with LWP:\n$gzurl\n"); $res = $Ua->mirror($gzurl, "$aslocal.gz"); if ($res->is_success) { if (eval {CPAN::Tarzip->new("$aslocal.gz")->gunzip($aslocal)}) { $ThesiteURL = $ro_url; return $aslocal; } } } else { $CPAN::Frontend->myprint(sprintf( "LWP failed with code[%s] message[%s]\n", $res->code, $res->message, )); # Alan Burlison informed me that in firewall environments # Net::FTP can still succeed where LWP fails. So we do not # skip Net::FTP anymore when LWP is available. } } elsif ($url =~ /^http:/i && $CPAN::META->has_usable('HTTP::Tiny')) { require CPAN::HTTP::Client; my $chc = CPAN::HTTP::Client->new( proxy => $CPAN::Config->{http_proxy} || $ENV{http_proxy}, no_proxy => $CPAN::Config->{no_proxy} || $ENV{no_proxy}, ); for my $try ( $url, ( $url !~ /\.gz(?!\n)\Z/ ? "$url.gz" : () ) ) { $CPAN::Frontend->myprint("Fetching with HTTP::Tiny:\n$try\n"); my $res = eval { $chc->mirror($try, $aslocal) }; if ( $res && $res->{success} ) { $ThesiteURL = $ro_url; my $now = time; utime $now, $now, $aslocal; # download time is more # important than upload # time return $aslocal; } elsif ( $res && $res->{status} ne '599') { $CPAN::Frontend->myprint(sprintf( "HTTP::Tiny failed with code[%s] message[%s]\n", $res->{status}, $res->{reason}, ) ); } elsif ( $res && $res->{status} eq '599') { $CPAN::Frontend->myprint(sprintf( "HTTP::Tiny failed with an internal error: %s\n", $res->{content}, ) ); } else { my $err = $@ || 'Unknown error'; $CPAN::Frontend->myprint(sprintf( "Error downloading with HTTP::Tiny: %s\n", $err ) ); } } } return if $CPAN::Signal; if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { # that's the nice and easy way thanks to Graham $self->debug("recognized ftp") if $CPAN::DEBUG; my($host,$dir,$getfile) = ($1,$2,$3); if ($CPAN::META->has_usable('Net::FTP')) { $dir =~ s|/+|/|g; $CPAN::Frontend->myprint("Fetching with Net::FTP:\n$url\n"); $self->debug("getfile[$getfile]dir[$dir]host[$host]" . "aslocal[$aslocal]") if $CPAN::DEBUG; if (CPAN::FTP->ftp_get($host,$dir,$getfile,$aslocal)) { $ThesiteURL = $ro_url; return $aslocal; } if ($aslocal !~ /\.gz(?!\n)\Z/) { my $gz = "$aslocal.gz"; $CPAN::Frontend->myprint("Fetching with Net::FTP\n$url.gz\n"); if (CPAN::FTP->ftp_get($host, $dir, "$getfile.gz", $gz) && eval{CPAN::Tarzip->new($gz)->gunzip($aslocal)} ) { $ThesiteURL = $ro_url; return $aslocal; } } # next HOSTEASY; } else { CPAN->debug("Net::FTP does not count as usable atm") if $CPAN::DEBUG; } } if ( UNIVERSAL::can($ro_url,"text") and $ro_url->{FROM} eq "USER" ) { ##address #17973: default URLs should not try to override ##user-defined URLs just because LWP is not available my $ret = $self->hostdlhard([$ro_url],$file,$aslocal,$stats); return $ret if $ret; } return if $CPAN::Signal; } } # package CPAN::FTP; sub hostdlhard { my($self,$host_seq,$file,$aslocal,$stats) = @_; # Came back if Net::FTP couldn't establish connection (or # failed otherwise) Maybe they are behind a firewall, but they # gave us a socksified (or other) ftp program... my($ro_url); my($devnull) = $CPAN::Config->{devnull} || ""; # < /dev/null "; my($aslocal_dir) = dirname($aslocal); mkpath($aslocal_dir); my $some_dl_success = 0; my $any_attempt = 0; HOSTHARD: for $ro_url (@$host_seq) { $self->_set_attempt($stats,"dlhard",$ro_url); my $url = "$ro_url$file"; my($proto,$host,$dir,$getfile); # Courtesy Mark Conty mark_conty@cargill.com change from # if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { # to if ($url =~ m|^([^:]+)://(.*?)/(.*)/(.*)|) { # proto not yet used ($proto,$host,$dir,$getfile) = ($1,$2,$3,$4); } else { next HOSTHARD; # who said, we could ftp anything except ftp? } next HOSTHARD if $proto eq "file"; # file URLs would have had # success above. Likely a bogus URL # making at least one attempt against a host $any_attempt++; $self->debug("localizing funkyftpwise[$url]") if $CPAN::DEBUG; # Try the most capable first and leave ncftp* for last as it only # does FTP. my $proxy_vars = $self->_proxy_vars($ro_url); DLPRG: for my $f (qw(curl wget lynx ncftpget ncftp)) { my $funkyftp = CPAN::HandleConfig->safe_quote($CPAN::Config->{$f}); next DLPRG unless defined $funkyftp; next DLPRG if $funkyftp =~ /^\s*$/; my($src_switch) = ""; my($chdir) = ""; my($stdout_redir) = " > \"$aslocal\""; if ($f eq "lynx") { $src_switch = " -source"; } elsif ($f eq "ncftp") { next DLPRG unless $url =~ m{\Aftp://}; $src_switch = " -c"; } elsif ($f eq "wget") { $src_switch = " -O \"$aslocal\""; $stdout_redir = ""; } elsif ($f eq 'curl') { $src_switch = ' -L -f -s -S --netrc-optional'; if ($proxy_vars->{http_proxy}) { $src_switch .= qq{ -U "$proxy_vars->{proxy_user}:$proxy_vars->{proxy_pass}" -x "$proxy_vars->{http_proxy}"}; } } elsif ($f eq "ncftpget") { next DLPRG unless $url =~ m{\Aftp://}; $chdir = "cd $aslocal_dir && "; $stdout_redir = ""; } $CPAN::Frontend->myprint( qq[ Trying with $funkyftp$src_switch to get $url ]); my($system) = "$chdir$funkyftp$src_switch \"$url\" $devnull$stdout_redir"; $self->debug("system[$system]") if $CPAN::DEBUG; my($wstatus) = system($system); if ($f eq "lynx") { # lynx returns 0 when it fails somewhere if (-s $aslocal) { my $content = do { local *FH; open FH, $aslocal or die; local $/; }; if ($content =~ /^<.*([45]|Error [45])/si) { $CPAN::Frontend->mywarn(qq{ No success, the file that lynx has downloaded looks like an error message: $content }); $CPAN::Frontend->mysleep(1); next DLPRG; } $some_dl_success++; } else { $CPAN::Frontend->myprint(qq{ No success, the file that lynx has downloaded is an empty file. }); next DLPRG; } } if ($wstatus == 0) { if (-s $aslocal) { # Looks good $some_dl_success++; } $ThesiteURL = $ro_url; return $aslocal; } else { my $estatus = $wstatus >> 8; my $size = -f $aslocal ? ", left\n$aslocal with size ".-s _ : "\nWarning: expected file [$aslocal] doesn't exist"; $CPAN::Frontend->myprint(qq{ Function system("$system") returned status $estatus (wstat $wstatus)$size }); } return if $CPAN::Signal; } # download/transfer programs (DLPRG) } # host return unless $any_attempt; if ($some_dl_success) { $CPAN::Frontend->mywarn("Warning: doesn't seem we had substantial success downloading '$aslocal'. Don't know how to proceed.\n"); } else { $CPAN::Frontend->mywarn("Warning: no success downloading '$aslocal'. Giving up on it.\n"); } return; } #-> CPAN::FTP::_proxy_vars sub _proxy_vars { my($self,$url) = @_; my $ret = +{}; my $http_proxy = $CPAN::Config->{'http_proxy'} || $ENV{'http_proxy'}; if ($http_proxy) { my($host) = $url =~ m|://([^/:]+)|; my $want_proxy = 1; my $noproxy = $CPAN::Config->{'no_proxy'} || $ENV{'no_proxy'} || ""; my @noproxy = split /\s*,\s*/, $noproxy; if ($host) { DOMAIN: for my $domain (@noproxy) { if ($host =~ /\Q$domain\E$/) { # cf. LWP::UserAgent $want_proxy = 0; last DOMAIN; } } } else { $CPAN::Frontend->mywarn(" Could not determine host from http_proxy '$http_proxy'\n"); } if ($want_proxy) { my($user, $pass) = CPAN::HTTP::Credentials->get_proxy_credentials(); $ret = { proxy_user => $user, proxy_pass => $pass, http_proxy => $http_proxy }; } } return $ret; } # package CPAN::FTP; sub hostdlhardest { my($self,$host_seq,$file,$aslocal,$stats) = @_; return unless @$host_seq; my($ro_url); my($aslocal_dir) = dirname($aslocal); mkpath($aslocal_dir); my $ftpbin = $CPAN::Config->{ftp}; unless ($ftpbin && length $ftpbin && MM->maybe_command($ftpbin)) { $CPAN::Frontend->myprint("No external ftp command available\n\n"); return; } $CPAN::Frontend->mywarn(qq{ As a last resort we now switch to the external ftp command '$ftpbin' to get '$aslocal'. Doing so often leads to problems that are hard to diagnose. If you're the victim of such problems, please consider unsetting the ftp config variable with o conf ftp "" o conf commit }); $CPAN::Frontend->mysleep(2); HOSTHARDEST: for $ro_url (@$host_seq) { $self->_set_attempt($stats,"dlhardest",$ro_url); my $url = "$ro_url$file"; $self->debug("localizing ftpwise[$url]") if $CPAN::DEBUG; unless ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { next; } my($host,$dir,$getfile) = ($1,$2,$3); my $timestamp = 0; my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, $ctime,$blksize,$blocks) = stat($aslocal); $timestamp = $mtime ||= 0; my($netrc) = CPAN::FTP::netrc->new; my($netrcfile) = $netrc->netrc; my($verbose) = $CPAN::DEBUG{'FTP'} & $CPAN::DEBUG ? " -v" : ""; my $targetfile = File::Basename::basename($aslocal); my(@dialog); push( @dialog, "lcd $aslocal_dir", "cd /", map("cd $_", split /\//, $dir), # RFC 1738 "bin", "passive", "get $getfile $targetfile", "quit" ); if (! $netrcfile) { CPAN->debug("No ~/.netrc file found") if $CPAN::DEBUG; } elsif ($netrc->hasdefault || $netrc->contains($host)) { CPAN->debug(sprintf("hasdef[%d]cont($host)[%d]", $netrc->hasdefault, $netrc->contains($host))) if $CPAN::DEBUG; if ($netrc->protected) { my $dialog = join "", map { " $_\n" } @dialog; my $netrc_explain; if ($netrc->contains($host)) { $netrc_explain = "Relying that your .netrc entry for '$host' ". "manages the login"; } else { $netrc_explain = "Relying that your default .netrc entry ". "manages the login"; } $CPAN::Frontend->myprint(qq{ Trying with external ftp to get '$url' $netrc_explain Sending the dialog $dialog } ); $self->talk_ftp("$ftpbin$verbose $host", @dialog); ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($aslocal); $mtime ||= 0; if ($mtime > $timestamp) { $CPAN::Frontend->myprint("GOT $aslocal\n"); $ThesiteURL = $ro_url; return $aslocal; } else { $CPAN::Frontend->myprint("Hmm... Still failed!\n"); } return if $CPAN::Signal; } else { $CPAN::Frontend->mywarn(qq{Your $netrcfile is not }. qq{correctly protected.\n}); } } else { $CPAN::Frontend->mywarn("Your ~/.netrc neither contains $host nor does it have a default entry\n"); } # OK, they don't have a valid ~/.netrc. Use 'ftp -n' # then and login manually to host, using e-mail as # password. $CPAN::Frontend->myprint(qq{Issuing "$ftpbin$verbose -n"\n}); unshift( @dialog, "open $host", "user anonymous $Config::Config{'cf_email'}" ); my $dialog = join "", map { " $_\n" } @dialog; $CPAN::Frontend->myprint(qq{ Trying with external ftp to get $url Sending the dialog $dialog } ); $self->talk_ftp("$ftpbin$verbose -n", @dialog); ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($aslocal); $mtime ||= 0; if ($mtime > $timestamp) { $CPAN::Frontend->myprint("GOT $aslocal\n"); $ThesiteURL = $ro_url; return $aslocal; } else { $CPAN::Frontend->myprint("Bad luck... Still failed!\n"); } return if $CPAN::Signal; $CPAN::Frontend->mywarn("Can't access URL $url.\n\n"); $CPAN::Frontend->mysleep(2); } # host } # package CPAN::FTP; sub talk_ftp { my($self,$command,@dialog) = @_; my $fh = FileHandle->new; $fh->open("|$command") or die "Couldn't open ftp: $!"; foreach (@dialog) { $fh->print("$_\n") } $fh->close; # Wait for process to complete my $wstatus = $?; my $estatus = $wstatus >> 8; $CPAN::Frontend->myprint(qq{ Subprocess "|$command" returned status $estatus (wstat $wstatus) }) if $wstatus; } # find2perl needs modularization, too, all the following is stolen # from there # CPAN::FTP::ls sub ls { my($self,$name) = @_; my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$sizemm, $atime,$mtime,$ctime,$blksize,$blocks) = lstat($name); my($perms,%user,%group); my $pname = $name; if ($blocks) { $blocks = int(($blocks + 1) / 2); } else { $blocks = int(($sizemm + 1023) / 1024); } if (-f _) { $perms = '-'; } elsif (-d _) { $perms = 'd'; } elsif (-c _) { $perms = 'c'; $sizemm = &sizemm; } elsif (-b _) { $perms = 'b'; $sizemm = &sizemm; } elsif (-p _) { $perms = 'p'; } elsif (-S _) { $perms = 's'; } else { $perms = 'l'; $pname .= ' -> ' . readlink($_); } my(@rwx) = ('---','--x','-w-','-wx','r--','r-x','rw-','rwx'); my(@moname) = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my $tmpmode = $mode; my $tmp = $rwx[$tmpmode & 7]; $tmpmode >>= 3; $tmp = $rwx[$tmpmode & 7] . $tmp; $tmpmode >>= 3; $tmp = $rwx[$tmpmode & 7] . $tmp; substr($tmp,2,1) =~ tr/-x/Ss/ if -u _; substr($tmp,5,1) =~ tr/-x/Ss/ if -g _; substr($tmp,8,1) =~ tr/-x/Tt/ if -k _; $perms .= $tmp; my $user = $user{$uid} || $uid; # too lazy to implement lookup my $group = $group{$gid} || $gid; my($sec,$min,$hour,$mday,$mon,$year) = localtime($mtime); my($timeyear); my($moname) = $moname[$mon]; if (-M _ > 365.25 / 2) { $timeyear = $year + 1900; } else { $timeyear = sprintf("%02d:%02d", $hour, $min); } sprintf "%5lu %4ld %-10s %2d %-8s %-8s %8s %s %2d %5s %s\n", $ino, $blocks, $perms, $nlink, $user, $group, $sizemm, $moname, $mday, $timeyear, $pname; } 1; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Distroprefs.pm���������������������������������������������������������������������������������0000444�����������������00000027326�15111204742�0010134 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: use 5.006; use strict; package CPAN::Distroprefs; use vars qw($VERSION); $VERSION = '6.0001'; package CPAN::Distroprefs::Result; use File::Spec; sub new { bless $_[1] || {} => $_[0] } sub abs { File::Spec->catfile($_[0]->dir, $_[0]->file) } sub __cloner { my ($class, $name, $newclass) = @_; $newclass = 'CPAN::Distroprefs::Result::' . $newclass; no strict 'refs'; *{$class . '::' . $name} = sub { $newclass->new({ %{ $_[0] }, %{ $_[1] }, }); }; } BEGIN { __PACKAGE__->__cloner(as_warning => 'Warning') } BEGIN { __PACKAGE__->__cloner(as_fatal => 'Fatal') } BEGIN { __PACKAGE__->__cloner(as_success => 'Success') } sub __accessor { my ($class, $key) = @_; no strict 'refs'; *{$class . '::' . $key} = sub { $_[0]->{$key} }; } BEGIN { __PACKAGE__->__accessor($_) for qw(type file ext dir) } sub is_warning { 0 } sub is_fatal { 0 } sub is_success { 0 } package CPAN::Distroprefs::Result::Error; use vars qw(@ISA); BEGIN { @ISA = 'CPAN::Distroprefs::Result' } ## no critic BEGIN { __PACKAGE__->__accessor($_) for qw(msg) } sub as_string { my ($self) = @_; if ($self->msg) { return sprintf $self->fmt_reason, $self->file, $self->msg; } else { return sprintf $self->fmt_unknown, $self->file; } } package CPAN::Distroprefs::Result::Warning; use vars qw(@ISA); BEGIN { @ISA = 'CPAN::Distroprefs::Result::Error' } ## no critic sub is_warning { 1 } sub fmt_reason { "Error reading distroprefs file %s, skipping: %s" } sub fmt_unknown { "Unknown error reading distroprefs file %s, skipping." } package CPAN::Distroprefs::Result::Fatal; use vars qw(@ISA); BEGIN { @ISA = 'CPAN::Distroprefs::Result::Error' } ## no critic sub is_fatal { 1 } sub fmt_reason { "Error reading distroprefs file %s: %s" } sub fmt_unknown { "Unknown error reading distroprefs file %s." } package CPAN::Distroprefs::Result::Success; use vars qw(@ISA); BEGIN { @ISA = 'CPAN::Distroprefs::Result' } ## no critic BEGIN { __PACKAGE__->__accessor($_) for qw(prefs extension) } sub is_success { 1 } package CPAN::Distroprefs::Iterator; sub new { bless $_[1] => $_[0] } sub next { $_[0]->() } package CPAN::Distroprefs; use Carp (); use DirHandle; sub _load_method { my ($self, $loader, $result) = @_; return '_load_yaml' if $loader eq 'CPAN' or $loader =~ /^YAML(::|$)/; return '_load_' . $result->ext; } sub _load_yaml { my ($self, $loader, $result) = @_; my $data = eval { $loader eq 'CPAN' ? $loader->_yaml_loadfile($result->abs) : [ $loader->can('LoadFile')->($result->abs) ] }; if (my $err = $@) { die $result->as_warning({ msg => $err, }); } elsif (!$data) { die $result->as_warning; } else { return @$data; } } sub _load_dd { my ($self, $loader, $result) = @_; my @data; { package CPAN::Eval; # this caused a die in CPAN.pm, and I am leaving it 'fatal', though I'm # not sure why we wouldn't just skip the file as we do for all other # errors. -- hdp my $abs = $result->abs; open FH, "<$abs" or die $result->as_fatal(msg => "$!"); local $/; my $eval = <FH>; close FH; no strict; eval $eval; if (my $err = $@) { die $result->as_warning({ msg => $err }); } my $i = 1; while (${"VAR$i"}) { push @data, ${"VAR$i"}; $i++; } } return @data; } sub _load_st { my ($self, $loader, $result) = @_; # eval because Storable is never forward compatible my @data = eval { @{scalar $loader->can('retrieve')->($result->abs) } }; if (my $err = $@) { die $result->as_warning({ msg => $err }); } return @data; } sub _build_file_list { if (@_ > 3) { die "_build_file_list should be called with 3 arguments, was called with more. First argument is '$_[0]'."; } my ($dir, $dir1, $ext_re) = @_; my @list; my $dh; unless (opendir($dh, $dir)) { $CPAN::Frontend->mywarn("ignoring prefs directory '$dir': $!"); return @list; } while (my $fn = readdir $dh) { next if $fn eq '.' || $fn eq '..'; if (-d "$dir/$fn") { next if $fn =~ /^[._]/; # prune .svn, .git, .hg, _darcs and what the user wants to hide push @list, _build_file_list("$dir/$fn", "$dir1$fn/", $ext_re); } else { if ($fn =~ $ext_re) { push @list, "$dir1$fn"; } } } return @list; } sub find { my ($self, $dir, $ext_map) = @_; return CPAN::Distroprefs::Iterator->new(sub { return }) unless %$ext_map; my $possible_ext = join "|", map { quotemeta } keys %$ext_map; my $ext_re = qr/\.($possible_ext)$/; my @files = _build_file_list($dir, '', $ext_re); @files = sort @files if @files; # label the block so that we can use redo in the middle return CPAN::Distroprefs::Iterator->new(sub { LOOP: { my $fn = shift @files; return unless defined $fn; my ($ext) = $fn =~ $ext_re; my $loader = $ext_map->{$ext}; my $result = CPAN::Distroprefs::Result->new({ file => $fn, ext => $ext, dir => $dir }); # copied from CPAN.pm; is this ever actually possible? redo unless -f $result->abs; my $load_method = $self->_load_method($loader, $result); my @prefs = eval { $self->$load_method($loader, $result) }; if (my $err = $@) { if (ref($err) && eval { $err->isa('CPAN::Distroprefs::Result') }) { return $err; } # rethrow any exceptions that we did not generate die $err; } elsif (!@prefs) { # the loader should have handled this, but just in case: return $result->as_warning; } return $result->as_success({ prefs => [ map { CPAN::Distroprefs::Pref->new({ data => $_ }) } @prefs ], }); } }); } package CPAN::Distroprefs::Pref; use Carp (); sub new { bless $_[1] => $_[0] } sub data { shift->{data} } sub has_any_match { $_[0]->data->{match} ? 1 : 0 } sub has_match { my $match = $_[0]->data->{match} || return 0; exists $match->{$_[1]} || exists $match->{"not_$_[1]"} } sub has_valid_subkeys { grep { exists $_[0]->data->{match}{$_} } map { $_, "not_$_" } $_[0]->match_attributes } sub _pattern { my $re = shift; my $p = eval sprintf 'qr{%s}', $re; if ($@) { $@ =~ s/\n$//; die "Error in Distroprefs pattern qr{$re}\n$@"; } return $p; } sub _match_scalar { my ($match, $data) = @_; my $qr = _pattern($match); return $data =~ /$qr/; } sub _match_hash { my ($match, $data) = @_; for my $mkey (keys %$match) { (my $dkey = $mkey) =~ s/^not_//; my $val = defined $data->{$dkey} ? $data->{$dkey} : ''; if (_match_scalar($match->{$mkey}, $val)) { return 0 if $mkey =~ /^not_/; } else { return 0 if $mkey !~ /^not_/; } } return 1; } sub _match { my ($self, $key, $data, $matcher) = @_; my $m = $self->data->{match}; if (exists $m->{$key}) { return 0 unless $matcher->($m->{$key}, $data); } if (exists $m->{"not_$key"}) { return 0 if $matcher->($m->{"not_$key"}, $data); } return 1; } sub _scalar_match { my ($self, $key, $data) = @_; return $self->_match($key, $data, \&_match_scalar); } sub _hash_match { my ($self, $key, $data) = @_; return $self->_match($key, $data, \&_match_hash); } # do not take the order of C<keys %$match> because "module" is by far the # slowest sub match_attributes { qw(env distribution perl perlconfig module) } sub match_module { my ($self, $modules) = @_; return $self->_match("module", $modules, sub { my($match, $data) = @_; my $qr = _pattern($match); for my $module (@$data) { return 1 if $module =~ /$qr/; } return 0; }); } sub match_distribution { shift->_scalar_match(distribution => @_) } sub match_perl { shift->_scalar_match(perl => @_) } sub match_perlconfig { shift->_hash_match(perlconfig => @_) } sub match_env { shift->_hash_match(env => @_) } sub matches { my ($self, $arg) = @_; my $default_match = 0; for my $key (grep { $self->has_match($_) } $self->match_attributes) { unless (exists $arg->{$key}) { Carp::croak "Can't match pref: missing argument key $key"; } $default_match = 1; my $val = $arg->{$key}; # make it possible to avoid computing things until we have to if (ref($val) eq 'CODE') { $val = $val->() } my $meth = "match_$key"; return 0 unless $self->$meth($val); } return $default_match; } 1; __END__ =head1 NAME CPAN::Distroprefs -- read and match distroprefs =head1 SYNOPSIS use CPAN::Distroprefs; my %info = (... distribution/environment info ...); my $finder = CPAN::Distroprefs->find($prefs_dir, \%ext_map); while (my $result = $finder->next) { die $result->as_string if $result->is_fatal; warn($result->as_string), next if $result->is_warning; for my $pref (@{ $result->prefs }) { if ($pref->matches(\%info)) { return $pref; } } } =head1 DESCRIPTION This module encapsulates reading L<Distroprefs|CPAN> and matching them against CPAN distributions. =head1 INTERFACE my $finder = CPAN::Distroprefs->find($dir, \%ext_map); while (my $result = $finder->next) { ... } Build an iterator which finds distroprefs files in the tree below the given directory. Within the tree directories matching C<m/^[._]/> are pruned. C<%ext_map> is a hashref whose keys are file extensions and whose values are modules used to load matching files: { 'yml' => 'YAML::Syck', 'dd' => 'Data::Dumper', ... } Each time C<< $finder->next >> is called, the iterator returns one of two possible values: =over =item * a CPAN::Distroprefs::Result object =item * C<undef>, indicating that no prefs files remain to be found =back =head1 RESULTS L<C<find()>|/INTERFACE> returns CPAN::Distroprefs::Result objects to indicate success or failure when reading a prefs file. =head2 Common All results share some common attributes: =head3 type C<success>, C<warning>, or C<fatal> =head3 file the file from which these prefs were read, or to which this error refers (relative filename) =head3 ext the file's extension, which determines how to load it =head3 dir the directory the file was read from =head3 abs the absolute path to the file =head2 Errors Error results (warning and fatal) contain: =head3 msg the error message (usually either C<$!> or a YAML error) =head2 Successes Success results contain: =head3 prefs an arrayref of CPAN::Distroprefs::Pref objects =head1 PREFS CPAN::Distroprefs::Pref objects represent individual distroprefs documents. They are constructed automatically as part of C<success> results from C<find()>. =head3 data the pref information as a hashref, suitable for e.g. passing to Kwalify =head3 match_attributes returns a list of the valid match attributes (see the Distroprefs section in L<CPAN>) currently: C<env perl perlconfig distribution module> =head3 has_any_match true if this pref has a 'match' attribute at all =head3 has_valid_subkeys true if this pref has a 'match' attribute and at least one valid match attribute =head3 matches if ($pref->matches(\%arg)) { ... } true if this pref matches the passed-in hashref, which must have a value for each of the C<match_attributes> (above) =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Admin.pm���������������������������������������������������������������������������������������0000444�����������������00000017157�15111204742�0006661 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package CPAN::Admin; use base CPAN; use CPAN; # old base.pm did not load CPAN on previous line use strict; use vars qw(@EXPORT $VERSION); use constant PAUSE_IP => "pause.perl.org"; @EXPORT = qw(shell); $VERSION = "5.501"; push @CPAN::Complete::COMMANDS, qw(register modsearch); $CPAN::Shell::COLOR_REGISTERED = 1; sub shell { CPAN::shell($_[0]||"admin's cpan> ",$_[1]); } sub CPAN::Shell::register { my($self,$mod,@rest) = @_; unless ($mod) { print "register called without argument\n"; return; } if ($CPAN::META->has_inst("URI::Escape")) { require URI::Escape; } else { print "register requires URI::Escape installed, otherwise it cannot work\n"; return; } print "Got request for mod[$mod]\n"; if (@rest) { my $modline = join " ", $mod, @rest; print "Sending to PAUSE [$modline]\n"; my $emodline = URI::Escape::uri_escape($modline, '^\w '); $emodline =~ s/ /+/g; my $url = sprintf("https://%s/pause/authenquery?pause99_add_mod_modid=". "%s;SUBMIT_pause99_add_mod_hint=hint", PAUSE_IP, $emodline, ); print "url[$url]\n\n"; print ">>>>Trying to open a netscape window<<<<\n"; sleep 1; system("netscape","-remote","openURL($url)"); return; } my $m = CPAN::Shell->expand("Module",$mod); unless (ref $m) { print "Could not determine the object for $mod\n"; return; } my $id = $m->id; print "Found module id[$id] in database\n"; if (exists $m->{RO} && $m->{RO}{chapterid}) { print "$id is already registered\n"; return; } my(@namespace) = split /::/, $id; my $rootns = $namespace[0]; # Tk, XML and Apache need special treatment if ($rootns=~/^(Bundle)\b/) { print "Bundles are not yet ready for registering\n"; return; } # make a good suggestion for the chapter my(@simile) = CPAN::Shell->expand("Module","/^$rootns(:|\$)/"); print "Found within this namespace ", join(", ", map { $_->id } @simile), "\n"; my(%seench); for my $ch (map { exists $_->{RO} ? $_->{RO}{chapterid} : ""} @simile) { next unless $ch; $seench{$ch}=undef; } my(@seench) = sort grep {length($_)} keys %seench; my $reco_ch = ""; if (@seench>1) { print "Found rootnamespace[$rootns] in the chapters [", join(", ", @seench), "]\n"; $reco_ch = $seench[0]; print "Picking $reco_ch\n"; } elsif (@seench==1) { print "Found rootnamespace[$rootns] in the chapter[$seench[0]]\n"; $reco_ch = $seench[0]; } else { print "The new rootnamespace[$rootns] needs to be introduced. Oh well.\n"; } # Look closer at the dist my $d = CPAN::Shell->expand("Distribution", $m->cpan_file); printf "Module comes with dist[%s]\n", $d->id; for my $contm ($d->containsmods) { if ($CPAN::META->exists("CPAN::Module",$contm)) { my $contm_obj = CPAN::Shell->expand("Module",$contm) or next; my $is_reg = exists $contm_obj->{RO} && $contm_obj->{RO}{description}; printf(" in same dist: %s%s\n", $contm, $is_reg ? " already in modulelist" : "", ); } } # get it so that m is better and we can inspect for XS CPAN::Shell->get($id); CPAN::Shell->m($id); CPAN::Shell->d($d->id); my $has_xs = 0; { my($mani,@mani); local $/ = "\n"; open $mani, "$d->{build_dir}/MANIFEST" and @mani = <$mani>; my @xs = grep /\.xs\b/, @mani; if (@xs) { print "Found XS files: @xs"; $has_xs=1; } } my $emodid = URI::Escape::uri_escape($id, '\W'); my $ech = $reco_ch; $ech =~ s/ /+/g; my $description = $m->{MANPAGE} || ""; $description =~ s/[A-Z]<//; # POD markup (and maybe more) $description =~ s/^\s+//; # leading spaces $description =~ s/>//; # POD $description =~ s/^\Q$id\E//; # usually this line starts with the modid $description =~ s/^[ \-]+//; # leading spaces and dashes substr($description,44) = "" if length($description)>44; $description = ucfirst($description); my $edescription = URI::Escape::uri_escape($description, '^\w '); $edescription =~ s/ /+/g; my $url = sprintf("https://%s/pause/authenquery?pause99_add_mod_modid=". "%s;pause99_add_mod_chapterid=%s;pause99_add_mod_statd=%s;". "pause99_add_mod_stats=%s;pause99_add_mod_statl=%s;". "pause99_add_mod_stati=%s;pause99_add_mod_description=%s;". "pause99_add_mod_userid=%s;SUBMIT_pause99_add_mod_preview=preview", PAUSE_IP, $emodid, $ech, "R", "d", $has_xs ? "c" : "p", "O", $edescription, $m->{RO}{CPAN_USERID}, ); print "$url\n\n"; print ">>>>Trying to open a netscape window<<<<\n"; system("netscape","-remote","openURL($url)"); } sub CPAN::Shell::modsearch { my($self,@line) = @_; unless (@line) { print "modsearch called without argument\n"; return; } my $request = join " ", @line; print "Got request[$request]\n"; my $erequest = URI::Escape::uri_escape($request, '^\w '); $erequest =~ s/ /+/g; my $url = sprintf("http://www.xray.mpe.mpg.de/cgi-bin/w3glimpse/modules?query=%s". "&errors=0&case=on&maxfiles=100&maxlines=30", $erequest, ); print "$url\n\n"; print ">>>>Trying to open a netscape window<<<<\n"; system("netscape","-remote","openURL('$url')"); } 1; __END__ =head1 NAME CPAN::Admin - A CPAN Shell for CPAN admins =head1 SYNOPSIS perl -MCPAN::Admin -e shell =head1 STATUS Note: this module is currently not maintained. If you need it and fix it for your needs, please submit patches. =head1 DESCRIPTION CPAN::Admin is a subclass of CPAN that adds the commands C<register> and C<modsearch> to the CPAN shell. C<register> calls C<get> on the named module, assembles a couple of informations (description, language), and calls Netscape with the -remote argument so that a form is filled with all the assembled informations and the registration can be performed with a single click. If the command line has more than one argument, register does not run a C<get>, instead it interprets the rest of the line as DSLI status, description, and userid and sends them to netscape such that the form is again mostly filled and can be edited or confirmed with a single click. CPAN::Admin never performs the submission click for you, it is only intended to fill in the form on PAUSE and leave the confirmation to you. C<modsearch> simply passes the arguments to the search engine for the modules@perl.org mailing list at L<http://www.xray.mpe.mpg.de> where all registration requests are stored. It does so in the same way as register, namely with the C<netscape -remote> command. An experimental feature has also been added, namely to color already registered modules in listings. If you have L<Term::ANSIColor> installed, the u, r, and m commands will show already registered modules in green. =head1 PREREQUISITES L<URI::Escape>, a browser available in the path, the browser must understand the -remote switch (as far as I know, this is only available on UNIX); coloring of registered modules is only available if L<Term::ANSIColor> is installed. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Mirrors.pm�������������������������������������������������������������������������������������0000444�����������������00000043512�15111204742�0007260 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: =head1 NAME CPAN::Mirrors - Get CPAN mirror information and select a fast one =head1 SYNOPSIS use CPAN::Mirrors; my $mirrors = CPAN::Mirrors->new( $mirrored_by_file ); my $seen = {}; my $best_continent = $mirrors->find_best_continents( { seen => $seen } ); my @mirrors = $mirrors->get_mirrors_by_continents( $best_continent ); my $callback = sub { my( $m ) = @_; printf "%s = %s\n", $m->hostname, $m->rtt }; $mirrors->get_mirrors_timings( \@mirrors, $seen, $callback, %args ); @mirrors = sort { $a->rtt <=> $b->rtt } @mirrors; print "Best mirrors are ", map( { $_->rtt } @mirrors[0..3] ), "\n"; =head1 DESCRIPTION =over =cut package CPAN::Mirrors; use strict; use vars qw($VERSION $urllist $silent); $VERSION = "2.27"; use Carp; use FileHandle; use Fcntl ":flock"; use Net::Ping (); use CPAN::Version; =item new( LOCAL_FILE_NAME ) Create a new CPAN::Mirrors object from LOCAL_FILE_NAME. This file should look like that in http://www.cpan.org/MIRRORED.BY . =cut sub new { my ($class, $file) = @_; croak "CPAN::Mirrors->new requires a filename" unless defined $file; croak "The file [$file] was not found" unless -e $file; my $self = bless { mirrors => [], geography => {}, }, $class; $self->parse_mirrored_by( $file ); return $self; } sub parse_mirrored_by { my ($self, $file) = @_; my $handle = FileHandle->new; $handle->open($file) or croak "Couldn't open $file: $!"; flock $handle, LOCK_SH; $self->_parse($file,$handle); flock $handle, LOCK_UN; $handle->close; } =item continents() Return a list of continents based on those defined in F<MIRRORED.BY>. =cut sub continents { my ($self) = @_; return sort keys %{$self->{geography} || {}}; } =item countries( [CONTINENTS] ) Return a list of countries based on those defined in F<MIRRORED.BY>. It only returns countries for the continents you specify (as defined in C<continents>). If you don't specify any continents, it returns all of the countries listed in F<MIRRORED.BY>. =cut sub countries { my ($self, @continents) = @_; @continents = $self->continents unless @continents; my @countries; for my $c (@continents) { push @countries, sort keys %{ $self->{geography}{$c} || {} }; } return @countries; } =item mirrors( [COUNTRIES] ) Return a list of mirrors based on those defined in F<MIRRORED.BY>. It only returns mirrors for the countries you specify (as defined in C<countries>). If you don't specify any countries, it returns all of the mirrors listed in F<MIRRORED.BY>. =cut sub mirrors { my ($self, @countries) = @_; return @{$self->{mirrors}} unless @countries; my %wanted = map { $_ => 1 } @countries; my @found; for my $m (@{$self->{mirrors}}) { push @found, $m if exists $wanted{$m->country}; } return @found; } =item get_mirrors_by_countries( [COUNTRIES] ) A more sensible synonym for mirrors. =cut sub get_mirrors_by_countries { &mirrors } =item get_mirrors_by_continents( [CONTINENTS] ) Return a list of mirrors for all of continents you specify. If you don't specify any continents, it returns all of the mirrors. You can specify a single continent or an array reference of continents. =cut sub get_mirrors_by_continents { my ($self, $continents ) = @_; $continents = [ $continents ] unless ref $continents; eval { $self->mirrors( $self->get_countries_by_continents( @$continents ) ); }; } =item get_countries_by_continents( [CONTINENTS] ) A more sensible synonym for countries. =cut sub get_countries_by_continents { &countries } =item default_mirror Returns the default mirror, http://www.cpan.org/ . This mirror uses dynamic DNS to give a close mirror. =cut sub default_mirror { CPAN::Mirrored::By->new({ http => 'http://www.cpan.org/'}); } =item best_mirrors C<best_mirrors> checks for the best mirrors based on the list of continents you pass, or, without that, all continents, as defined by C<CPAN::Mirrored::By>. It pings each mirror, up to the value of C<how_many>. In list context, it returns up to C<how_many> mirrors. In scalar context, it returns the single best mirror. Arguments how_many - the number of mirrors to return. Default: 1 callback - a callback for find_best_continents verbose - true or false on all the whining and moaning. Default: false continents - an array ref of the continents to check external_ping - if true, use external ping via Net::Ping::External. Default: false If you don't specify the continents, C<best_mirrors> calls C<find_best_continents> to get the list of continents to check. If you don't have L<Net::Ping> v2.13 or later, needed for timings, this returns the default mirror. C<external_ping> should be set and then C<Net::Ping::External> needs to be installed, if the local network has a transparent proxy. =cut sub best_mirrors { my ($self, %args) = @_; my $how_many = $args{how_many} || 1; my $callback = $args{callback}; my $verbose = defined $args{verbose} ? $args{verbose} : 0; my $continents = $args{continents} || []; $continents = [$continents] unless ref $continents; $args{external_ping} = 0 unless defined $args{external_ping}; my $external_ping = $args{external_ping}; # Old Net::Ping did not do timings at all my $min_version = '2.13'; unless( CPAN::Version->vgt(Net::Ping->VERSION, $min_version) ) { carp sprintf "Net::Ping version is %s (< %s). Returning %s", Net::Ping->VERSION, $min_version, $self->default_mirror; return $self->default_mirror; } my $seen = {}; if ( ! @$continents ) { print "Searching for the best continent ...\n" if $verbose; my @best_continents = $self->find_best_continents( seen => $seen, verbose => $verbose, callback => $callback, external_ping => $external_ping, ); # Only add enough continents to find enough mirrors my $count = 0; for my $continent ( @best_continents ) { push @$continents, $continent; $count += $self->mirrors( $self->countries($continent) ); last if $count >= $how_many; } } return $self->default_mirror unless @$continents; print "Scanning " . join(", ", @$continents) . " ...\n" if $verbose; my $trial_mirrors = $self->get_n_random_mirrors_by_continents( 3 * $how_many, $continents->[0] ); my $timings = $self->get_mirrors_timings( $trial_mirrors, $seen, $callback, %args, ); return $self->default_mirror unless @$timings; $how_many = @$timings if $how_many > @$timings; return wantarray ? @{$timings}[0 .. $how_many-1] : $timings->[0]; } =item get_n_random_mirrors_by_continents( N, [CONTINENTS] ) Returns up to N random mirrors for the specified continents. Specify the continents as an array reference. =cut sub get_n_random_mirrors_by_continents { my( $self, $n, $continents ) = @_; $n ||= 3; $continents = [ $continents ] unless ref $continents; if ( $n <= 0 ) { return wantarray ? () : []; } my @long_list = $self->get_mirrors_by_continents( $continents ); if ( $n eq '*' or $n > @long_list ) { return wantarray ? @long_list : \@long_list; } @long_list = map {$_->[0]} sort {$a->[1] <=> $b->[1]} map {[$_, rand]} @long_list; splice @long_list, $n; # truncate \@long_list; } =item get_mirrors_timings( MIRROR_LIST, SEEN, CALLBACK, %ARGS ); Pings the listed mirrors and returns a list of mirrors sorted in ascending ping times. C<MIRROR_LIST> is an anonymous array of C<CPAN::Mirrored::By> objects to ping. The optional argument C<SEEN> is a hash reference used to track the mirrors you've already pinged. The optional argument C<CALLBACK> is a subroutine reference to call after each ping. It gets the C<CPAN::Mirrored::By> object after each ping. =cut sub get_mirrors_timings { my( $self, $mirror_list, $seen, $callback, %args ) = @_; $seen = {} unless defined $seen; croak "The mirror list argument must be an array reference" unless ref $mirror_list eq ref []; croak "The seen argument must be a hash reference" unless ref $seen eq ref {}; croak "callback must be a subroutine" if( defined $callback and ref $callback ne ref sub {} ); my $timings = []; for my $m ( @$mirror_list ) { $seen->{$m->hostname} = $m; next unless eval{ $m->http }; if( $self->_try_a_ping( $seen, $m, ) ) { my $ping = $m->ping(%args); next unless defined $ping; # printf "m %s ping %s\n", $m, $ping; push @$timings, $m; $callback->( $m ) if $callback; } else { push @$timings, $seen->{$m->hostname} if defined $seen->{$m->hostname}->rtt; } } my @best = sort { if( defined $a->rtt and defined $b->rtt ) { $a->rtt <=> $b->rtt } elsif( defined $a->rtt and ! defined $b->rtt ) { return -1; } elsif( ! defined $a->rtt and defined $b->rtt ) { return 1; } elsif( ! defined $a->rtt and ! defined $b->rtt ) { return 0; } } @$timings; return wantarray ? @best : \@best; } =item find_best_continents( HASH_REF ); C<find_best_continents> goes through each continent and pings C<N> random mirrors on that continent. It then orders the continents by ascending median ping time. In list context, it returns the ordered list of continent. In scalar context, it returns the same list as an anonymous array. Arguments: n - the number of hosts to ping for each continent. Default: 3 seen - a hashref of cached hostname ping times verbose - true or false for noisy or quiet. Default: false callback - a subroutine to run after each ping. ping_cache_limit - how long, in seconds, to reuse previous ping times. Default: 1 day The C<seen> hash has hostnames as keys and anonymous arrays as values. The anonymous array is a triplet of a C<CPAN::Mirrored::By> object, a ping time, and the epoch time for the measurement. The callback subroutine gets the C<CPAN::Mirrored::By> object, the ping time, and measurement time (the same things in the C<seen> hashref) as arguments. C<find_best_continents> doesn't care what the callback does and ignores the return value. With a low value for C<N>, a single mirror might skew the results enough to choose a worse continent. If you have that problem, try a larger value. =cut sub find_best_continents { my ($self, %args) = @_; $args{n} ||= 3; $args{verbose} = 0 unless defined $args{verbose}; $args{seen} = {} unless defined $args{seen}; croak "The seen argument must be a hash reference" unless ref $args{seen} eq ref {}; $args{ping_cache_limit} = 24 * 60 * 60 unless defined $args{ping_cache_limit}; croak "callback must be a subroutine" if( defined $args{callback} and ref $args{callback} ne ref sub {} ); my %medians; CONT: for my $c ( $self->continents ) { my @mirrors = $self->mirrors( $self->countries($c) ); printf "Testing %s (%d mirrors)\n", $c, scalar @mirrors if $args{verbose}; next CONT unless @mirrors; my $n = (@mirrors < $args{n}) ? @mirrors : $args{n}; my @tests; my $tries = 0; RANDOM: while ( @mirrors && @tests < $n && $tries++ < 15 ) { my $m = splice( @mirrors, int(rand(@mirrors)), 1 ); if( $self->_try_a_ping( $args{seen}, $m, $args{ping_cache_limit} )) { $self->get_mirrors_timings( [ $m ], $args{seen}, $args{callback}, %args, ); next RANDOM unless defined $args{seen}{$m->hostname}->rtt; } printf "(%s -> %0.2f ms)", $m->hostname, join ' ', 1000 * $args{seen}{$m->hostname}->rtt if $args{verbose}; push @tests, $args{seen}{$m->hostname}->rtt; } my $median = $self->_get_median_ping_time( \@tests, $args{verbose} ); $medians{$c} = $median if defined $median; } my @best_cont = sort { $medians{$a} <=> $medians{$b} } keys %medians; if ( $args{verbose} ) { print "Median result by continent:\n"; if ( @best_cont ) { for my $c ( @best_cont ) { printf( " %7.2f ms %s\n", $medians{$c}*1000, $c ); } } else { print " **** No results found ****\n" } } return wantarray ? @best_cont : $best_cont[0]; } # retry if sub _try_a_ping { my ($self, $seen, $mirror, $ping_cache_limit ) = @_; ( ! exists $seen->{$mirror->hostname} or ! defined $seen->{$mirror->hostname}->rtt or ! defined $ping_cache_limit or time - $seen->{$mirror->hostname}->ping_time > $ping_cache_limit ) } sub _get_median_ping_time { my ($self, $tests, $verbose ) = @_; my @sorted = sort { $a <=> $b } @$tests; my $median = do { if ( @sorted == 0 ) { undef } elsif ( @sorted == 1 ) { $sorted[0] } elsif ( @sorted % 2 ) { $sorted[ int(@sorted / 2) ] } else { my $mid_high = int(@sorted/2); ($sorted[$mid_high-1] + $sorted[$mid_high])/2; } }; if ($verbose){ if ($median) { printf " => median time: %.2f ms\n", $median * 1000 } else { printf " => **** no median time ****\n"; } } return $median; } # Adapted from Parse::CPAN::MirroredBy by Adam Kennedy sub _parse { my ($self, $file, $handle) = @_; my $output = $self->{mirrors}; my $geo = $self->{geography}; local $/ = "\012"; my $line = 0; my $mirror = undef; while ( 1 ) { # Next line my $string = <$handle>; last if ! defined $string; $line = $line + 1; # Remove the useless lines chomp( $string ); next if $string =~ /^\s*$/; next if $string =~ /^\s*#/; # Hostname or property? if ( $string =~ /^\s/ ) { # Property unless ( $string =~ /^\s+(\w+)\s+=\s+\"(.*)\"$/ ) { croak("Invalid property on line $line"); } my ($prop, $value) = ($1,$2); $mirror ||= {}; if ( $prop eq 'dst_location' ) { my (@location,$continent,$country); @location = (split /\s*,\s*/, $value) and ($continent, $country) = @location[-1,-2]; $continent =~ s/\s\(.*//; $continent =~ s/\W+$//; # if Jarkko doesn't know latitude/longitude $geo->{$continent}{$country} = 1 if $continent && $country; $mirror->{continent} = $continent || "unknown"; $mirror->{country} = $country || "unknown"; } elsif ( $prop eq 'dst_http' ) { $mirror->{http} = $value; } elsif ( $prop eq 'dst_ftp' ) { $mirror->{ftp} = $value; } elsif ( $prop eq 'dst_rsync' ) { $mirror->{rsync} = $value; } else { $prop =~ s/^dst_//; $mirror->{$prop} = $value; } } else { # Hostname unless ( $string =~ /^([\w\.-]+)\:\s*$/ ) { croak("Invalid host name on line $line"); } my $current = $mirror; $mirror = { hostname => "$1" }; if ( $current ) { push @$output, CPAN::Mirrored::By->new($current); } } } if ( $mirror ) { push @$output, CPAN::Mirrored::By->new($mirror); } return; } #--------------------------------------------------------------------------# package CPAN::Mirrored::By; use strict; use Net::Ping (); sub new { my($self,$arg) = @_; $arg ||= {}; bless $arg, $self; } sub hostname { shift->{hostname} } sub continent { shift->{continent} } sub country { shift->{country} } sub http { shift->{http} || '' } sub ftp { shift->{ftp} || '' } sub rsync { shift->{rsync} || '' } sub rtt { shift->{rtt} } sub ping_time { shift->{ping_time} } sub url { my $self = shift; return $self->{http} || $self->{ftp}; } sub ping { my($self, %args) = @_; my $external_ping = $args{external_ping}; if ($external_ping) { eval { require Net::Ping::External } or die "Net::Ping::External required to use external ping command"; } my $ping = Net::Ping->new( $external_ping ? 'external' : $^O eq 'VMS' ? 'icmp' : 'tcp', 1 ); my ($proto) = $self->url =~ m{^([^:]+)}; my $port = $proto eq 'http' ? 80 : 21; return unless $port; if ( $ping->can('port_number') ) { $ping->port_number($port); } else { $ping->{'port_num'} = $port; } $ping->hires(1) if $ping->can('hires'); my ($alive,$rtt) = eval { $ping->ping($self->hostname); }; my $verbose = $args{verbose}; if ($verbose && !$alive) { printf "(host %s not alive)", $self->hostname; } $self->{rtt} = $alive ? $rtt : undef; $self->{ping_time} = time; $self->rtt; } 1; =back =head1 AUTHOR Andreas Koenig C<< <andk@cpan.org> >>, David Golden C<< <dagolden@cpan.org> >>, brian d foy C<< <bdfoy@cpan.org> >> =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L<http://www.perl.com/perl/misc/Artistic.html> =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Distrostatus.pm��������������������������������������������������������������������������������0000444�����������������00000001714�15111204742�0010331 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Distrostatus; use overload '""' => "as_string", fallback => 1; use vars qw($something_has_failed_at); use vars qw( $VERSION ); $VERSION = "5.5"; sub new { my($class,$arg) = @_; my $failed = substr($arg,0,2) eq "NO"; if ($failed) { $something_has_failed_at = $CPAN::CurrentCommandId; } bless { TEXT => $arg, FAILED => $failed, COMMANDID => $CPAN::CurrentCommandId, TIME => time, }, $class; } sub something_has_just_failed () { defined $something_has_failed_at && $something_has_failed_at == $CPAN::CurrentCommandId; } sub commandid { shift->{COMMANDID} } sub failed { shift->{FAILED} } sub text { my($self,$set) = @_; if (defined $set) { $self->{TEXT} = $set; } $self->{TEXT}; } sub as_string { my($self) = @_; $self->text; } 1; ����������������������������������������������������CPAN/Exception/blocked_urllist.pm�������������������������������������������������������������������0000444�����������������00000001630�15111204742�0012735 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Exception::blocked_urllist; use strict; use overload '""' => "as_string"; use vars qw( $VERSION ); $VERSION = "1.001"; sub new { my($class) = @_; bless {}, $class; } sub as_string { my($self) = shift; if ($CPAN::Config->{connect_to_internet_ok}) { return qq{ You have not configured a urllist for CPAN mirrors. Configure it with o conf init urllist }; } else { return qq{ You have not configured a urllist and do not allow connections to the internet to get a list of mirrors. If you wish to get a list of CPAN mirrors to pick from, use this command o conf init connect_to_internet_ok urllist If you do not wish to get a list of mirrors and would prefer to set your urllist manually, use just this command instead o conf init urllist }; } } 1; ��������������������������������������������������������������������������������������������������������CPAN/Exception/RecursiveDependency.pm���������������������������������������������������������������0000444�����������������00000007456�15111204742�0013536 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Exception::RecursiveDependency; use strict; use overload '""' => "as_string"; use vars qw( $VERSION ); $VERSION = "5.5001"; { package CPAN::Exception::RecursiveDependency::na; use overload '""' => "as_string"; sub new { bless {}, shift }; sub as_string { "N/A" }; } my $NA = CPAN::Exception::RecursiveDependency::na->new; # a module sees its distribution (no version) # a distribution sees its prereqs (which are module names) (usually with versions) # a bundle sees its module names and/or its distributions (no version) sub new { my($class) = shift; my($deps_arg) = shift; my (@deps,%seen,$loop_starts_with); DCHAIN: for my $dep (@$deps_arg) { push @deps, {name => $dep, display_as => $dep}; if ($seen{$dep}++) { $loop_starts_with = $dep; last DCHAIN; } } my $in_loop = 0; my %mark; DWALK: for my $i (0..$#deps) { my $x = $deps[$i]{name}; $in_loop ||= $loop_starts_with && $x eq $loop_starts_with; my $xo = CPAN::Shell->expandany($x) or next; if ($xo->isa("CPAN::Module")) { my $have = $xo->inst_version || $NA; my($want,$d,$want_type); if ($i>0 and $d = $deps[$i-1]{name}) { my $do = CPAN::Shell->expandany($d); $want = $do->{prereq_pm}{requires}{$x}; if (defined $want) { $want_type = "requires: "; } else { $want = $do->{prereq_pm}{build_requires}{$x}; if (defined $want) { $want_type = "build_requires: "; } else { $want_type = "unknown status"; $want = "???"; } } } else { $want = $xo->cpan_version; $want_type = "want: "; } $deps[$i]{have} = $have; $deps[$i]{want_type} = $want_type; $deps[$i]{want} = $want; $deps[$i]{display_as} = "$x (have: $have; $want_type$want)"; if ((! ref $have || !$have->isa('CPAN::Exception::RecursiveDependency::na')) && CPAN::Version->vge($have, $want)) { # https://rt.cpan.org/Ticket/Display.html?id=115340 undef $loop_starts_with; last DWALK; } } elsif ($xo->isa("CPAN::Distribution")) { my $pretty = $deps[$i]{display_as} = $xo->pretty_id; my $mark_as; if ($in_loop) { $mark_as = CPAN::Distrostatus->new("NO cannot resolve circular dependency"); } else { $mark_as = CPAN::Distrostatus->new("NO one dependency ($loop_starts_with) is a circular dependency"); } $mark{$pretty} = { xo => $xo, mark_as => $mark_as }; } } if ($loop_starts_with) { while (my($k,$v) = each %mark) { my $xo = $v->{xo}; $xo->{make} = $v->{mark_as}; $xo->store_persistent_state; # otherwise I will not reach # all involved parties for # the next session } } bless { deps => \@deps, loop_starts_with => $loop_starts_with }, $class; } sub is_resolvable { ! defined shift->{loop_starts_with}; } sub as_string { my($self) = shift; my $deps = $self->{deps}; my $loop_starts_with = $self->{loop_starts_with}; unless ($loop_starts_with) { return "--not a recursive/circular dependency--"; } my $ret = "\nRecursive dependency detected:\n "; $ret .= join("\n => ", map {$_->{display_as}} @$deps); $ret .= ".\nCannot resolve.\n"; $ret; } 1; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Exception/yaml_process_error.pm����������������������������������������������������������������0000444�����������������00000003247�15111204742�0013473 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Exception::yaml_process_error; use strict; use overload '""' => "as_string"; use vars qw( $VERSION ); $VERSION = "5.5"; sub new { my($class,$module,$file,$during,$error) = @_; # my $at = Carp::longmess(""); # XXX find something more beautiful bless { module => $module, file => $file, during => $during, error => $error, # at => $at, }, $class; } sub as_string { my($self) = shift; if ($self->{during}) { if ($self->{file}) { if ($self->{module}) { if ($self->{error}) { return "Alert: While trying to '$self->{during}' YAML file\n". " '$self->{file}'\n". "with '$self->{module}' the following error was encountered:\n". " $self->{error}\n"; } else { return "Alert: While trying to '$self->{during}' YAML file\n". " '$self->{file}'\n". "with '$self->{module}' some unknown error was encountered\n"; } } else { return "Alert: While trying to '$self->{during}' YAML file\n". " '$self->{file}'\n". "some unknown error was encountered\n"; } } else { return "Alert: While trying to '$self->{during}' some YAML file\n". "some unknown error was encountered\n"; } } else { return "Alert: unknown error encountered\n"; } } 1; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Exception/yaml_not_installed.pm����������������������������������������������������������������0000444�����������������00000000752�15111204742�0013441 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Exception::yaml_not_installed; use strict; use overload '""' => "as_string"; use vars qw( $VERSION ); $VERSION = "5.5"; sub new { my($class,$module,$file,$during) = @_; bless { module => $module, file => $file, during => $during }, $class; } sub as_string { my($self) = shift; "'$self->{module}' not installed, cannot $self->{during} '$self->{file}'\n"; } 1; ����������������������CPAN/Complete.pm������������������������������������������������������������������������������������0000444�����������������00000013607�15111204742�0007375 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::Complete; use strict; @CPAN::Complete::ISA = qw(CPAN::Debug); # Q: where is the "How do I add a new command" HOWTO? # A: git log -p -1 355c44e9caaec857e4b12f51afb96498833c3e36 where andk added the report command @CPAN::Complete::COMMANDS = sort qw( ? ! a b d h i m o q r u autobundle bye clean cvs_import dump exit failed force fforce hosts install install_tested is_tested look ls make mkmyconfig notest perldoc quit readme recent recompile reload report reports scripts smoke test upgrade ); use vars qw( $VERSION ); $VERSION = "5.5001"; package CPAN::Complete; use strict; sub gnu_cpl { my($text, $line, $start, $end) = @_; my(@perlret) = cpl($text, $line, $start); # find longest common match. Can anybody show me how to peruse # T::R::Gnu to have this done automatically? Seems expensive. return () unless @perlret; my($newtext) = $text; for (my $i = length($text)+1;;$i++) { last unless length($perlret[0]) && length($perlret[0]) >= $i; my $try = substr($perlret[0],0,$i); my @tries = grep {substr($_,0,$i) eq $try} @perlret; # warn "try[$try]tries[@tries]"; if (@tries == @perlret) { $newtext = $try; } else { last; } } ($newtext,@perlret); } #-> sub CPAN::Complete::cpl ; sub cpl { my($word,$line,$pos) = @_; $word ||= ""; $line ||= ""; $pos ||= 0; CPAN->debug("word [$word] line[$line] pos[$pos]") if $CPAN::DEBUG; $line =~ s/^\s*//; if ($line =~ s/^((?:notest|f?force)\s*)//) { $pos -= length($1); } my @return; if ($pos == 0 || $line =~ /^(?:h(?:elp)?|\?)\s/) { @return = grep /^\Q$word\E/, @CPAN::Complete::COMMANDS; } elsif ( $line !~ /^[\!abcdghimorutl]/ ) { @return = (); } elsif ($line =~ /^a\s/) { @return = cplx('CPAN::Author',uc($word)); } elsif ($line =~ /^ls\s/) { my($author,$rest) = $word =~ m|([^/]+)/?(.*)|; @return = $rest ? () : map {"$_/"} cplx('CPAN::Author',uc($author||"")); if (0 && 1==@return) { # XXX too slow and even wrong when there is a * already @return = grep /^\Q$word\E/, map {"$author/$_->[2]"} CPAN::Shell->expand("Author",$author)->ls("$rest*","2"); } } elsif ($line =~ /^b\s/) { CPAN::Shell->local_bundles; @return = cplx('CPAN::Bundle',$word); } elsif ($line =~ /^d\s/) { @return = cplx('CPAN::Distribution',$word); } elsif ($line =~ m/^( [mru]|make|clean|dump|get|test|install|readme|look|cvs_import|perldoc|recent )\s/x ) { if ($word =~ /^Bundle::/) { CPAN::Shell->local_bundles; } @return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word)); } elsif ($line =~ /^i\s/) { @return = cpl_any($word); } elsif ($line =~ /^reload\s/) { @return = cpl_reload($word,$line,$pos); } elsif ($line =~ /^o\s/) { @return = cpl_option($word,$line,$pos); } elsif ($line =~ m/^\S+\s/ ) { # fallback for future commands and what we have forgotten above @return = (cplx('CPAN::Module',$word),cplx('CPAN::Bundle',$word)); } else { @return = (); } return @return; } #-> sub CPAN::Complete::cplx ; sub cplx { my($class, $word) = @_; if (CPAN::_sqlite_running()) { $CPAN::SQLite->search($class, "^\Q$word\E"); } my $method = "id"; $method = "pretty_id" if $class eq "CPAN::Distribution"; sort grep /^\Q$word\E/, map { $_->$method() } $CPAN::META->all_objects($class); } #-> sub CPAN::Complete::cpl_any ; sub cpl_any { my($word) = shift; return ( cplx('CPAN::Author',$word), cplx('CPAN::Bundle',$word), cplx('CPAN::Distribution',$word), cplx('CPAN::Module',$word), ); } #-> sub CPAN::Complete::cpl_reload ; sub cpl_reload { my($word,$line,$pos) = @_; $word ||= ""; my(@words) = split " ", $line; CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; my(@ok) = qw(cpan index); return @ok if @words == 1; return grep /^\Q$word\E/, @ok if @words == 2 && $word; } #-> sub CPAN::Complete::cpl_option ; sub cpl_option { my($word,$line,$pos) = @_; $word ||= ""; my(@words) = split " ", $line; CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; my(@ok) = qw(conf debug); return @ok if @words == 1; return grep /^\Q$word\E/, @ok if @words == 2 && length($word); if (0) { } elsif ($words[1] eq 'index') { return (); } elsif ($words[1] eq 'conf') { return CPAN::HandleConfig::cpl(@_); } elsif ($words[1] eq 'debug') { return sort grep /^\Q$word\E/i, sort keys %CPAN::DEBUG, 'all'; } } 1; �������������������������������������������������������������������������������������������������������������������������CPAN/CacheMgr.pm������������������������������������������������������������������������������������0000444�����������������00000016760�15111204742�0007301 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: package CPAN::CacheMgr; use strict; use CPAN::InfoObj; @CPAN::CacheMgr::ISA = qw(CPAN::InfoObj CPAN); use Cwd qw(chdir); use File::Find; use vars qw( $VERSION ); $VERSION = "5.5002"; package CPAN::CacheMgr; use strict; #-> sub CPAN::CacheMgr::as_string ; sub as_string { eval { require Data::Dumper }; if ($@) { return shift->SUPER::as_string; } else { return Data::Dumper::Dumper(shift); } } #-> sub CPAN::CacheMgr::cachesize ; sub cachesize { shift->{DU}; } #-> sub CPAN::CacheMgr::tidyup ; sub tidyup { my($self) = @_; return unless $CPAN::META->{LOCK}; return unless -d $self->{ID}; my @toremove = grep { $self->{SIZE}{$_}==0 } @{$self->{FIFO}}; for my $current (0..$#toremove) { my $toremove = $toremove[$current]; $CPAN::Frontend->myprint(sprintf( "DEL(%d/%d): %s \n", $current+1, scalar @toremove, $toremove, ) ); return if $CPAN::Signal; $self->_clean_cache($toremove); return if $CPAN::Signal; } $self->{FIFO} = []; } #-> sub CPAN::CacheMgr::dir ; sub dir { shift->{ID}; } #-> sub CPAN::CacheMgr::entries ; sub entries { my($self,$dir) = @_; return unless defined $dir; $self->debug("reading dir[$dir]") if $CPAN::DEBUG; $dir ||= $self->{ID}; my($cwd) = CPAN::anycwd(); chdir $dir or Carp::croak("Can't chdir to $dir: $!"); my $dh = DirHandle->new(File::Spec->curdir) or Carp::croak("Couldn't opendir $dir: $!"); my(@entries); for ($dh->read) { next if $_ eq "." || $_ eq ".."; if (-f $_) { push @entries, File::Spec->catfile($dir,$_); } elsif (-d _) { push @entries, File::Spec->catdir($dir,$_); } else { $CPAN::Frontend->mywarn("Warning: weird direntry in $dir: $_\n"); } } chdir $cwd or Carp::croak("Can't chdir to $cwd: $!"); sort { -M $a <=> -M $b} @entries; } #-> sub CPAN::CacheMgr::disk_usage ; sub disk_usage { my($self,$dir,$fast) = @_; return if exists $self->{SIZE}{$dir}; return if $CPAN::Signal; my($Du) = 0; if (-e $dir) { if (-d $dir) { unless (-x $dir) { unless (chmod 0755, $dir) { $CPAN::Frontend->mywarn("I have neither the -x permission nor the ". "permission to change the permission; cannot ". "estimate disk usage of '$dir'\n"); $CPAN::Frontend->mysleep(5); return; } } } elsif (-f $dir) { # nothing to say, no matter what the permissions } } else { $CPAN::Frontend->mywarn("File or directory '$dir' has gone, ignoring\n"); return; } if ($fast) { $Du = 0; # placeholder } else { find( sub { $File::Find::prune++ if $CPAN::Signal; return if -l $_; if ($^O eq 'MacOS') { require Mac::Files; my $cat = Mac::Files::FSpGetCatInfo($_); $Du += $cat->ioFlLgLen() + $cat->ioFlRLgLen() if $cat; } else { if (-d _) { unless (-x _) { unless (chmod 0755, $_) { $CPAN::Frontend->mywarn("I have neither the -x permission nor ". "the permission to change the permission; ". "can only partially estimate disk usage ". "of '$_'\n"); $CPAN::Frontend->mysleep(5); return; } } } else { $Du += (-s _); } } }, $dir ); } return if $CPAN::Signal; $self->{SIZE}{$dir} = $Du/1024/1024; unshift @{$self->{FIFO}}, $dir; $self->debug("measured $dir is $Du") if $CPAN::DEBUG; $self->{DU} += $Du/1024/1024; $self->{DU}; } #-> sub CPAN::CacheMgr::_clean_cache ; sub _clean_cache { my($self,$dir) = @_; return unless -e $dir; unless (File::Spec->canonpath(File::Basename::dirname($dir)) eq File::Spec->canonpath($CPAN::Config->{build_dir})) { $CPAN::Frontend->mywarn("Directory '$dir' not below $CPAN::Config->{build_dir}, ". "will not remove\n"); $CPAN::Frontend->mysleep(5); return; } $self->debug("have to rmtree $dir, will free $self->{SIZE}{$dir}") if $CPAN::DEBUG; File::Path::rmtree($dir); my $id_deleted = 0; if ($dir !~ /\.yml$/ && -f "$dir.yml") { my $yaml_module = CPAN::_yaml_module(); if ($CPAN::META->has_inst($yaml_module)) { my($peek_yaml) = eval { CPAN->_yaml_loadfile("$dir.yml"); }; if ($@) { $CPAN::Frontend->mywarn("(parse error on '$dir.yml' removing anyway)"); unlink "$dir.yml" or $CPAN::Frontend->mywarn("(Could not unlink '$dir.yml': $!)"); return; } elsif (my $id = $peek_yaml->[0]{distribution}{ID}) { $CPAN::META->delete("CPAN::Distribution", $id); # XXX we should restore the state NOW, otherwise this # distro does not exist until we read an index. BUG ALERT(?) # $CPAN::Frontend->mywarn (" +++\n"); $id_deleted++; } } unlink "$dir.yml"; # may fail unless ($id_deleted) { CPAN->debug("no distro found associated with '$dir'"); } } $self->{DU} -= $self->{SIZE}{$dir}; delete $self->{SIZE}{$dir}; } #-> sub CPAN::CacheMgr::new ; sub new { my($class,$phase) = @_; $phase ||= "atstart"; my $time = time; my($debug,$t2); $debug = ""; my $self = { ID => $CPAN::Config->{build_dir}, MAX => $CPAN::Config->{'build_cache'}, SCAN => $CPAN::Config->{'scan_cache'} || 'atstart', DU => 0 }; $CPAN::Frontend->mydie("Unknown scan_cache argument: $self->{SCAN}") unless $self->{SCAN} =~ /never|atstart|atexit/; File::Path::mkpath($self->{ID}); my $dh = DirHandle->new($self->{ID}); bless $self, $class; $self->scan_cache($phase); $t2 = time; $debug .= "timing of CacheMgr->new: ".($t2 - $time); $time = $t2; CPAN->debug($debug) if $CPAN::DEBUG; $self; } #-> sub CPAN::CacheMgr::scan_cache ; sub scan_cache { my ($self, $phase) = @_; $phase = '' unless defined $phase; return unless $phase eq $self->{SCAN}; return unless $CPAN::META->{LOCK}; $CPAN::Frontend->myprint( sprintf("Scanning cache %s for sizes\n", $self->{ID})); my $e; my @entries = $self->entries($self->{ID}); my $i = 0; my $painted = 0; for $e (@entries) { my $symbol = "."; if ($self->{DU} > $self->{MAX}) { $symbol = "-"; $self->disk_usage($e,1); } else { $self->disk_usage($e); } $i++; while (($painted/76) < ($i/@entries)) { $CPAN::Frontend->myprint($symbol); $painted++; } return if $CPAN::Signal; } $CPAN::Frontend->myprint("DONE\n"); $self->tidyup; } 1; ����������������CPAN/Plugin.pm��������������������������������������������������������������������������������������0000444�����������������00000006223�15111204742�0007057 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package CPAN::Plugin; use strict; use warnings; our $VERSION = '0.97'; require CPAN; ###################################################################### sub new { # ; my ($class, %params) = @_; my $self = +{ (ref $class ? (%$class) : ()), %params, }; $self = bless $self, ref $class ? ref $class : $class; unless (ref $class) { local $_; no warnings 'once'; $CPAN::META->use_inst ($_) for $self->plugin_requires; } $self; } ###################################################################### sub plugin_requires { # ; } ###################################################################### sub distribution_object { # ; my ($self) = @_; $self->{distribution_object}; } ###################################################################### sub distribution { # ; my ($self) = @_; my $distribution = $self->distribution_object->id; CPAN::Shell->expand("Distribution",$distribution) or $self->frontend->mydie("Unknowns distribution '$distribution'\n"); } ###################################################################### sub distribution_info { # ; my ($self) = @_; CPAN::DistnameInfo->new ($self->distribution->id); } ###################################################################### sub build_dir { # ; my ($self) = @_; my $build_dir = $self->distribution->{build_dir} or $self->frontend->mydie("Distribution has not been built yet, cannot proceed"); } ###################################################################### sub is_xs { # my ($self) = @_; my @xs = glob File::Spec->catfile ($self->build_dir, '*.xs'); # quick try unless (@xs) { require ExtUtils::Manifest; my $manifest_file = File::Spec->catfile ($self->build_dir, "MANIFEST"); my $manifest = ExtUtils::Manifest::maniread($manifest_file); @xs = grep /\.xs$/, keys %$manifest; } scalar @xs; } ###################################################################### package CPAN::Plugin; 1; __END__ =pod =head1 NAME CPAN::Plugin - Base class for CPAN shell extensions =head1 SYNOPSIS package CPAN::Plugin::Flurb; use parent 'CPAN::Plugin'; sub post_test { my ($self, $distribution_object) = @_; $self = $self->new (distribution_object => $distribution_object); ...; } =head1 DESCRIPTION =head2 Alpha Status The plugin system in the CPAN shell was introduced in version 2.07 and is still considered experimental. =head2 How Plugins work? See L<CPAN/"Plugin support">. =head1 METHODS =head2 plugin_requires returns list of packages given plugin requires for functionality. This list is evaluated using C<< CPAN->use_inst >> method. =head2 distribution_object Get current distribution object. =head2 distribution =head2 distribution_info =head2 build_dir Simple delegatees for misc parameters derived from distribution =head2 is_xs Predicate to detect whether package contains XS. =head1 AUTHOR Branislav Zahradnik <barney@cpan.org> =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Shell.pm���������������������������������������������������������������������������������������0000444�����������������00000217724�15111204742�0006702 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package CPAN::Shell; use strict; # -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: use vars qw( $ADVANCED_QUERY $AUTOLOAD $COLOR_REGISTERED $Help $autoload_recursion $reload @ISA @relo $VERSION ); @relo = ( "CPAN.pm", "CPAN/Author.pm", "CPAN/CacheMgr.pm", "CPAN/Complete.pm", "CPAN/Debug.pm", "CPAN/DeferredCode.pm", "CPAN/Distribution.pm", "CPAN/Distroprefs.pm", "CPAN/Distrostatus.pm", "CPAN/Exception/RecursiveDependency.pm", "CPAN/Exception/yaml_not_installed.pm", "CPAN/FirstTime.pm", "CPAN/FTP.pm", "CPAN/FTP/netrc.pm", "CPAN/HandleConfig.pm", "CPAN/Index.pm", "CPAN/InfoObj.pm", "CPAN/Kwalify.pm", "CPAN/LWP/UserAgent.pm", "CPAN/Module.pm", "CPAN/Prompt.pm", "CPAN/Queue.pm", "CPAN/Reporter/Config.pm", "CPAN/Reporter/History.pm", "CPAN/Reporter/PrereqCheck.pm", "CPAN/Reporter.pm", "CPAN/Shell.pm", "CPAN/SQLite.pm", "CPAN/Tarzip.pm", "CPAN/Version.pm", ); $VERSION = "5.5009"; # record the initial timestamp for reload. $reload = { map {$INC{$_} ? ($_,(stat $INC{$_})[9]) : ()} @relo }; @CPAN::Shell::ISA = qw(CPAN::Debug); use Cwd qw(chdir); use Carp (); $COLOR_REGISTERED ||= 0; $Help = { '?' => \"help", '!' => "eval the rest of the line as perl", a => "whois author", autobundle => "write inventory into a bundle file", b => "info about bundle", bye => \"quit", clean => "clean up a distribution's build directory", # cvs_import d => "info about a distribution", # dump exit => \"quit", failed => "list all failed actions within current session", fforce => "redo a command from scratch", force => "redo a command", get => "download a distribution", h => \"help", help => "overview over commands; 'help ...' explains specific commands", hosts => "statistics about recently used hosts", i => "info about authors/bundles/distributions/modules", install => "install a distribution", install_tested => "install all distributions tested OK", is_tested => "list all distributions tested OK", look => "open a subshell in a distribution's directory", ls => "list distributions matching a fileglob", m => "info about a module", make => "make/build a distribution", mkmyconfig => "write current config into a CPAN/MyConfig.pm file", notest => "run a (usually install) command but leave out the test phase", o => "'o conf ...' for config stuff; 'o debug ...' for debugging", perldoc => "try to get a manpage for a module", q => \"quit", quit => "leave the cpan shell", r => "review upgradable modules", readme => "display the README of a distro with a pager", recent => "show recent uploads to the CPAN", # recompile reload => "'reload cpan' or 'reload index'", report => "test a distribution and send a test report to cpantesters", reports => "info about reported tests from cpantesters", # scripts # smoke test => "test a distribution", u => "display uninstalled modules", upgrade => "combine 'r' command with immediate installation", }; { $autoload_recursion ||= 0; #-> sub CPAN::Shell::AUTOLOAD ; sub AUTOLOAD { ## no critic $autoload_recursion++; my($l) = $AUTOLOAD; my $class = shift(@_); # warn "autoload[$l] class[$class]"; $l =~ s/.*:://; if ($CPAN::Signal) { warn "Refusing to autoload '$l' while signal pending"; $autoload_recursion--; return; } if ($autoload_recursion > 1) { my $fullcommand = join " ", map { "'$_'" } $l, @_; warn "Refusing to autoload $fullcommand in recursion\n"; $autoload_recursion--; return; } if ($l =~ /^w/) { # XXX needs to be reconsidered if ($CPAN::META->has_inst('CPAN::WAIT')) { CPAN::WAIT->$l(@_); } else { $CPAN::Frontend->mywarn(qq{ Commands starting with "w" require CPAN::WAIT to be installed. Please consider installing CPAN::WAIT to use the fulltext index. For this you just need to type install CPAN::WAIT }); } } else { $CPAN::Frontend->mywarn(qq{Unknown shell command '$l'. }. qq{Type ? for help. }); } $autoload_recursion--; } } #-> sub CPAN::Shell::h ; sub h { my($class,$about) = @_; if (defined $about) { my $help; if (exists $Help->{$about}) { if (ref $Help->{$about}) { # aliases $about = ${$Help->{$about}}; } $help = $Help->{$about}; } else { $help = "No help available"; } $CPAN::Frontend->myprint("$about\: $help\n"); } else { my $filler = " " x (80 - 28 - length($CPAN::VERSION)); $CPAN::Frontend->myprint(qq{ Display Information $filler (ver $CPAN::VERSION) command argument description a,b,d,m WORD or /REGEXP/ about authors, bundles, distributions, modules i WORD or /REGEXP/ about any of the above ls AUTHOR or GLOB about files in the author's directory (with WORD being a module, bundle or author name or a distribution name of the form AUTHOR/DISTRIBUTION) Download, Test, Make, Install... get download clean make clean make make (implies get) look open subshell in dist directory test make test (implies make) readme display these README files install make install (implies test) perldoc display POD documentation Upgrade installed modules r WORDs or /REGEXP/ or NONE report updates for some/matching/all upgrade WORDs or /REGEXP/ or NONE upgrade some/matching/all modules Pragmas force CMD try hard to do command fforce CMD try harder notest CMD skip testing Other h,? display this menu ! perl-code eval a perl command o conf [opt] set and query options q quit the cpan shell reload cpan load CPAN.pm again reload index load newer indices autobundle Snapshot recent latest CPAN uploads}); } } *help = \&h; #-> sub CPAN::Shell::a ; sub a { my($self,@arg) = @_; # authors are always UPPERCASE for (@arg) { $_ = uc $_ unless /=/; } $CPAN::Frontend->myprint($self->format_result('Author',@arg)); } #-> sub CPAN::Shell::globls ; sub globls { my($self,$s,$pragmas) = @_; # ls is really very different, but we had it once as an ordinary # command in the Shell (up to rev. 321) and we could not handle # force well then my(@accept,@preexpand); if ($s =~ /[\*\?\/]/) { if ($CPAN::META->has_inst("Text::Glob")) { if (my($au,$pathglob) = $s =~ m|(.*?)/(.*)|) { my $rau = Text::Glob::glob_to_regex(uc $au); CPAN::Shell->debug("au[$au]pathglob[$pathglob]rau[$rau]") if $CPAN::DEBUG; push @preexpand, map { $_->id . "/" . $pathglob } CPAN::Shell->expand_by_method('CPAN::Author',['id'],"/$rau/"); } else { my $rau = Text::Glob::glob_to_regex(uc $s); push @preexpand, map { $_->id } CPAN::Shell->expand_by_method('CPAN::Author', ['id'], "/$rau/"); } } else { $CPAN::Frontend->mydie("Text::Glob not installed, cannot proceed"); } } else { push @preexpand, uc $s; } for (@preexpand) { unless (/^[A-Z0-9\-]+(\/|$)/i) { $CPAN::Frontend->mywarn("ls command rejects argument $_: not an author\n"); next; } push @accept, $_; } my $silent = @accept>1; my $last_alpha = ""; my @results; for my $a (@accept) { my($author,$pathglob); if ($a =~ m|(.*?)/(.*)|) { my $a2 = $1; $pathglob = $2; $author = CPAN::Shell->expand_by_method('CPAN::Author', ['id'], $a2) or $CPAN::Frontend->mydie("No author found for $a2\n"); } else { $author = CPAN::Shell->expand_by_method('CPAN::Author', ['id'], $a) or $CPAN::Frontend->mydie("No author found for $a\n"); } if ($silent) { my $alpha = substr $author->id, 0, 1; my $ad; if ($alpha eq $last_alpha) { $ad = ""; } else { $ad = "[$alpha]"; $last_alpha = $alpha; } $CPAN::Frontend->myprint($ad); } for my $pragma (@$pragmas) { if ($author->can($pragma)) { $author->$pragma(); } } CPAN->debug("author[$author]pathglob[$pathglob]silent[$silent]") if $CPAN::DEBUG; push @results, $author->ls($pathglob,$silent); # silent if # more than one # author for my $pragma (@$pragmas) { my $unpragma = "un$pragma"; if ($author->can($unpragma)) { $author->$unpragma(); } } } @results; } #-> sub CPAN::Shell::local_bundles ; sub local_bundles { my($self,@which) = @_; my($incdir,$bdir,$dh); foreach $incdir ($CPAN::Config->{'cpan_home'},@INC) { my @bbase = "Bundle"; while (my $bbase = shift @bbase) { $bdir = File::Spec->catdir($incdir,split /::/, $bbase); CPAN->debug("bdir[$bdir]\@bbase[@bbase]") if $CPAN::DEBUG; if ($dh = DirHandle->new($bdir)) { # may fail my($entry); for $entry ($dh->read) { next if $entry =~ /^\./; next unless $entry =~ /^\w+(\.pm)?(?!\n)\Z/; if (-d File::Spec->catdir($bdir,$entry)) { push @bbase, "$bbase\::$entry"; } else { next unless $entry =~ s/\.pm(?!\n)\Z//; $CPAN::META->instance('CPAN::Bundle',"$bbase\::$entry"); } } } } } } #-> sub CPAN::Shell::b ; sub b { my($self,@which) = @_; CPAN->debug("which[@which]") if $CPAN::DEBUG; $self->local_bundles; $CPAN::Frontend->myprint($self->format_result('Bundle',@which)); } #-> sub CPAN::Shell::d ; sub d { $CPAN::Frontend->myprint(shift->format_result('Distribution',@_));} #-> sub CPAN::Shell::m ; sub m { # emacs confused here }; sub mimimimimi { # emacs in sync here my $self = shift; my @m = @_; for (@m) { if (m|(?:\w+/)*\w+\.pm$|) { # same regexp in expandany s/.pm$//; s|/|::|g; } } $CPAN::Frontend->myprint($self->format_result('Module',@m)); } #-> sub CPAN::Shell::i ; sub i { my($self) = shift; my(@args) = @_; @args = '/./' unless @args; my(@result); for my $type (qw/Bundle Distribution Module/) { push @result, $self->expand($type,@args); } # Authors are always uppercase. push @result, $self->expand("Author", map { uc $_ } @args); my $result = @result == 1 ? $result[0]->as_string : @result == 0 ? "No objects found of any type for argument @args\n" : join("", (map {$_->as_glimpse} @result), scalar @result, " items found\n", ); $CPAN::Frontend->myprint($result); } #-> sub CPAN::Shell::o ; # CPAN::Shell::o and CPAN::HandleConfig::edit are closely related. 'o # conf' calls through to CPAN::HandleConfig::edit. 'o conf' should # probably have been called 'set' and 'o debug' maybe 'set debug' or # 'debug'; 'o conf ARGS' calls ->edit in CPAN/HandleConfig.pm sub o { my($self,$o_type,@o_what) = @_; $o_type ||= ""; CPAN->debug("o_type[$o_type] o_what[".join(" | ",@o_what)."]\n"); if ($o_type eq 'conf') { my($cfilter); ($cfilter) = $o_what[0] =~ m|^/(.*)/$| if @o_what; if (!@o_what or $cfilter) { # print all things, "o conf" $cfilter ||= ""; my $qrfilter = eval 'qr/$cfilter/'; if ($@) { $CPAN::Frontend->mydie("Cannot parse commandline: $@"); } my($k,$v); my $configpm = CPAN::HandleConfig->require_myconfig_or_config; $CPAN::Frontend->myprint("\$CPAN::Config options from $configpm\:\n"); for $k (sort keys %CPAN::HandleConfig::can) { next unless $k =~ /$qrfilter/; $v = $CPAN::HandleConfig::can{$k}; $CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v); } $CPAN::Frontend->myprint("\n"); for $k (sort keys %CPAN::HandleConfig::keys) { next unless $k =~ /$qrfilter/; CPAN::HandleConfig->prettyprint($k); } $CPAN::Frontend->myprint("\n"); } else { if (CPAN::HandleConfig->edit(@o_what)) { } else { $CPAN::Frontend->myprint(qq{Type 'o conf' to view all configuration }. qq{items\n\n}); } } } elsif ($o_type eq 'debug') { my(%valid); @o_what = () if defined $o_what[0] && $o_what[0] =~ /help/i; if (@o_what) { while (@o_what) { my($what) = shift @o_what; if ($what =~ s/^-// && exists $CPAN::DEBUG{$what}) { $CPAN::DEBUG &= $CPAN::DEBUG ^ $CPAN::DEBUG{$what}; next; } if ( exists $CPAN::DEBUG{$what} ) { $CPAN::DEBUG |= $CPAN::DEBUG{$what}; } elsif ($what =~ /^\d/) { $CPAN::DEBUG = $what; } elsif (lc $what eq 'all') { my($max) = 0; for (values %CPAN::DEBUG) { $max += $_; } $CPAN::DEBUG = $max; } else { my($known) = 0; for (keys %CPAN::DEBUG) { next unless lc($_) eq lc($what); $CPAN::DEBUG |= $CPAN::DEBUG{$_}; $known = 1; } $CPAN::Frontend->myprint("unknown argument [$what]\n") unless $known; } } } else { my $raw = "Valid options for debug are ". join(", ",sort(keys %CPAN::DEBUG), 'all'). qq{ or a number. Completion works on the options. }. qq{Case is ignored.}; require Text::Wrap; $CPAN::Frontend->myprint(Text::Wrap::fill("","",$raw)); $CPAN::Frontend->myprint("\n\n"); } if ($CPAN::DEBUG) { $CPAN::Frontend->myprint("Options set for debugging ($CPAN::DEBUG):\n"); my($k,$v); for $k (sort {$CPAN::DEBUG{$a} <=> $CPAN::DEBUG{$b}} keys %CPAN::DEBUG) { $v = $CPAN::DEBUG{$k}; $CPAN::Frontend->myprint(sprintf " %-14s(%s)\n", $k, $v) if $v & $CPAN::DEBUG; } } else { $CPAN::Frontend->myprint("Debugging turned off completely.\n"); } } else { $CPAN::Frontend->myprint(qq{ Known options: conf set or get configuration variables debug set or get debugging options }); } } # CPAN::Shell::paintdots_onreload sub paintdots_onreload { my($ref) = shift; sub { if ( $_[0] =~ /[Ss]ubroutine ([\w:]+) redefined/ ) { my($subr) = $1; ++$$ref; local($|) = 1; # $CPAN::Frontend->myprint(".($subr)"); $CPAN::Frontend->myprint("."); if ($subr =~ /\bshell\b/i) { # warn "debug[$_[0]]"; # It would be nice if we could detect that a # subroutine has actually changed, but for now we # practically always set the GOTOSHELL global $CPAN::GOTOSHELL=1; } return; } warn @_; }; } #-> sub CPAN::Shell::hosts ; sub hosts { my($self) = @_; my $fullstats = CPAN::FTP->_ftp_statistics(); my $history = $fullstats->{history} || []; my %S; # statistics while (my $last = pop @$history) { my $attempts = $last->{attempts} or next; my $start; if (@$attempts) { $start = $attempts->[-1]{start}; if ($#$attempts > 0) { for my $i (0..$#$attempts-1) { my $url = $attempts->[$i]{url} or next; $S{no}{$url}++; } } } else { $start = $last->{start}; } next unless $last->{thesiteurl}; # C-C? bad filenames? $S{start} = $start; $S{end} ||= $last->{end}; my $dltime = $last->{end} - $start; my $dlsize = $last->{filesize} || 0; my $url = ref $last->{thesiteurl} ? $last->{thesiteurl}->text : $last->{thesiteurl}; my $s = $S{ok}{$url} ||= {}; $s->{n}++; $s->{dlsize} ||= 0; $s->{dlsize} += $dlsize/1024; $s->{dltime} ||= 0; $s->{dltime} += $dltime; } my $res; for my $url (sort keys %{$S{ok}}) { next if $S{ok}{$url}{dltime} == 0; # div by zero push @{$res->{ok}}, [@{$S{ok}{$url}}{qw(n dlsize dltime)}, $S{ok}{$url}{dlsize}/$S{ok}{$url}{dltime}, $url, ]; } for my $url (sort keys %{$S{no}}) { push @{$res->{no}}, [$S{no}{$url}, $url, ]; } my $R = ""; # report if ($S{start} && $S{end}) { $R .= sprintf "Log starts: %s\n", $S{start} ? scalar(localtime $S{start}) : "unknown"; $R .= sprintf "Log ends : %s\n", $S{end} ? scalar(localtime $S{end}) : "unknown"; } if ($res->{ok} && @{$res->{ok}}) { $R .= sprintf "\nSuccessful downloads: N kB secs kB/s url\n"; my $i = 20; for (sort { $b->[3] <=> $a->[3] } @{$res->{ok}}) { $R .= sprintf "%4d %8d %5d %9.1f %s\n", @$_; last if --$i<=0; } } if ($res->{no} && @{$res->{no}}) { $R .= sprintf "\nUnsuccessful downloads:\n"; my $i = 20; for (sort { $b->[0] <=> $a->[0] } @{$res->{no}}) { $R .= sprintf "%4d %s\n", @$_; last if --$i<=0; } } $CPAN::Frontend->myprint($R); } # here is where 'reload cpan' is done #-> sub CPAN::Shell::reload ; sub reload { my($self,$command,@arg) = @_; $command ||= ""; $self->debug("self[$self]command[$command]arg[@arg]") if $CPAN::DEBUG; if ($command =~ /^cpan$/i) { my $redef = 0; chdir "$CPAN::iCwd" if $CPAN::iCwd; # may fail my $failed; MFILE: for my $f (@relo) { next unless exists $INC{$f}; my $p = $f; $p =~ s/\.pm$//; $p =~ s|/|::|g; $CPAN::Frontend->myprint("($p"); local($SIG{__WARN__}) = paintdots_onreload(\$redef); $self->_reload_this($f) or $failed++; my $v = eval "$p\::->VERSION"; $CPAN::Frontend->myprint("v$v)"); } $CPAN::Frontend->myprint("\n$redef subroutines redefined\n"); if ($failed) { my $errors = $failed == 1 ? "error" : "errors"; $CPAN::Frontend->mywarn("\n$failed $errors during reload. You better quit ". "this session.\n"); } } elsif ($command =~ /^index$/i) { CPAN::Index->force_reload; } else { $CPAN::Frontend->myprint(qq{cpan re-evals the CPAN modules index re-reads the index files\n}); } } # reload means only load again what we have loaded before #-> sub CPAN::Shell::_reload_this ; sub _reload_this { my($self,$f,$args) = @_; CPAN->debug("f[$f]") if $CPAN::DEBUG; return 1 unless $INC{$f}; # we never loaded this, so we do not # reload but say OK my $pwd = CPAN::anycwd(); CPAN->debug("pwd[$pwd]") if $CPAN::DEBUG; my($file); for my $inc (@INC) { $file = File::Spec->catfile($inc,split /\//, $f); last if -f $file; $file = ""; } CPAN->debug("file[$file]") if $CPAN::DEBUG; my @inc = @INC; unless ($file && -f $file) { # this thingy is not in the INC path, maybe CPAN/MyConfig.pm? $file = $INC{$f}; unless (CPAN->has_inst("File::Basename")) { @inc = File::Basename::dirname($file); } else { # do we ever need this? @inc = substr($file,0,-length($f)-1); # bring in back to me! } } CPAN->debug("file[$file]inc[@inc]") if $CPAN::DEBUG; unless (-f $file) { $CPAN::Frontend->mywarn("Found no file to reload for '$f'\n"); return; } my $mtime = (stat $file)[9]; $reload->{$f} ||= -1; my $must_reload = $mtime != $reload->{$f}; $args ||= {}; $must_reload ||= $args->{reloforce}; # o conf defaults needs this if ($must_reload) { my $fh = FileHandle->new($file) or $CPAN::Frontend->mydie("Could not open $file: $!"); my $content; { local($/); local $^W = 1; $content = <$fh>; } CPAN->debug(sprintf("reload file[%s] content[%s...]",$file,substr($content,0,128))) if $CPAN::DEBUG; my $includefile; if ($includefile = $INC{$f} and -e $includefile) { $f = $includefile; } delete $INC{$f}; local @INC = @inc; eval "require '$f'"; if ($@) { warn $@; return; } $reload->{$f} = $mtime; } else { $CPAN::Frontend->myprint("__unchanged__"); } return 1; } #-> sub CPAN::Shell::mkmyconfig ; sub mkmyconfig { my($self) = @_; if ( my $configpm = $INC{'CPAN/MyConfig.pm'} ) { $CPAN::Frontend->myprint( "CPAN::MyConfig already exists as $configpm.\n" . "Running configuration again...\n" ); require CPAN::FirstTime; CPAN::FirstTime::init($configpm); } else { # force some missing values to be filled in with defaults delete $CPAN::Config->{$_} for qw/build_dir cpan_home keep_source_where histfile/; CPAN::HandleConfig->load( make_myconfig => 1 ); } } #-> sub CPAN::Shell::_binary_extensions ; sub _binary_extensions { my($self) = shift @_; my(@result,$module,%seen,%need,$headerdone); for $module ($self->expand('Module','/./')) { my $file = $module->cpan_file; next if $file eq "N/A"; next if $file =~ /^Contact Author/; my $dist = $CPAN::META->instance('CPAN::Distribution',$file); next if $dist->isa_perl; next unless $module->xs_file; local($|) = 1; $CPAN::Frontend->myprint("."); push @result, $module; } # print join " | ", @result; $CPAN::Frontend->myprint("\n"); return @result; } #-> sub CPAN::Shell::recompile ; sub recompile { my($self) = shift @_; my($module,@module,$cpan_file,%dist); @module = $self->_binary_extensions(); for $module (@module) { # we force now and compile later, so we # don't do it twice $cpan_file = $module->cpan_file; my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); $pack->force; $dist{$cpan_file}++; } for $cpan_file (sort keys %dist) { $CPAN::Frontend->myprint(" CPAN: Recompiling $cpan_file\n\n"); my $pack = $CPAN::META->instance('CPAN::Distribution',$cpan_file); $pack->install; $CPAN::Signal = 0; # it's tempting to reset Signal, so we can # stop a package from recompiling, # e.g. IO-1.12 when we have perl5.003_10 } } #-> sub CPAN::Shell::scripts ; sub scripts { my($self, $arg) = @_; $CPAN::Frontend->mywarn(">>>> experimental command, currently unsupported <<<<\n\n"); for my $req (qw( HTML::LinkExtor Sort::Versions List::Util )) { unless ($CPAN::META->has_inst($req)) { $CPAN::Frontend->mywarn(" $req not available\n"); } } my $p = HTML::LinkExtor->new(); my $indexfile = "/home/ftp/pub/PAUSE/scripts/new/index.html"; unless (-f $indexfile) { $CPAN::Frontend->mydie("found no indexfile[$indexfile]\n"); } $p->parse_file($indexfile); my @hrefs; my $qrarg; if ($arg =~ s|^/(.+)/$|$1|) { $qrarg = eval 'qr/$arg/'; # hide construct from 5.004 } for my $l ($p->links) { my $tag = shift @$l; next unless $tag eq "a"; my %att = @$l; my $href = $att{href}; next unless $href =~ s|^\.\./authors/id/./../||; if ($arg) { if ($qrarg) { if ($href =~ $qrarg) { push @hrefs, $href; } } else { if ($href =~ /\Q$arg\E/) { push @hrefs, $href; } } } else { push @hrefs, $href; } } # now filter for the latest version if there is more than one of a name my %stems; for (sort @hrefs) { my $href = $_; s/-v?\d.*//; my $stem = $_; $stems{$stem} ||= []; push @{$stems{$stem}}, $href; } for (sort keys %stems) { my $highest; if (@{$stems{$_}} > 1) { $highest = List::Util::reduce { Sort::Versions::versioncmp($a,$b) > 0 ? $a : $b } @{$stems{$_}}; } else { $highest = $stems{$_}[0]; } $CPAN::Frontend->myprint("$highest\n"); } } sub _guess_manpage { my($self,$d,$contains,$dist) = @_; $dist =~ s/-/::/g; my $module; if (exists $contains->{$dist}) { $module = $dist; } elsif (1 == keys %$contains) { ($module) = keys %$contains; } my $manpage; if ($module) { my $m = $self->expand("Module",$module); $m->as_string; # called for side-effects, shame $manpage = $m->{MANPAGE}; } else { $manpage = "unknown"; } return $manpage; } #-> sub CPAN::Shell::_specfile ; sub _specfile { die "CPAN::Shell::_specfile() has been moved to CPAN::Plugin::Specfile::post_test()"; } #-> sub CPAN::Shell::report ; sub report { my($self,@args) = @_; unless ($CPAN::META->has_inst("CPAN::Reporter")) { $CPAN::Frontend->mydie("CPAN::Reporter not installed; cannot continue"); } local $CPAN::Config->{test_report} = 1; $self->force("test",@args); # force is there so that the test be # re-run (as documented) } # compare with is_tested #-> sub CPAN::Shell::install_tested sub install_tested { my($self,@some) = @_; $CPAN::Frontend->mywarn("install_tested() must not be called with arguments.\n"), return if @some; CPAN::Index->reload; for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { my $yaml = "$b.yml"; unless (-f $yaml) { $CPAN::Frontend->mywarn("No YAML file for $b available, skipping\n"); next; } my $yaml_content = CPAN->_yaml_loadfile($yaml); my $id = $yaml_content->[0]{distribution}{ID}; unless ($id) { $CPAN::Frontend->mywarn("No ID found in '$yaml', skipping\n"); next; } my $do = CPAN::Shell->expandany($id); unless ($do) { $CPAN::Frontend->mywarn("Could not expand ID '$id', skipping\n"); next; } unless ($do->{build_dir}) { $CPAN::Frontend->mywarn("Distro '$id' has no build_dir, skipping\n"); next; } unless ($do->{build_dir} eq $b) { $CPAN::Frontend->mywarn("Distro '$id' has build_dir '$do->{build_dir}' but expected '$b', skipping\n"); next; } push @some, $do; } $CPAN::Frontend->mywarn("No tested distributions found.\n"), return unless @some; @some = grep { $_->{make_test} && ! $_->{make_test}->failed } @some; $CPAN::Frontend->mywarn("No distributions tested with this build of perl found.\n"), return unless @some; # @some = grep { not $_->uptodate } @some; # $CPAN::Frontend->mywarn("No non-uptodate distributions tested with this build of perl found.\n"), # return unless @some; CPAN->debug("some[@some]"); for my $d (@some) { my $id = $d->can("pretty_id") ? $d->pretty_id : $d->id; $CPAN::Frontend->myprint("install_tested: Running for $id\n"); $CPAN::Frontend->mysleep(1); $self->install($d); } } #-> sub CPAN::Shell::upgrade ; sub upgrade { my($self,@args) = @_; $self->install($self->r(@args)); } #-> sub CPAN::Shell::_u_r_common ; sub _u_r_common { my($self) = shift @_; my($what) = shift @_; CPAN->debug("self[$self] what[$what] args[@_]") if $CPAN::DEBUG; Carp::croak "Usage: \$obj->_u_r_common(a|r|u)" unless $what && $what =~ /^[aru]$/; my(@args) = @_; @args = '/./' unless @args; my(@result,$module,%seen,%need,$headerdone, $version_undefs,$version_zeroes, @version_undefs,@version_zeroes); $version_undefs = $version_zeroes = 0; my $sprintf = "%s%-25s%s %9s %9s %s\n"; my @expand = $self->expand('Module',@args); if ($CPAN::DEBUG) { # Looks like noise to me, was very useful for debugging # for metadata cache my $expand = scalar @expand; $CPAN::Frontend->myprint(sprintf "%d matches in the database, time[%d]\n", $expand, time); } my @sexpand; if ($] < 5.008) { # hard to believe that the more complex sorting can lead to # stack curruptions on older perl @sexpand = sort {$a->id cmp $b->id} @expand; } else { @sexpand = map { $_->[1] } sort { $b->[0] <=> $a->[0] || $a->[1]{ID} cmp $b->[1]{ID}, } map { [$_->_is_representative_module, $_ ] } @expand; } if ($CPAN::DEBUG) { $CPAN::Frontend->myprint(sprintf "sorted at time[%d]\n", time); sleep 1; } MODULE: for $module (@sexpand) { my $file = $module->cpan_file; next MODULE unless defined $file; # ?? $file =~ s!^./../!!; my($latest) = $module->cpan_version; my($inst_file) = $module->inst_file; CPAN->debug("file[$file]latest[$latest]") if $CPAN::DEBUG; my($have); return if $CPAN::Signal; my($next_MODULE); eval { # version.pm involved! if ($inst_file) { if ($what eq "a") { $have = $module->inst_version; } elsif ($what eq "r") { $have = $module->inst_version; local($^W) = 0; if ($have eq "undef") { $version_undefs++; push @version_undefs, $module->as_glimpse; } elsif (CPAN::Version->vcmp($have,0)==0) { $version_zeroes++; push @version_zeroes, $module->as_glimpse; } ++$next_MODULE unless CPAN::Version->vgt($latest, $have); # to be pedantic we should probably say: # && !($have eq "undef" && $latest ne "undef" && $latest gt ""); # to catch the case where CPAN has a version 0 and we have a version undef } elsif ($what eq "u") { ++$next_MODULE; } } else { if ($what eq "a") { ++$next_MODULE; } elsif ($what eq "r") { ++$next_MODULE; } elsif ($what eq "u") { $have = "-"; } } }; next MODULE if $next_MODULE; if ($@) { $CPAN::Frontend->mywarn (sprintf("Error while comparing cpan/installed versions of '%s': INST_FILE: %s INST_VERSION: %s %s CPAN_VERSION: %s %s ", $module->id, $inst_file || "", (defined $have ? $have : "[UNDEFINED]"), (ref $have ? ref $have : ""), $latest, (ref $latest ? ref $latest : ""), )); next MODULE; } return if $CPAN::Signal; # this is sometimes lengthy $seen{$file} ||= 0; if ($what eq "a") { push @result, sprintf "%s %s\n", $module->id, $have; } elsif ($what eq "r") { push @result, $module->id; next MODULE if $seen{$file}++; } elsif ($what eq "u") { push @result, $module->id; next MODULE if $seen{$file}++; next MODULE if $file =~ /^Contact/; } unless ($headerdone++) { $CPAN::Frontend->myprint("\n"); $CPAN::Frontend->myprint(sprintf( $sprintf, "", "Package namespace", "", "installed", "latest", "in CPAN file" )); } my $color_on = ""; my $color_off = ""; if ( $COLOR_REGISTERED && $CPAN::META->has_inst("Term::ANSIColor") && $module->description ) { $color_on = Term::ANSIColor::color("green"); $color_off = Term::ANSIColor::color("reset"); } $CPAN::Frontend->myprint(sprintf $sprintf, $color_on, $module->id, $color_off, $have, $latest, $file); $need{$module->id}++; } unless (%need) { if (!@expand || $what eq "u") { $CPAN::Frontend->myprint("No modules found for @args\n"); } elsif ($what eq "r") { $CPAN::Frontend->myprint("All modules are up to date for @args\n"); } } if ($what eq "r") { if ($version_zeroes) { my $s_has = $version_zeroes > 1 ? "s have" : " has"; $CPAN::Frontend->myprint(qq{$version_zeroes installed module$s_has }. qq{a version number of 0\n}); if ($CPAN::Config->{show_zero_versions}) { local $" = "\t"; $CPAN::Frontend->myprint(qq{ they are\n\t@version_zeroes\n}); $CPAN::Frontend->myprint(qq{(use 'o conf show_zero_versions 0' }. qq{to hide them)\n}); } else { $CPAN::Frontend->myprint(qq{(use 'o conf show_zero_versions 1' }. qq{to show them)\n}); } } if ($version_undefs) { my $s_has = $version_undefs > 1 ? "s have" : " has"; $CPAN::Frontend->myprint(qq{$version_undefs installed module$s_has no }. qq{parsable version number\n}); if ($CPAN::Config->{show_unparsable_versions}) { local $" = "\t"; $CPAN::Frontend->myprint(qq{ they are\n\t@version_undefs\n}); $CPAN::Frontend->myprint(qq{(use 'o conf show_unparsable_versions 0' }. qq{to hide them)\n}); } else { $CPAN::Frontend->myprint(qq{(use 'o conf show_unparsable_versions 1' }. qq{to show them)\n}); } } } @result; } #-> sub CPAN::Shell::r ; sub r { shift->_u_r_common("r",@_); } #-> sub CPAN::Shell::u ; sub u { shift->_u_r_common("u",@_); } #-> sub CPAN::Shell::failed ; sub failed { my($self,$only_id,$silent) = @_; my @failed = $self->find_failed($only_id); my $scope; if ($only_id) { $scope = "this command"; } elsif ($CPAN::Index::HAVE_REANIMATED) { $scope = "this or a previous session"; # it might be nice to have a section for previous session and # a second for this } else { $scope = "this session"; } if (@failed) { my $print; my $debug = 0; if ($debug) { $print = join "", map { sprintf "%5d %-45s: %s %s\n", @$_ } sort { $a->[0] <=> $b->[0] } @failed; } else { $print = join "", map { sprintf " %-45s: %s %s\n", @$_[1..3] } sort { $a->[0] <=> $b->[0] || $a->[4] <=> $b->[4] } @failed; } $CPAN::Frontend->myprint("Failed during $scope:\n$print"); } elsif (!$only_id || !$silent) { $CPAN::Frontend->myprint("Nothing failed in $scope\n"); } } sub find_failed { my($self,$only_id) = @_; my @failed; DIST: for my $d (sort { $a->id cmp $b->id } $CPAN::META->all_objects("CPAN::Distribution")) { my $failed = ""; NAY: for my $nosayer ( # order matters! "unwrapped", "writemakefile", "signature_verify", "make", "make_test", "install", "make_clean", ) { next unless exists $d->{$nosayer}; next unless defined $d->{$nosayer}; next unless ( UNIVERSAL::can($d->{$nosayer},"failed") ? $d->{$nosayer}->failed : $d->{$nosayer} =~ /^NO/ ); next NAY if $only_id && $only_id != ( UNIVERSAL::can($d->{$nosayer},"commandid") ? $d->{$nosayer}->commandid : $CPAN::CurrentCommandId ); $failed = $nosayer; last; } next DIST unless $failed; my $id = $d->id; $id =~ s|^./../||; ### XXX need to flag optional modules as '(optional)' if they are # from recommends/suggests -- i.e. *show* failure, but make it clear # it was failure of optional module -- xdg, 2012-04-01 $id = "(optional) $id" if ! $d->{mandatory}; #$print .= sprintf( # " %-45s: %s %s\n", push @failed, ( UNIVERSAL::can($d->{$failed},"failed") ? [ $d->{$failed}->commandid, $id, $failed, $d->{$failed}->text, $d->{$failed}{TIME}||0, !! $d->{mandatory}, ] : [ 1, $id, $failed, $d->{$failed}, 0, !! $d->{mandatory}, ] ); } return @failed; } sub mandatory_dist_failed { my ($self) = @_; return grep { $_->[5] } $self->find_failed($CPAN::CurrentCommandID); } # XXX intentionally undocumented because completely bogus, unportable, # useless, etc. #-> sub CPAN::Shell::status ; sub status { my($self) = @_; require Devel::Size; my $ps = FileHandle->new; open $ps, "/proc/$$/status"; my $vm = 0; while (<$ps>) { next unless /VmSize:\s+(\d+)/; $vm = $1; last; } $CPAN::Frontend->mywarn(sprintf( "%-27s %6d\n%-27s %6d\n", "vm", $vm, "CPAN::META", Devel::Size::total_size($CPAN::META)/1024, )); for my $k (sort keys %$CPAN::META) { next unless substr($k,0,4) eq "read"; warn sprintf " %-26s %6d\n", $k, Devel::Size::total_size($CPAN::META->{$k})/1024; for my $k2 (sort keys %{$CPAN::META->{$k}}) { warn sprintf " %-25s %6d (keys: %6d)\n", $k2, Devel::Size::total_size($CPAN::META->{$k}{$k2})/1024, scalar keys %{$CPAN::META->{$k}{$k2}}; } } } # compare with install_tested #-> sub CPAN::Shell::is_tested sub is_tested { my($self) = @_; CPAN::Index->reload; for my $b (reverse $CPAN::META->_list_sorted_descending_is_tested) { my $time; if ($CPAN::META->{is_tested}{$b}) { $time = scalar(localtime $CPAN::META->{is_tested}{$b}); } else { $time = scalar localtime; $time =~ s/\S/?/g; } $CPAN::Frontend->myprint(sprintf "%s %s\n", $time, $b); } } #-> sub CPAN::Shell::autobundle ; sub autobundle { my($self) = shift; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; my(@bundle) = $self->_u_r_common("a",@_); my($todir) = File::Spec->catdir($CPAN::Config->{'cpan_home'},"Bundle"); File::Path::mkpath($todir); unless (-d $todir) { $CPAN::Frontend->myprint("Couldn't mkdir $todir for some reason\n"); return; } my($y,$m,$d) = (localtime)[5,4,3]; $y+=1900; $m++; my($c) = 0; my($me) = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, $c; my($to) = File::Spec->catfile($todir,"$me.pm"); while (-f $to) { $me = sprintf "Snapshot_%04d_%02d_%02d_%02d", $y, $m, $d, ++$c; $to = File::Spec->catfile($todir,"$me.pm"); } my($fh) = FileHandle->new(">$to") or Carp::croak "Can't open >$to: $!"; $fh->print( "package Bundle::$me;\n\n", "\$","VERSION = '0.01';\n\n", # hide from perl-reversion "1;\n\n", "__END__\n\n", "=head1 NAME\n\n", "Bundle::$me - Snapshot of installation on ", $Config::Config{'myhostname'}, " on ", scalar(localtime), "\n\n=head1 SYNOPSIS\n\n", "perl -MCPAN -e 'install Bundle::$me'\n\n", "=head1 CONTENTS\n\n", join("\n", @bundle), "\n\n=head1 CONFIGURATION\n\n", Config->myconfig, "\n\n=head1 AUTHOR\n\n", "This Bundle has been generated automatically ", "by the autobundle routine in CPAN.pm.\n", ); $fh->close; $CPAN::Frontend->myprint("\nWrote bundle file $to\n\n"); return $to; } #-> sub CPAN::Shell::expandany ; sub expandany { my($self,$s) = @_; CPAN->debug("s[$s]") if $CPAN::DEBUG; my $module_as_path = ""; if ($s =~ m|(?:\w+/)*\w+\.pm$|) { # same regexp in sub m $module_as_path = $s; $module_as_path =~ s/.pm$//; $module_as_path =~ s|/|::|g; } if ($module_as_path) { if ($module_as_path =~ m|^Bundle::|) { $self->local_bundles; return $self->expand('Bundle',$module_as_path); } else { return $self->expand('Module',$module_as_path) if $CPAN::META->exists('CPAN::Module',$module_as_path); } } elsif ($s =~ m|/| or substr($s,-1,1) eq ".") { # looks like a file or a directory $s = CPAN::Distribution->normalize($s); return $CPAN::META->instance('CPAN::Distribution',$s); # Distributions spring into existence, not expand } elsif ($s =~ m|^Bundle::|) { $self->local_bundles; # scanning so late for bundles seems # both attractive and crumpy: always # current state but easy to forget # somewhere return $self->expand('Bundle',$s); } else { return $self->expand('Module',$s) if $CPAN::META->exists('CPAN::Module',$s); } return; } #-> sub CPAN::Shell::expand ; sub expand { my $self = shift; my($type,@args) = @_; CPAN->debug("type[$type]args[@args]") if $CPAN::DEBUG; my $class = "CPAN::$type"; my $methods = ['id']; for my $meth (qw(name)) { next unless $class->can($meth); push @$methods, $meth; } $self->expand_by_method($class,$methods,@args); } #-> sub CPAN::Shell::expand_by_method ; sub expand_by_method { my $self = shift; my($class,$methods,@args) = @_; my($arg,@m); for $arg (@args) { my($regex,$command); if ($arg =~ m|^/(.*)/$|) { $regex = $1; # FIXME: there seem to be some ='s in the author data, which trigger # a failure here. This needs to be contemplated. # } elsif ($arg =~ m/=/) { # $command = 1; } my $obj; CPAN->debug(sprintf "class[%s]regex[%s]command[%s]", $class, defined $regex ? $regex : "UNDEFINED", defined $command ? $command : "UNDEFINED", ) if $CPAN::DEBUG; if (defined $regex) { if (CPAN::_sqlite_running()) { CPAN::Index->reload; $CPAN::SQLite->search($class, $regex); } for $obj ( $CPAN::META->all_objects($class) ) { unless ($obj && UNIVERSAL::can($obj,"id") && $obj->id) { # BUG, we got an empty object somewhere require Data::Dumper; CPAN->debug(sprintf( "Bug in CPAN: Empty id on obj[%s][%s]", $obj, Data::Dumper::Dumper($obj) )) if $CPAN::DEBUG; next; } for my $method (@$methods) { my $match = eval {$obj->$method() =~ /$regex/i}; if ($@) { my($err) = $@ =~ /^(.+) at .+? line \d+\.$/; $err ||= $@; # if we were too restrictive above $CPAN::Frontend->mydie("$err\n"); } elsif ($match) { push @m, $obj; last; } } } } elsif ($command) { die "equal sign in command disabled (immature interface), ". "you can set ! \$CPAN::Shell::ADVANCED_QUERY=1 to enable it. But please note, this is HIGHLY EXPERIMENTAL code that may go away anytime.\n" unless $ADVANCED_QUERY; my($method,$criterion) = $arg =~ /(.+?)=(.+)/; my($matchcrit) = $criterion =~ m/^~(.+)/; for my $self ( sort {$a->id cmp $b->id} $CPAN::META->all_objects($class) ) { my $lhs = $self->$method() or next; # () for 5.00503 if ($matchcrit) { push @m, $self if $lhs =~ m/$matchcrit/; } else { push @m, $self if $lhs eq $criterion; } } } else { my($xarg) = $arg; if ( $class eq 'CPAN::Bundle' ) { $xarg =~ s/^(Bundle::)?(.*)/Bundle::$2/; } elsif ($class eq "CPAN::Distribution") { $xarg = CPAN::Distribution->normalize($arg); } else { $xarg =~ s/:+/::/g; } if ($CPAN::META->exists($class,$xarg)) { $obj = $CPAN::META->instance($class,$xarg); } elsif ($CPAN::META->exists($class,$arg)) { $obj = $CPAN::META->instance($class,$arg); } else { next; } push @m, $obj; } } @m = sort {$a->id cmp $b->id} @m; if ( $CPAN::DEBUG ) { my $wantarray = wantarray; my $join_m = join ",", map {$_->id} @m; # $self->debug("wantarray[$wantarray]join_m[$join_m]"); my $count = scalar @m; $self->debug("class[$class]wantarray[$wantarray]count m[$count]"); } return wantarray ? @m : $m[0]; } #-> sub CPAN::Shell::format_result ; sub format_result { my($self) = shift; my($type,@args) = @_; @args = '/./' unless @args; my(@result) = $self->expand($type,@args); my $result = @result == 1 ? $result[0]->as_string : @result == 0 ? "No objects of type $type found for argument @args\n" : join("", (map {$_->as_glimpse} @result), scalar @result, " items found\n", ); $result; } #-> sub CPAN::Shell::report_fh ; { my $installation_report_fh; my $previously_noticed = 0; sub report_fh { return $installation_report_fh if $installation_report_fh; if ($CPAN::META->has_usable("File::Temp")) { $installation_report_fh = File::Temp->new( dir => File::Spec->tmpdir, template => 'cpan_install_XXXX', suffix => '.txt', unlink => 0, ); } unless ( $installation_report_fh ) { warn("Couldn't open installation report file; " . "no report file will be generated." ) unless $previously_noticed++; } } } # The only reason for this method is currently to have a reliable # debugging utility that reveals which output is going through which # channel. No, I don't like the colors ;-) # to turn colordebugging on, write # cpan> o conf colorize_output 1 #-> sub CPAN::Shell::colorize_output ; { my $print_ornamented_have_warned = 0; sub colorize_output { my $colorize_output = $CPAN::Config->{colorize_output}; if ($colorize_output && $^O eq 'MSWin32' && !$CPAN::META->has_inst("Win32::Console::ANSI")) { unless ($print_ornamented_have_warned++) { # no myprint/mywarn within myprint/mywarn! warn "Colorize_output is set to true but Win32::Console::ANSI is not installed. To activate colorized output, please install Win32::Console::ANSI.\n\n"; } $colorize_output = 0; } if ($colorize_output && !$CPAN::META->has_inst("Term::ANSIColor")) { unless ($print_ornamented_have_warned++) { # no myprint/mywarn within myprint/mywarn! warn "Colorize_output is set to true but Term::ANSIColor is not installed. To activate colorized output, please install Term::ANSIColor.\n\n"; } $colorize_output = 0; } return $colorize_output; } } #-> sub CPAN::Shell::print_ornamented ; sub print_ornamented { my($self,$what,$ornament) = @_; return unless defined $what; local $| = 1; # Flush immediately if ( $CPAN::Be_Silent ) { # WARNING: variable Be_Silent is poisoned and must be eliminated. print {report_fh()} $what; return; } my $swhat = "$what"; # stringify if it is an object if ($CPAN::Config->{term_is_latin}) { # note: deprecated, need to switch to $LANG and $LC_* # courtesy jhi: $swhat =~ s{([\xC0-\xDF])([\x80-\xBF])}{chr(ord($1)<<6&0xC0|ord($2)&0x3F)}eg; #}; } if ($self->colorize_output) { if ( $CPAN::DEBUG && $swhat =~ /^Debug\(/ ) { # if you want to have this configurable, please file a bug report $ornament = $CPAN::Config->{colorize_debug} || "black on_cyan"; } my $color_on = eval { Term::ANSIColor::color($ornament) } || ""; if ($@) { print "Term::ANSIColor rejects color[$ornament]: $@\n Please choose a different color (Hint: try 'o conf init /color/')\n"; } # GGOLDBACH/Test-GreaterVersion-0.008 broke without this # $trailer construct. We want the newline be the last thing if # there is a newline at the end ensuring that the next line is # empty for other players my $trailer = ""; $trailer = $1 if $swhat =~ s/([\r\n]+)\z//; print $color_on, $swhat, Term::ANSIColor::color("reset"), $trailer; } else { print $swhat; } } #-> sub CPAN::Shell::myprint ; # where is myprint/mywarn/Frontend/etc. documented? Where to use what? # I think, we send everything to STDOUT and use print for normal/good # news and warn for news that need more attention. Yes, this is our # working contract for now. sub myprint { my($self,$what) = @_; $self->print_ornamented($what, $CPAN::Config->{colorize_print}||'bold blue on_white', ); } my %already_printed; #-> sub CPAN::Shell::mywarnonce ; sub myprintonce { my($self,$what) = @_; $self->myprint($what) unless $already_printed{$what}++; } sub optprint { my($self,$category,$what) = @_; my $vname = $category . "_verbosity"; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; if (!$CPAN::Config->{$vname} || $CPAN::Config->{$vname} =~ /^v/ ) { $CPAN::Frontend->myprint($what); } } #-> sub CPAN::Shell::myexit ; sub myexit { my($self,$what) = @_; $self->myprint($what); exit; } #-> sub CPAN::Shell::mywarn ; sub mywarn { my($self,$what) = @_; $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white'); } my %already_warned; #-> sub CPAN::Shell::mywarnonce ; sub mywarnonce { my($self,$what) = @_; $self->mywarn($what) unless $already_warned{$what}++; } # only to be used for shell commands #-> sub CPAN::Shell::mydie ; sub mydie { my($self,$what) = @_; $self->mywarn($what); # If it is the shell, we want the following die to be silent, # but if it is not the shell, we would need a 'die $what'. We need # to take care that only shell commands use mydie. Is this # possible? die "\n"; } # sub CPAN::Shell::colorable_makemaker_prompt ; sub colorable_makemaker_prompt { my($foo,$bar,$ornament) = @_; $ornament ||= "colorize_print"; if (CPAN::Shell->colorize_output) { my $ornament = $CPAN::Config->{$ornament}||'bold blue on_white'; my $color_on = eval { Term::ANSIColor::color($ornament); } || ""; print $color_on; } my $ans = ExtUtils::MakeMaker::prompt($foo,$bar); if (CPAN::Shell->colorize_output) { print Term::ANSIColor::color('reset'); } return $ans; } # use this only for unrecoverable errors! #-> sub CPAN::Shell::unrecoverable_error ; sub unrecoverable_error { my($self,$what) = @_; my @lines = split /\n/, $what; my $longest = 0; for my $l (@lines) { $longest = length $l if length $l > $longest; } $longest = 62 if $longest > 62; for my $l (@lines) { if ($l =~ /^\s*$/) { $l = "\n"; next; } $l = "==> $l"; if (length $l < 66) { $l = pack "A66 A*", $l, "<=="; } $l .= "\n"; } unshift @lines, "\n"; $self->mydie(join "", @lines); } #-> sub CPAN::Shell::mysleep ; sub mysleep { return if $ENV{AUTOMATED_TESTING} || ! -t STDOUT; my($self, $sleep) = @_; if (CPAN->has_inst("Time::HiRes")) { Time::HiRes::sleep($sleep); } else { sleep($sleep < 1 ? 1 : int($sleep + 0.5)); } } #-> sub CPAN::Shell::setup_output ; sub setup_output { return if -t STDOUT; my $odef = select STDERR; $| = 1; select STDOUT; $| = 1; select $odef; } #-> sub CPAN::Shell::rematein ; # RE-adme||MA-ke||TE-st||IN-stall : nearly everything runs through here sub rematein { my $self = shift; # this variable was global and disturbed programmers, so localize: local $CPAN::Distrostatus::something_has_failed_at; my($meth,@some) = @_; my @pragma; while($meth =~ /^(ff?orce|notest)$/) { push @pragma, $meth; $meth = shift @some or $CPAN::Frontend->mydie("Pragma $pragma[-1] used without method: ". "cannot continue"); } setup_output(); CPAN->debug("pragma[@pragma]meth[$meth]some[@some]") if $CPAN::DEBUG; # Here is the place to set "test_count" on all involved parties to # 0. We then can pass this counter on to the involved # distributions and those can refuse to test if test_count > X. In # the first stab at it we could use a 1 for "X". # But when do I reset the distributions to start with 0 again? # Jost suggested to have a random or cycling interaction ID that # we pass through. But the ID is something that is just left lying # around in addition to the counter, so I'd prefer to set the # counter to 0 now, and repeat at the end of the loop. But what # about dependencies? They appear later and are not reset, they # enter the queue but not its copy. How do they get a sensible # test_count? # With configure_requires, "get" is vulnerable in recursion. my $needs_recursion_protection = "get|make|test|install"; # construct the queue my($s,@s,@qcopy); STHING: foreach $s (@some) { my $obj; if (ref $s) { CPAN->debug("s is an object[$s]") if $CPAN::DEBUG; $obj = $s; } elsif ($s =~ m|[\$\@\%]|) { # looks like a perl variable } elsif ($s =~ m|^/|) { # looks like a regexp if (substr($s,-1,1) eq ".") { $obj = CPAN::Shell->expandany($s); } else { my @obj; CLASS: for my $class (qw(Distribution Bundle Module)) { if (@obj = $self->expand($class,$s)) { last CLASS; } } if (@obj) { if (1==@obj) { $obj = $obj[0]; } else { $CPAN::Frontend->mywarn("Sorry, $meth with a regular expression is ". "only supported when unambiguous.\nRejecting argument '$s'\n"); $CPAN::Frontend->mysleep(2); next STHING; } } } } elsif ($meth eq "ls") { $self->globls($s,\@pragma); next STHING; } else { CPAN->debug("calling expandany [$s]") if $CPAN::DEBUG; $obj = CPAN::Shell->expandany($s); } if (0) { } elsif (ref $obj) { if ($meth =~ /^($needs_recursion_protection)$/) { # it would be silly to check for recursion for look or dump # (we are in CPAN::Shell::rematein) CPAN->debug("Testing against recursion") if $CPAN::DEBUG; eval { $obj->color_cmd_tmps(0,1); }; if ($@) { if (ref $@ and $@->isa("CPAN::Exception::RecursiveDependency")) { $CPAN::Frontend->mywarn($@); } else { if (0) { require Carp; Carp::confess(sprintf "DEBUG: \$\@[%s]ref[%s]", $@, ref $@); } die; } } } CPAN::Queue->queue_item(qmod => $obj->id, reqtype => "c", optional => ''); push @qcopy, $obj; } elsif ($CPAN::META->exists('CPAN::Author',uc($s))) { $obj = $CPAN::META->instance('CPAN::Author',uc($s)); if ($meth =~ /^(dump|ls|reports)$/) { $obj->$meth(); } else { $CPAN::Frontend->mywarn( join "", "Don't be silly, you can't $meth ", $obj->fullname, " ;-)\n" ); $CPAN::Frontend->mysleep(2); } } elsif ($s =~ m|[\$\@\%]| && $meth eq "dump") { CPAN::InfoObj->dump($s); } else { $CPAN::Frontend ->mywarn(qq{Warning: Cannot $meth $s, }. qq{don't know what it is. Try the command i /$s/ to find objects with matching identifiers. }); $CPAN::Frontend->mysleep(2); } } # queuerunner (please be warned: when I started to change the # queue to hold objects instead of names, I made one or two # mistakes and never found which. I reverted back instead) QITEM: while (my $q = CPAN::Queue->first) { my $obj; my $s = $q->as_string; my $reqtype = $q->reqtype || ""; my $optional = $q->optional || ""; $obj = CPAN::Shell->expandany($s); unless ($obj) { # don't know how this can happen, maybe we should panic, # but maybe we get a solution from the first user who hits # this unfortunate exception? $CPAN::Frontend->mywarn("Warning: Could not expand string '$s' ". "to an object. Skipping.\n"); $CPAN::Frontend->mysleep(5); CPAN::Queue->delete_first($s); next QITEM; } $obj->{reqtype} ||= ""; my $type = ref $obj; if ( $type eq 'CPAN::Distribution' || $type eq 'CPAN::Bundle' ) { $obj->{mandatory} ||= ! $optional; # once mandatory, always mandatory } elsif ( $type eq 'CPAN::Module' ) { $obj->{mandatory} ||= ! $optional; # once mandatory, always mandatory if (my $d = $obj->distribution) { $d->{mandatory} ||= ! $optional; # once mandatory, always mandatory } elsif ($optional) { # the queue object does not know who was recommending/suggesting us:( # So we only vaguely write "optional". $CPAN::Frontend->mywarn("Warning: optional module '$s' ". "not known. Skipping.\n"); CPAN::Queue->delete_first($s); next QITEM; } } { # force debugging because CPAN::SQLite somehow delivers us # an empty object; # local $CPAN::DEBUG = 1024; # Shell; probably fixed now CPAN->debug("s[$s]obj-reqtype[$obj->{reqtype}]". "q-reqtype[$reqtype]") if $CPAN::DEBUG; } if ($obj->{reqtype}) { if ($obj->{reqtype} eq "b" && $reqtype =~ /^[rc]$/) { $obj->{reqtype} = $reqtype; if ( exists $obj->{install} && ( UNIVERSAL::can($obj->{install},"failed") ? $obj->{install}->failed : $obj->{install} =~ /^NO/ ) ) { delete $obj->{install}; $CPAN::Frontend->mywarn ("Promoting $obj->{ID} from 'build_requires' to 'requires'"); } } } else { $obj->{reqtype} = $reqtype; } for my $pragma (@pragma) { if ($pragma && $obj->can($pragma)) { $obj->$pragma($meth); } } if (UNIVERSAL::can($obj, 'called_for')) { $obj->called_for($s) unless $obj->called_for; } CPAN->debug(qq{pragma[@pragma]meth[$meth]}. qq{ID[$obj->{ID}]}) if $CPAN::DEBUG; push @qcopy, $obj; if ($meth =~ /^(report)$/) { # they came here with a pragma? $self->$meth($obj); } elsif (! UNIVERSAL::can($obj,$meth)) { # Must never happen my $serialized = ""; if (0) { } elsif ($CPAN::META->has_inst("YAML::Syck")) { $serialized = YAML::Syck::Dump($obj); } elsif ($CPAN::META->has_inst("YAML")) { $serialized = YAML::Dump($obj); } elsif ($CPAN::META->has_inst("Data::Dumper")) { $serialized = Data::Dumper::Dumper($obj); } else { require overload; $serialized = overload::StrVal($obj); } CPAN->debug("Going to panic. meth[$meth]s[$s]") if $CPAN::DEBUG; $CPAN::Frontend->mydie("Panic: obj[$serialized] cannot meth[$meth]"); } else { my $upgraded_meth = $meth; if ( $meth eq "make" and $obj->{reqtype} eq "b" ) { # rt 86915 $upgraded_meth = "test"; } if ($obj->$upgraded_meth()) { CPAN::Queue->delete($s); CPAN->debug("Succeeded and deleted from queue. pragma[@pragma]meth[$meth][s][$s]") if $CPAN::DEBUG; } else { CPAN->debug("Failed. pragma[@pragma]meth[$meth]s[$s]") if $CPAN::DEBUG; } } $obj->undelay; for my $pragma (@pragma) { my $unpragma = "un$pragma"; if ($obj->can($unpragma)) { $obj->$unpragma(); } } # if any failures occurred and the current object is mandatory, we # still don't know if *it* failed or if it was another (optional) # module, so we have to check that explicitly (and expensively) if ( $CPAN::Config->{halt_on_failure} && $obj->{mandatory} && CPAN::Distrostatus::something_has_just_failed() && $self->mandatory_dist_failed() ) { $CPAN::Frontend->mywarn("Stopping: '$meth' failed for '$s'.\n"); CPAN::Queue->nullify_queue; last QITEM; } CPAN::Queue->delete_first($s); } if ($meth =~ /^($needs_recursion_protection)$/) { for my $obj (@qcopy) { $obj->color_cmd_tmps(0,0); } } } #-> sub CPAN::Shell::recent ; sub recent { my($self) = @_; if ($CPAN::META->has_inst("XML::LibXML")) { my $url = $CPAN::Defaultrecent; $CPAN::Frontend->myprint("Fetching '$url'\n"); unless ($CPAN::META->has_usable("LWP")) { $CPAN::Frontend->mydie("LWP not installed; cannot continue"); } CPAN::LWP::UserAgent->config; my $Ua; eval { $Ua = CPAN::LWP::UserAgent->new; }; if ($@) { $CPAN::Frontend->mydie("CPAN::LWP::UserAgent->new dies with $@\n"); } my $resp = $Ua->get($url); unless ($resp->is_success) { $CPAN::Frontend->mydie(sprintf "Could not download '%s': %s\n", $url, $resp->code); } $CPAN::Frontend->myprint("DONE\n\n"); my $xml = XML::LibXML->new->parse_string($resp->content); if (0) { my $s = $xml->serialize(2); $s =~ s/\n\s*\n/\n/g; $CPAN::Frontend->myprint($s); return; } my @distros; if ($url =~ /winnipeg/) { my $pubdate = $xml->findvalue("/rss/channel/pubDate"); $CPAN::Frontend->myprint(" pubDate: $pubdate\n\n"); for my $eitem ($xml->findnodes("/rss/channel/item")) { my $distro = $eitem->findvalue("enclosure/\@url"); $distro =~ s|.*?/authors/id/./../||; my $size = $eitem->findvalue("enclosure/\@length"); my $desc = $eitem->findvalue("description"); $desc =~ s/.+? - //; $CPAN::Frontend->myprint("$distro [$size b]\n $desc\n"); push @distros, $distro; } } elsif ($url =~ /search.*uploads.rdf/) { # xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" # xmlns="http://purl.org/rss/1.0/" # xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" # xmlns:dc="http://purl.org/dc/elements/1.1/" # xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" # xmlns:admin="http://webns.net/mvcb/" my $dc_date = $xml->findvalue("//*[local-name(.) = 'RDF']/*[local-name(.) = 'channel']/*[local-name(.) = 'date']"); $CPAN::Frontend->myprint(" dc:date: $dc_date\n\n"); my $finish_eitem = 0; local $SIG{INT} = sub { $finish_eitem = 1 }; EITEM: for my $eitem ($xml->findnodes("//*[local-name(.) = 'RDF']/*[local-name(.) = 'item']")) { my $distro = $eitem->findvalue("\@rdf:about"); $distro =~ s|.*~||; # remove up to the tilde before the name $distro =~ s|/$||; # remove trailing slash $distro =~ s|([^/]+)|\U$1\E|; # upcase the name my $author = uc $1 or die "distro[$distro] without author, cannot continue"; my $desc = $eitem->findvalue("*[local-name(.) = 'description']"); my $i = 0; SUBDIRTEST: while () { last SUBDIRTEST if ++$i >= 6; # half a dozen must do! if (my @ret = $self->globls("$distro*")) { @ret = grep {$_->[2] !~ /meta/} @ret; @ret = grep {length $_->[2]} @ret; if (@ret) { $distro = "$author/$ret[0][2]"; last SUBDIRTEST; } } $distro =~ s|/|/*/|; # allow it to reside in a subdirectory } next EITEM if $distro =~ m|\*|; # did not find the thing $CPAN::Frontend->myprint("____$desc\n"); push @distros, $distro; last EITEM if $finish_eitem; } } return \@distros; } else { # deprecated old version $CPAN::Frontend->mydie("no XML::LibXML installed, cannot continue\n"); } } #-> sub CPAN::Shell::smoke ; sub smoke { my($self) = @_; my $distros = $self->recent; DISTRO: for my $distro (@$distros) { next if $distro =~ m|/Bundle-|; # XXX crude heuristic to skip bundles $CPAN::Frontend->myprint(sprintf "Downloading and testing '$distro'\n"); { my $skip = 0; local $SIG{INT} = sub { $skip = 1 }; for (0..9) { $CPAN::Frontend->myprint(sprintf "\r%2d (Hit ^C to skip)", 10-$_); sleep 1; if ($skip) { $CPAN::Frontend->myprint(" skipped\n"); next DISTRO; } } } $CPAN::Frontend->myprint("\r \n"); # leave the dirty line with a newline $self->test($distro); } } { # set up the dispatching methods no strict "refs"; for my $command (qw( clean cvs_import dump force fforce get install look ls make notest perldoc readme reports test )) { *$command = sub { shift->rematein($command, @_); }; } } 1; ��������������������������������������������CPAN/HandleConfig.pm��������������������������������������������������������������������������������0000444�����������������00000057234�15111204742�0010152 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package CPAN::HandleConfig; use strict; use vars qw(%can %keys $loading $VERSION); use File::Path (); use File::Spec (); use File::Basename (); use Carp (); =head1 NAME CPAN::HandleConfig - internal configuration handling for CPAN.pm =cut $VERSION = "5.5013"; # see also CPAN::Config::VERSION at end of file %can = ( commit => "Commit changes to disk", defaults => "Reload defaults from disk", help => "Short help about 'o conf' usage", init => "Interactive setting of all options", ); # Q: where is the "How do I add a new config option" HOWTO? # A1: svn diff -r 757:758 # where dagolden added test_report [git e997b71de88f1019a1472fc13cb97b1b7f96610f] # A2: svn diff -r 985:986 # where andk added yaml_module [git 312b6d9b12b1bdec0b6e282d853482145475021f] # A3: 1. add new config option to %keys below # 2. add a Pod description in CPAN::FirstTime in the DESCRIPTION # section; it should include a prompt line; see others for # examples # 3. add a "matcher" section in CPAN::FirstTime::init that includes # a prompt function; see others for examples # 4. add config option to documentation section in CPAN.pm %keys = map { $_ => undef } ( "allow_installing_module_downgrades", "allow_installing_outdated_dists", "applypatch", "auto_commit", "build_cache", "build_dir", "build_dir_reuse", "build_requires_install_policy", "bzip2", "cache_metadata", "check_sigs", "cleanup_after_install", "colorize_debug", "colorize_output", "colorize_print", "colorize_warn", "commandnumber_in_prompt", "commands_quote", "connect_to_internet_ok", "cpan_home", "curl", "dontload_hash", # deprecated after 1.83_68 (rev. 581) "dontload_list", "ftp", "ftp_passive", "ftp_proxy", "ftpstats_size", "ftpstats_period", "getcwd", "gpg", "gzip", "halt_on_failure", "histfile", "histsize", "http_proxy", "inactivity_timeout", "index_expire", "inhibit_startup_message", "keep_source_where", "load_module_verbosity", "lynx", "make", "make_arg", "make_install_arg", "make_install_make_command", "makepl_arg", "mbuild_arg", "mbuild_install_arg", "mbuild_install_build_command", "mbuildpl_arg", "ncftp", "ncftpget", "no_proxy", "pager", "password", "patch", "patches_dir", "perl5lib_verbosity", "plugin_list", "prefer_external_tar", "prefer_installer", "prefs_dir", "prerequisites_policy", "proxy_pass", "proxy_user", "pushy_https", "randomize_urllist", "recommends_policy", "scan_cache", "shell", "show_unparsable_versions", "show_upload_date", "show_zero_versions", "suggests_policy", "tar", "tar_verbosity", "term_is_latin", "term_ornaments", "test_report", "trust_test_report_history", "unzip", "urllist", "urllist_ping_verbose", "urllist_ping_external", "use_prompt_default", "use_sqlite", "username", "version_timeout", "wait_list", "wget", "yaml_load_code", "yaml_module", ); my %prefssupport = map { $_ => 1 } ( "allow_installing_module_downgrades", "allow_installing_outdated_dists", "build_requires_install_policy", "check_sigs", "make", "make_install_make_command", "prefer_installer", "test_report", ); # returns true on successful action sub edit { my($self,@args) = @_; return unless @args; CPAN->debug("self[$self]args[".join(" | ",@args)."]"); my($o,$str,$func,$args,$key_exists); $o = shift @args; if($can{$o}) { my $success = $self->$o(args => \@args); # o conf init => sub init => sub load unless ($success) { die "Panic: could not configure CPAN.pm for args [@args]. Giving up."; } } else { CPAN->debug("o[$o]") if $CPAN::DEBUG; unless (exists $keys{$o}) { $CPAN::Frontend->mywarn("Warning: unknown configuration variable '$o'\n"); } require_myconfig_or_config(); my $changed; # one day I used randomize_urllist for a boolean, so we must # list them explicitly --ak if (0) { } elsif ($o =~ /^(wait_list|urllist|dontload_list|plugin_list)$/) { # # ARRAYS # $func = shift @args; $func ||= ""; CPAN->debug("func[$func]args[@args]") if $CPAN::DEBUG; # Let's avoid eval, it's easier to comprehend without. if ($func eq "push") { push @{$CPAN::Config->{$o}}, @args; $changed = 1; } elsif ($func eq "pop") { pop @{$CPAN::Config->{$o}}; $changed = 1; } elsif ($func eq "shift") { shift @{$CPAN::Config->{$o}}; $changed = 1; } elsif ($func eq "unshift") { unshift @{$CPAN::Config->{$o}}, @args; $changed = 1; } elsif ($func eq "splice") { my $offset = shift @args || 0; my $length = shift @args || 0; splice @{$CPAN::Config->{$o}}, $offset, $length, @args; # may warn $changed = 1; } elsif ($func) { $CPAN::Config->{$o} = [$func, @args]; $changed = 1; } else { $self->prettyprint($o); } if ($changed) { if ($o eq "urllist") { # reset the cached values undef $CPAN::FTP::Thesite; undef $CPAN::FTP::Themethod; $CPAN::Index::LAST_TIME = 0; } elsif ($o eq "dontload_list") { # empty it, it will be built up again $CPAN::META->{dontload_hash} = {}; } } } elsif ($o =~ /_hash$/) { # # HASHES # if (@args==1 && $args[0] eq "") { @args = (); } elsif (@args % 2) { push @args, ""; } $CPAN::Config->{$o} = { @args }; $changed = 1; } else { # # SCALARS # if (defined $args[0]) { $CPAN::CONFIG_DIRTY = 1; $CPAN::Config->{$o} = $args[0]; $changed = 1; } $self->prettyprint($o) if exists $keys{$o} or defined $CPAN::Config->{$o}; } if ($changed) { if ($CPAN::Config->{auto_commit}) { $self->commit; } else { $CPAN::CONFIG_DIRTY = 1; $CPAN::Frontend->myprint("Please use 'o conf commit' to ". "make the config permanent!\n\n"); } } } } sub prettyprint { my($self,$k) = @_; my $v = $CPAN::Config->{$k}; if (ref $v) { my(@report); if (ref $v eq "ARRAY") { @report = map {"\t$_ \[$v->[$_]]\n"} 0..$#$v; } else { @report = map { sprintf "\t%-18s => %s\n", "[$_]", defined $v->{$_} ? "[$v->{$_}]" : "undef" } sort keys %$v; } $CPAN::Frontend->myprint( join( "", sprintf( " %-18s\n", $k ), @report ) ); } elsif (defined $v) { $CPAN::Frontend->myprint(sprintf " %-18s [%s]\n", $k, $v); } else { $CPAN::Frontend->myprint(sprintf " %-18s undef\n", $k); } } # generally, this should be called without arguments so that the currently # loaded config file is where changes are committed. sub commit { my($self,@args) = @_; CPAN->debug("args[@args]") if $CPAN::DEBUG; if ($CPAN::RUN_DEGRADED) { $CPAN::Frontend->mydie( "'o conf commit' disabled in ". "degraded mode. Maybe try\n". " !undef \$CPAN::RUN_DEGRADED\n" ); } my ($configpm, $must_reload); # XXX does anything do this? can it be simplified? -- dagolden, 2011-01-19 if (@args) { if ($args[0] eq "args") { # we have not signed that contract } else { $configpm = $args[0]; } } # use provided name or the current config or create a new MyConfig $configpm ||= require_myconfig_or_config() || make_new_config(); # commit to MyConfig if we can't write to Config if ( ! -w $configpm && $configpm =~ m{CPAN/Config\.pm} ) { my $myconfig = _new_config_name(); $CPAN::Frontend->mywarn( "Your $configpm file\n". "is not writable. I will attempt to write your configuration to\n" . "$myconfig instead.\n\n" ); $configpm = make_new_config(); $must_reload++; # so it gets loaded as $INC{'CPAN/MyConfig.pm'} } # XXX why not just "-w $configpm"? -- dagolden, 2011-01-19 my($mode); if (-f $configpm) { $mode = (stat $configpm)[2]; if ($mode && ! -w _) { _die_cant_write_config($configpm); } } $self->_write_config_file($configpm); require_myconfig_or_config() if $must_reload; #$mode = 0444 | ( $mode & 0111 ? 0111 : 0 ); #chmod $mode, $configpm; ###why was that so? $self->defaults; $CPAN::Frontend->myprint("commit: wrote '$configpm'\n"); $CPAN::CONFIG_DIRTY = 0; 1; } sub _write_config_file { my ($self, $configpm) = @_; my $msg; $msg = <<EOF if $configpm =~ m{CPAN/Config\.pm}; # This is CPAN.pm's systemwide configuration file. This file provides # defaults for users, and the values can be changed in a per-user # configuration file. EOF $msg ||= "\n"; my($fh) = FileHandle->new; rename $configpm, "$configpm~" if -f $configpm; open $fh, ">$configpm" or $CPAN::Frontend->mydie("Couldn't open >$configpm: $!"); $fh->print(qq[$msg\$CPAN::Config = \{\n]); foreach (sort keys %$CPAN::Config) { unless (exists $keys{$_}) { # do not drop them: forward compatibility! $CPAN::Frontend->mywarn("Unknown config variable '$_'\n"); next; } $fh->print( " '$_' => ", $self->neatvalue($CPAN::Config->{$_}), ",\n" ); } $fh->print("};\n1;\n__END__\n"); close $fh; return; } # stolen from MakeMaker; not taking the original because it is buggy; # bugreport will have to say: keys of hashes remain unquoted and can # produce syntax errors sub neatvalue { my($self, $v) = @_; return "undef" unless defined $v; my($t) = ref $v; unless ($t) { $v =~ s/\\/\\\\/g; return "q[$v]"; } if ($t eq 'ARRAY') { my(@m, @neat); push @m, "["; foreach my $elem (@$v) { push @neat, "q[$elem]"; } push @m, join ", ", @neat; push @m, "]"; return join "", @m; } return "$v" unless $t eq 'HASH'; my @m; foreach my $key (sort keys %$v) { my $val = $v->{$key}; push(@m,"q[$key]=>".$self->neatvalue($val)) ; } return "{ ".join(', ',@m)." }"; } sub defaults { my($self) = @_; if ($CPAN::RUN_DEGRADED) { $CPAN::Frontend->mydie( "'o conf defaults' disabled in ". "degraded mode. Maybe try\n". " !undef \$CPAN::RUN_DEGRADED\n" ); } my $done; for my $config (qw(CPAN/MyConfig.pm CPAN/Config.pm)) { if ($INC{$config}) { CPAN->debug("INC{'$config'}[$INC{$config}]") if $CPAN::DEBUG; CPAN::Shell->_reload_this($config,{reloforce => 1}); $CPAN::Frontend->myprint("'$INC{$config}' reread\n"); last; } } $CPAN::CONFIG_DIRTY = 0; 1; } =head2 C<< CLASS->safe_quote ITEM >> Quotes an item to become safe against spaces in shell interpolation. An item is enclosed in double quotes if: - the item contains spaces in the middle - the item does not start with a quote This happens to avoid shell interpolation problems when whitespace is present in directory names. This method uses C<commands_quote> to determine the correct quote. If C<commands_quote> is a space, no quoting will take place. if it starts and ends with the same quote character: leave it as it is if it contains no whitespace: leave it as it is if it contains whitespace, then if it contains quotes: better leave it as it is else: quote it with the correct quote type for the box we're on =cut { # Instead of patching the guess, set commands_quote # to the right value my ($quotes,$use_quote) = $^O eq 'MSWin32' ? ('"', '"') : (q{"'}, "'") ; sub safe_quote { my ($self, $command) = @_; # Set up quote/default quote my $quote = $CPAN::Config->{commands_quote} || $quotes; if ($quote ne ' ' and defined($command ) and $command =~ /\s/ and $command !~ /[$quote]/) { return qq<$use_quote$command$use_quote> } return $command; } } sub init { my($self,@args) = @_; CPAN->debug("self[$self]args[".join(",",@args)."]"); $self->load(do_init => 1, @args); 1; } # Loads CPAN::MyConfig or fall-back to CPAN::Config. Will not reload a file # if already loaded. Returns the path to the file %INC or else the empty string # # Note -- if CPAN::Config were loaded and CPAN::MyConfig subsequently # created, calling this again will leave *both* in %INC sub require_myconfig_or_config () { if ( $INC{"CPAN/MyConfig.pm"} || _try_loading("CPAN::MyConfig", cpan_home())) { return $INC{"CPAN/MyConfig.pm"}; } elsif ( $INC{"CPAN/Config.pm"} || _try_loading("CPAN::Config") ) { return $INC{"CPAN/Config.pm"}; } else { return q{}; } } # Load a module, but ignore "can't locate..." errors # Optionally take a list of directories to add to @INC for the load sub _try_loading { my ($module, @dirs) = @_; (my $file = $module) =~ s{::}{/}g; $file .= ".pm"; local @INC = @INC; for my $dir ( @dirs ) { if ( -f File::Spec->catfile($dir, $file) ) { unshift @INC, $dir; last; } } eval { require $file }; my $err_myconfig = $@; if ($err_myconfig and $err_myconfig !~ m#locate \Q$file\E#) { die "Error while requiring ${module}:\n$err_myconfig"; } return $INC{$file}; } # prioritized list of possible places for finding "CPAN/MyConfig.pm" sub cpan_home_dir_candidates { my @dirs; my $old_v = $CPAN::Config->{load_module_verbosity}; $CPAN::Config->{load_module_verbosity} = q[none]; if ($CPAN::META->has_usable('File::HomeDir')) { if ($^O ne 'darwin') { push @dirs, File::HomeDir->my_data; # my_data is ~/Library/Application Support on darwin, # which causes issues in the toolchain. } push @dirs, File::HomeDir->my_home; } # Windows might not have HOME, so check it first push @dirs, $ENV{HOME} if $ENV{HOME}; # Windows might have these instead push( @dirs, File::Spec->catpath($ENV{HOMEDRIVE}, $ENV{HOMEPATH}, '') ) if $ENV{HOMEDRIVE} && $ENV{HOMEPATH}; push @dirs, $ENV{USERPROFILE} if $ENV{USERPROFILE}; $CPAN::Config->{load_module_verbosity} = $old_v; my $dotcpan = $^O eq 'VMS' ? '_cpan' : '.cpan'; @dirs = map { File::Spec->catdir($_, $dotcpan) } grep { defined } @dirs; return wantarray ? @dirs : $dirs[0]; } sub load { my($self, %args) = @_; $CPAN::Be_Silent+=0; # protect against 'used only once' $CPAN::Be_Silent++ if $args{be_silent}; # do not use; planned to be removed in 2011 my $do_init = delete $args{do_init} || 0; my $make_myconfig = delete $args{make_myconfig}; $loading = 0 unless defined $loading; my $configpm = require_myconfig_or_config; my @miss = $self->missing_config_data; CPAN->debug("do_init[$do_init]loading[$loading]miss[@miss]") if $CPAN::DEBUG; return unless $do_init || @miss; if (@miss==1 and $miss[0] eq "pushy_https" && !$do_init) { $CPAN::Frontend->myprint(<<'END'); Starting with version 2.29 of the cpan shell, a new download mechanism is the default which exclusively uses cpan.org as the host to download from. The configuration variable pushy_https can be used to (de)select the new mechanism. Please read more about it and make your choice between the old and the new mechanism by running o conf init pushy_https Once you have done that and stored the config variable this dialog will disappear. END return; } # I'm not how we'd ever wind up in a recursive loop, but I'm leaving # this here for safety's sake -- dagolden, 2011-01-19 return if $loading; local $loading = ($loading||0) + 1; # Warn if we have a config file, but things were found missing if ($configpm && @miss && !$do_init) { if ($make_myconfig || ( ! -w $configpm && $configpm =~ m{CPAN/Config\.pm})) { $configpm = make_new_config(); $CPAN::Frontend->myprint(<<END); The system CPAN configuration file has provided some default values, but you need to complete the configuration dialog for CPAN.pm. Configuration will be written to <<$configpm>> END } else { $CPAN::Frontend->myprint(<<END); Sorry, we have to rerun the configuration dialog for CPAN.pm due to some missing parameters. Configuration will be written to <<$configpm>> END } } require CPAN::FirstTime; return CPAN::FirstTime::init($configpm || make_new_config(), %args); } # Creates a new, empty config file at the preferred location # Any existing will be renamed with a ".bak" suffix if possible # If the file cannot be created, an exception is thrown sub make_new_config { my $configpm = _new_config_name(); my $configpmdir = File::Basename::dirname( $configpm ); File::Path::mkpath($configpmdir) unless -d $configpmdir; if ( -w $configpmdir ) { #_#_# following code dumped core on me with 5.003_11, a.k. if( -f $configpm ) { my $configpm_bak = "$configpm.bak"; unlink $configpm_bak if -f $configpm_bak; if( rename $configpm, $configpm_bak ) { $CPAN::Frontend->mywarn(<<END); Old configuration file $configpm moved to $configpm_bak END } } my $fh = FileHandle->new; if ($fh->open(">$configpm")) { $fh->print("1;\n"); return $configpm; } } _die_cant_write_config($configpm); } sub _die_cant_write_config { my ($configpm) = @_; $CPAN::Frontend->mydie(<<"END"); WARNING: CPAN.pm is unable to write a configuration file. You must be able to create and write to '$configpm'. Aborting configuration. END } # From candidate directories, we would like (in descending preference order): # * the one that contains a MyConfig file # * one that exists (even without MyConfig) # * the first one on the list sub cpan_home { my @dirs = cpan_home_dir_candidates(); for my $d (@dirs) { return $d if -f "$d/CPAN/MyConfig.pm"; } for my $d (@dirs) { return $d if -d $d; } return $dirs[0]; } sub _new_config_name { return File::Spec->catfile(cpan_home(), 'CPAN', 'MyConfig.pm'); } # returns mandatory but missing entries in the Config sub missing_config_data { my(@miss); for ( "auto_commit", "build_cache", "build_dir", "cache_metadata", "cpan_home", "ftp_proxy", #"gzip", "http_proxy", "index_expire", #"inhibit_startup_message", "keep_source_where", #"make", "make_arg", "make_install_arg", "makepl_arg", "mbuild_arg", "mbuild_install_arg", ($^O eq "MSWin32" ? "" : "mbuild_install_build_command"), "mbuildpl_arg", "no_proxy", #"pager", "prerequisites_policy", "pushy_https", "scan_cache", #"tar", #"unzip", "urllist", ) { next unless exists $keys{$_}; push @miss, $_ unless defined $CPAN::Config->{$_}; } return @miss; } sub help { $CPAN::Frontend->myprint(q[ Known options: commit commit session changes to disk defaults reload default config values from disk help this help init enter a dialog to set all or a set of parameters Edit key values as in the following (the "o" is a literal letter o): o conf build_cache 15 o conf build_dir "/foo/bar" o conf urllist shift o conf urllist unshift ftp://ftp.foo.bar/ o conf inhibit_startup_message 1 ]); 1; #don't reprint CPAN::Config } sub cpl { my($word,$line,$pos) = @_; $word ||= ""; CPAN->debug("word[$word] line[$line] pos[$pos]") if $CPAN::DEBUG; my(@words) = split " ", substr($line,0,$pos+1); if ( defined($words[2]) and $words[2] =~ /list$/ and ( @words == 3 || @words == 4 && length($word) ) ) { return grep /^\Q$word\E/, qw(splice shift unshift pop push); } elsif (defined($words[2]) and $words[2] eq "init" and ( @words == 3 || @words >= 4 && length($word) )) { return sort grep /^\Q$word\E/, keys %keys; } elsif (@words >= 4) { return (); } my %seen; my(@o_conf) = sort grep { !$seen{$_}++ } keys %can, keys %$CPAN::Config, keys %keys; return grep /^\Q$word\E/, @o_conf; } sub prefs_lookup { my($self,$distro,$what) = @_; if ($prefssupport{$what}) { return $CPAN::Config->{$what} unless $distro and $distro->prefs and $distro->prefs->{cpanconfig} and defined $distro->prefs->{cpanconfig}{$what}; return $distro->prefs->{cpanconfig}{$what}; } else { $CPAN::Frontend->mywarn("Warning: $what not yet officially ". "supported for distroprefs, doing a normal lookup\n"); return $CPAN::Config->{$what}; } } { package CPAN::Config; ####::###### #hide from indexer # note: J. Nick Koston wrote me that they are using # CPAN::Config->commit although undocumented. I suggested # CPAN::Shell->o("conf","commit") even when ugly it is at least # documented # that's why I added the CPAN::Config class with autoload and # deprecated warning use strict; use vars qw($AUTOLOAD $VERSION); $VERSION = "5.5013"; # formerly CPAN::HandleConfig was known as CPAN::Config sub AUTOLOAD { ## no critic my $class = shift; # e.g. in dh-make-perl: CPAN::Config my($l) = $AUTOLOAD; $CPAN::Frontend->mywarn("Dispatching deprecated method '$l' to CPAN::HandleConfig\n"); $l =~ s/.*:://; CPAN::HandleConfig->$l(@_); } } 1; __END__ =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut # Local Variables: # mode: cperl # cperl-indent-level: 4 # End: # vim: ts=4 sts=4 sw=4: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Version.pm�������������������������������������������������������������������������������������0000444�����������������00000010456�15111204742�0007251 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package CPAN::Version; use strict; use vars qw($VERSION); $VERSION = "5.5003"; # CPAN::Version::vcmp courtesy Jost Krieger sub vcmp { my($self,$l,$r) = @_; local($^W) = 0; CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; # treat undef as zero $l = 0 if $l eq 'undef'; $r = 0 if $r eq 'undef'; return 0 if $l eq $r; # short circuit for quicker success for ($l,$r) { s/_//g; } CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; for ($l,$r) { next unless tr/.// > 1 || /^v/; s/^v?/v/; 1 while s/\.0+(\d)/.$1/; # remove leading zeroes per group } CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; if ($l=~/^v/ <=> $r=~/^v/) { for ($l,$r) { next if /^v/; $_ = $self->float2vv($_); } } CPAN->debug("l[$l] r[$r]") if $CPAN::DEBUG; my $lvstring = "v0"; my $rvstring = "v0"; if ($] >= 5.006 && $l =~ /^v/ && $r =~ /^v/) { $lvstring = $self->vstring($l); $rvstring = $self->vstring($r); CPAN->debug(sprintf "lv[%vd] rv[%vd]", $lvstring, $rvstring) if $CPAN::DEBUG; } return ( ($l ne "undef") <=> ($r ne "undef") || $lvstring cmp $rvstring || $l <=> $r || $l cmp $r ); } sub vgt { my($self,$l,$r) = @_; $self->vcmp($l,$r) > 0; } sub vlt { my($self,$l,$r) = @_; $self->vcmp($l,$r) < 0; } sub vge { my($self,$l,$r) = @_; $self->vcmp($l,$r) >= 0; } sub vle { my($self,$l,$r) = @_; $self->vcmp($l,$r) <= 0; } sub vstring { my($self,$n) = @_; $n =~ s/^v// or die "CPAN::Version::vstring() called with invalid arg [$n]"; pack "U*", split /\./, $n; } # vv => visible vstring sub float2vv { my($self,$n) = @_; my($rev) = int($n); $rev ||= 0; my($mantissa) = $n =~ /\.(\d{1,12})/; # limit to 12 digits to limit # architecture influence $mantissa ||= 0; $mantissa .= "0" while length($mantissa)%3; my $ret = "v" . $rev; while ($mantissa) { $mantissa =~ s/(\d{1,3})// or die "Panic: length>0 but not a digit? mantissa[$mantissa]"; $ret .= ".".int($1); } # warn "n[$n]ret[$ret]"; $ret =~ s/(\.0)+/.0/; # v1.0.0 => v1.0 $ret; } sub readable { my($self,$n) = @_; $n =~ /^([\w\-\+\.]+)/; return $1 if defined $1 && length($1)>0; # if the first user reaches version v43, he will be treated as "+". # We'll have to decide about a new rule here then, depending on what # will be the prevailing versioning behavior then. if ($] < 5.006) { # or whenever v-strings were introduced # we get them wrong anyway, whatever we do, because 5.005 will # have already interpreted 0.2.4 to be "0.24". So even if he # indexer sends us something like "v0.2.4" we compare wrongly. # And if they say v1.2, then the old perl takes it as "v12" if (defined $CPAN::Frontend) { $CPAN::Frontend->mywarn("Suspicious version string seen [$n]\n"); } else { warn("Suspicious version string seen [$n]\n"); } return $n; } my $better = sprintf "v%vd", $n; CPAN->debug("n[$n] better[$better]") if $CPAN::DEBUG; return $better; } 1; __END__ =head1 NAME CPAN::Version - utility functions to compare CPAN versions =head1 SYNOPSIS use CPAN::Version; CPAN::Version->vgt("1.1","1.1.1"); # 1 bc. 1.1 > 1.001001 CPAN::Version->vlt("1.1","1.1"); # 0 bc. 1.1 not < 1.1 CPAN::Version->vcmp("1.1","1.1.1"); # 1 bc. first is larger CPAN::Version->vcmp("1.1.1","1.1"); # -1 bc. first is smaller CPAN::Version->readable(v1.2.3); # "v1.2.3" CPAN::Version->vstring("v1.2.3"); # v1.2.3 CPAN::Version->float2vv(1.002003); # "v1.2.3" =head1 DESCRIPTION This module mediates between some version that perl sees in a package and the version that is published by the CPAN indexer. It's only written as a helper module for both CPAN.pm and CPANPLUS.pm. As it stands it predates version.pm but has the same goal: make version strings visible and comparable. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut # Local Variables: # mode: cperl # cperl-indent-level: 4 # End: ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/FTP/netrc.pm�����������������������������������������������������������������������������������0000444�����������������00000003032�15111204742�0007360 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package CPAN::FTP::netrc; use strict; $CPAN::FTP::netrc::VERSION = $CPAN::FTP::netrc::VERSION = "1.01"; # package CPAN::FTP::netrc; sub new { my($class) = @_; my $file = File::Spec->catfile($ENV{HOME},".netrc"); my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($file); $mode ||= 0; my $protected = 0; my($fh,@machines,$hasdefault); $hasdefault = 0; $fh = FileHandle->new or die "Could not create a filehandle"; if($fh->open($file)) { $protected = ($mode & 077) == 0; local($/) = ""; NETRC: while (<$fh>) { my(@tokens) = split " ", $_; TOKEN: while (@tokens) { my($t) = shift @tokens; if ($t eq "default") { $hasdefault++; last NETRC; } last TOKEN if $t eq "macdef"; if ($t eq "machine") { push @machines, shift @tokens; } } } } else { $file = $hasdefault = $protected = ""; } bless { 'mach' => [@machines], 'netrc' => $file, 'hasdefault' => $hasdefault, 'protected' => $protected, }, $class; } # CPAN::FTP::netrc::hasdefault; sub hasdefault { shift->{'hasdefault'} } sub netrc { shift->{'netrc'} } sub protected { shift->{'protected'} } sub contains { my($self,$mach) = @_; for ( @{$self->{'mach'}} ) { return 1 if $_ eq $mach; } return 0; } 1; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/Kwalify.pm�������������������������������������������������������������������������������������0000444�����������������00000006544�15111204742�0007235 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=head1 NAME CPAN::Kwalify - Interface between CPAN.pm and Kwalify.pm =head1 SYNOPSIS use CPAN::Kwalify; validate($schema_name, $data, $file, $doc); =head1 DESCRIPTION =over =item _validate($schema_name, $data, $file, $doc) $schema_name is the name of a supported schema. Currently only C<distroprefs> is supported. $data is the data to be validated. $file is the absolute path to the file the data are coming from. $doc is the index of the document within $doc that is to be validated. The last two arguments are only there for better error reporting. Relies on being called from within CPAN.pm. Dies if something fails. Does not return anything useful. =item yaml($schema_name) Returns the YAML text of that schema. Dies if something fails. =back =head1 AUTHOR Andreas Koenig C<< <andk@cpan.org> >> =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L<http://www.perl.com/perl/misc/Artistic.html> =cut use strict; package CPAN::Kwalify; use vars qw($VERSION $VAR1); $VERSION = "5.50"; use File::Spec (); my %vcache = (); my $schema_loaded = {}; sub _validate { my($schema_name,$data,$abs,$y) = @_; my $yaml_module = CPAN->_yaml_module; if ( $CPAN::META->has_inst($yaml_module) && $CPAN::META->has_inst("Kwalify") ) { my $load = UNIVERSAL::can($yaml_module,"Load"); unless ($schema_loaded->{$schema_name}) { eval { my $schema_yaml = yaml($schema_name); $schema_loaded->{$schema_name} = $load->($schema_yaml); }; if ($@) { # we know that YAML.pm 0.62 cannot parse the schema, # so we try a fallback my $content = do { my $path = __FILE__; $path =~ s/\.pm$//; $path = File::Spec->catfile($path, "$schema_name.dd"); local *FH; open FH, $path or die "Could not open '$path': $!"; local $/; <FH>; }; $VAR1 = undef; eval $content; if (my $err = $@) { die "parsing of '$schema_name.dd' failed: $err"; } $schema_loaded->{$schema_name} = $VAR1; } } } if (my $schema = $schema_loaded->{$schema_name}) { my $mtime = (stat $abs)[9]; for my $k (keys %{$vcache{$abs}}) { delete $vcache{$abs}{$k} unless $k eq $mtime; } return if $vcache{$abs}{$mtime}{$y}++; eval { Kwalify::validate($schema, $data) }; if (my $err = $@) { my $info = {}; yaml($schema_name, info => $info); die "validation of distropref '$abs'[$y] against schema '$info->{path}' failed: $err"; } } } sub _clear_cache { %vcache = (); } sub yaml { my($schema_name, %opt) = @_; my $content = do { my $path = __FILE__; $path =~ s/\.pm$//; $path = File::Spec->catfile($path, "$schema_name.yml"); if ($opt{info}) { $opt{info}{path} = $path; } local *FH; open FH, $path or die "Could not open '$path': $!"; local $/; <FH>; }; return $content; } 1; # Local Variables: # mode: cperl # cperl-indent-level: 4 # End: ������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN/DeferredCode.pm��������������������������������������������������������������������������������0000444�����������������00000000275�15111204742�0010135 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package CPAN::DeferredCode; use strict; use vars qw/$VERSION/; use overload fallback => 1, map { ($_ => 'run') } qw/ bool "" 0+ /; $VERSION = "5.50"; sub run { $_[0]->(); } 1; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������App/Cpan.pm�����������������������������������������������������������������������������������������0000444�����������������00000117273�15111204742�0006511 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package App::Cpan; use strict; use warnings; use vars qw($VERSION); use if $] < 5.008 => 'IO::Scalar'; $VERSION = '1.678'; =head1 NAME App::Cpan - easily interact with CPAN from the command line =head1 SYNOPSIS # with arguments and no switches, installs specified modules cpan module_name [ module_name ... ] # with switches, installs modules with extra behavior cpan [-cfFimtTw] module_name [ module_name ... ] # use local::lib cpan -I module_name [ module_name ... ] # one time mirror override for faster mirrors cpan -p ... # with just the dot, install from the distribution in the # current directory cpan . # without arguments, starts CPAN.pm shell cpan # without arguments, but some switches cpan [-ahpruvACDLOPX] =head1 DESCRIPTION This script provides a command interface (not a shell) to CPAN. At the moment it uses CPAN.pm to do the work, but it is not a one-shot command runner for CPAN.pm. =head2 Options =over 4 =item -a Creates a CPAN.pm autobundle with CPAN::Shell->autobundle. =item -A module [ module ... ] Shows the primary maintainers for the specified modules. =item -c module Runs a `make clean` in the specified module's directories. =item -C module [ module ... ] Show the F<Changes> files for the specified modules =item -D module [ module ... ] Show the module details. This prints one line for each out-of-date module (meaning, modules locally installed but have newer versions on CPAN). Each line has three columns: module name, local version, and CPAN version. =item -f Force the specified action, when it normally would have failed. Use this to install a module even if its tests fail. When you use this option, -i is not optional for installing a module when you need to force it: % cpan -f -i Module::Foo =item -F Turn off CPAN.pm's attempts to lock anything. You should be careful with this since you might end up with multiple scripts trying to muck in the same directory. This isn't so much of a concern if you're loading a special config with C<-j>, and that config sets up its own work directories. =item -g module [ module ... ] Downloads to the current directory the latest distribution of the module. =item -G module [ module ... ] UNIMPLEMENTED Download to the current directory the latest distribution of the modules, unpack each distribution, and create a git repository for each distribution. If you want this feature, check out Yanick Champoux's C<Git::CPAN::Patch> distribution. =item -h Print a help message and exit. When you specify C<-h>, it ignores all of the other options and arguments. =item -i module [ module ... ] Install the specified modules. With no other switches, this switch is implied. =item -I Load C<local::lib> (think like C<-I> for loading lib paths). Too bad C<-l> was already taken. =item -j Config.pm Load the file that has the CPAN configuration data. This should have the same format as the standard F<CPAN/Config.pm> file, which defines C<$CPAN::Config> as an anonymous hash. If the file does not exist, C<cpan> dies. =item -J Dump the configuration in the same format that CPAN.pm uses. This is useful for checking the configuration as well as using the dump as a starting point for a new, custom configuration. =item -l List all installed modules with their versions =item -L author [ author ... ] List the modules by the specified authors. =item -m Make the specified modules. =item -M mirror1,mirror2,... A comma-separated list of mirrors to use for just this run. The C<-P> option can find them for you automatically. =item -n Do a dry run, but don't actually install anything. (unimplemented) =item -O Show the out-of-date modules. =item -p Ping the configured mirrors and print a report =item -P Find the best mirrors you could be using and use them for the current session. =item -r Recompiles dynamically loaded modules with CPAN::Shell->recompile. =item -s Drop in the CPAN.pm shell. This command does this automatically if you don't specify any arguments. =item -t module [ module ... ] Run a `make test` on the specified modules. =item -T Do not test modules. Simply install them. =item -u Upgrade all installed modules. Blindly doing this can really break things, so keep a backup. =item -v Print the script version and CPAN.pm version then exit. =item -V Print detailed information about the cpan client. =item -w UNIMPLEMENTED Turn on cpan warnings. This checks various things, like directory permissions, and tells you about problems you might have. =item -x module [ module ... ] Find close matches to the named modules that you think you might have mistyped. This requires the optional installation of Text::Levenshtein or Text::Levenshtein::Damerau. =item -X Dump all the namespaces to standard output. =back =head2 Examples # print a help message cpan -h # print the version numbers cpan -v # create an autobundle cpan -a # recompile modules cpan -r # upgrade all installed modules cpan -u # install modules ( sole -i is optional ) cpan -i Netscape::Booksmarks Business::ISBN # force install modules ( must use -i ) cpan -fi CGI::Minimal URI # install modules but without testing them cpan -Ti CGI::Minimal URI =head2 Environment variables There are several components in CPAN.pm that use environment variables. The build tools, L<ExtUtils::MakeMaker> and L<Module::Build> use some, while others matter to the levels above them. Some of these are specified by the Perl Toolchain Gang: Lancaster Consensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md> Oslo Consensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/oslo-consensus.md> =over 4 =item NONINTERACTIVE_TESTING Assume no one is paying attention and skips prompts for distributions that do that correctly. C<cpan(1)> sets this to C<1> unless it already has a value (even if that value is false). =item PERL_MM_USE_DEFAULT Use the default answer for a prompted questions. C<cpan(1)> sets this to C<1> unless it already has a value (even if that value is false). =item CPAN_OPTS As with C<PERL5OPT>, a string of additional C<cpan(1)> options to add to those you specify on the command line. =item CPANSCRIPT_LOGLEVEL The log level to use, with either the embedded, minimal logger or L<Log::Log4perl> if it is installed. Possible values are the same as the C<Log::Log4perl> levels: C<TRACE>, C<DEBUG>, C<INFO>, C<WARN>, C<ERROR>, and C<FATAL>. The default is C<INFO>. =item GIT_COMMAND The path to the C<git> binary to use for the Git features. The default is C</usr/local/bin/git>. =back =head2 Methods =over 4 =cut use autouse Carp => qw(carp croak cluck); use CPAN 1.80 (); # needs no test use Config; use autouse Cwd => qw(cwd); use autouse 'Data::Dumper' => qw(Dumper); use File::Spec::Functions qw(catfile file_name_is_absolute rel2abs); use File::Basename; use Getopt::Std; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Internal constants use constant TRUE => 1; use constant FALSE => 0; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # The return values use constant HEY_IT_WORKED => 0; use constant I_DONT_KNOW_WHAT_HAPPENED => 1; # 0b0000_0001 use constant ITS_NOT_MY_FAULT => 2; use constant THE_PROGRAMMERS_AN_IDIOT => 4; use constant A_MODULE_FAILED_TO_INSTALL => 8; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # set up the order of options that we layer over CPAN::Shell BEGIN { # most of this should be in methods use vars qw( @META_OPTIONS $Default %CPAN_METHODS @CPAN_OPTIONS @option_order %Method_table %Method_table_index ); @META_OPTIONS = qw( h v V I g G M: C A D O l L a r p P j: J w x X ); $Default = 'default'; %CPAN_METHODS = ( # map switches to method names in CPAN::Shell $Default => 'install', 'c' => 'clean', 'f' => 'force', 'i' => 'install', 'm' => 'make', 't' => 'test', 'u' => 'upgrade', 'T' => 'notest', 's' => 'shell', ); @CPAN_OPTIONS = grep { $_ ne $Default } sort keys %CPAN_METHODS; @option_order = ( @META_OPTIONS, @CPAN_OPTIONS ); # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # map switches to the subroutines in this script, along with other information. # use this stuff instead of hard-coded indices and values sub NO_ARGS () { 0 } sub ARGS () { 1 } sub GOOD_EXIT () { 0 } %Method_table = ( # key => [ sub ref, takes args?, exit value, description ] # options that do their thing first, then exit h => [ \&_print_help, NO_ARGS, GOOD_EXIT, 'Printing help' ], v => [ \&_print_version, NO_ARGS, GOOD_EXIT, 'Printing version' ], V => [ \&_print_details, NO_ARGS, GOOD_EXIT, 'Printing detailed version' ], X => [ \&_list_all_namespaces, NO_ARGS, GOOD_EXIT, 'Listing all namespaces' ], # options that affect other options j => [ \&_load_config, ARGS, GOOD_EXIT, 'Use specified config file' ], J => [ \&_dump_config, NO_ARGS, GOOD_EXIT, 'Dump configuration to stdout' ], F => [ \&_lock_lobotomy, NO_ARGS, GOOD_EXIT, 'Turn off CPAN.pm lock files' ], I => [ \&_load_local_lib, NO_ARGS, GOOD_EXIT, 'Loading local::lib' ], M => [ \&_use_these_mirrors, ARGS, GOOD_EXIT, 'Setting per session mirrors' ], P => [ \&_find_good_mirrors, NO_ARGS, GOOD_EXIT, 'Finding good mirrors' ], w => [ \&_turn_on_warnings, NO_ARGS, GOOD_EXIT, 'Turning on warnings' ], # options that do their one thing g => [ \&_download, ARGS, GOOD_EXIT, 'Download the latest distro' ], G => [ \&_gitify, ARGS, GOOD_EXIT, 'Down and gitify the latest distro' ], C => [ \&_show_Changes, ARGS, GOOD_EXIT, 'Showing Changes file' ], A => [ \&_show_Author, ARGS, GOOD_EXIT, 'Showing Author' ], D => [ \&_show_Details, ARGS, GOOD_EXIT, 'Showing Details' ], O => [ \&_show_out_of_date, NO_ARGS, GOOD_EXIT, 'Showing Out of date' ], l => [ \&_list_all_mods, NO_ARGS, GOOD_EXIT, 'Listing all modules' ], L => [ \&_show_author_mods, ARGS, GOOD_EXIT, 'Showing author mods' ], a => [ \&_create_autobundle, NO_ARGS, GOOD_EXIT, 'Creating autobundle' ], p => [ \&_ping_mirrors, NO_ARGS, GOOD_EXIT, 'Pinging mirrors' ], r => [ \&_recompile, NO_ARGS, GOOD_EXIT, 'Recompiling' ], u => [ \&_upgrade, NO_ARGS, GOOD_EXIT, 'Running `make test`' ], 's' => [ \&_shell, NO_ARGS, GOOD_EXIT, 'Drop into the CPAN.pm shell' ], 'x' => [ \&_guess_namespace, ARGS, GOOD_EXIT, 'Guessing namespaces' ], c => [ \&_default, ARGS, GOOD_EXIT, 'Running `make clean`' ], f => [ \&_default, ARGS, GOOD_EXIT, 'Installing with force' ], i => [ \&_default, ARGS, GOOD_EXIT, 'Running `make install`' ], 'm' => [ \&_default, ARGS, GOOD_EXIT, 'Running `make`' ], t => [ \&_default, ARGS, GOOD_EXIT, 'Running `make test`' ], T => [ \&_default, ARGS, GOOD_EXIT, 'Installing with notest' ], ); %Method_table_index = ( code => 0, takes_args => 1, exit_value => 2, description => 3, ); } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # finally, do some argument processing sub _stupid_interface_hack_for_non_rtfmers { no warnings 'uninitialized'; shift @ARGV if( $ARGV[0] eq 'install' and @ARGV > 1 ) } sub _process_options { my %options; push @ARGV, grep $_, split /\s+/, $ENV{CPAN_OPTS} || ''; # if no arguments, just drop into the shell if( 0 == @ARGV ) { CPAN::shell(); exit 0 } elsif (Getopt::Std::getopts( join( '', @option_order ), \%options )) { \%options; } else { exit 1 } } sub _process_setup_options { my( $class, $options ) = @_; if( $options->{j} ) { $Method_table{j}[ $Method_table_index{code} ]->( $options->{j} ); delete $options->{j}; } elsif ( ! $options->{h} ) { # h "ignores all of the other options and arguments" # this is what CPAN.pm would do otherwise local $CPAN::Be_Silent = 1; CPAN::HandleConfig->load( # be_silent => 1, deprecated write_file => 0, ); } $class->_turn_off_testing if $options->{T}; foreach my $o ( qw(F I w P M) ) { next unless exists $options->{$o}; $Method_table{$o}[ $Method_table_index{code} ]->( $options->{$o} ); delete $options->{$o}; } if( $options->{o} ) { my @pairs = map { [ split /=/, $_, 2 ] } split /,/, $options->{o}; foreach my $pair ( @pairs ) { my( $setting, $value ) = @$pair; $CPAN::Config->{$setting} = $value; # $logger->debug( "Setting [$setting] to [$value]" ); } delete $options->{o}; } my $option_count = grep { $options->{$_} } @option_order; no warnings 'uninitialized'; # don't count options that imply installation foreach my $opt ( qw(f T) ) { # don't count force or notest $option_count -= $options->{$opt}; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if there are no options, set -i (this line fixes RT ticket 16915) $options->{i}++ unless $option_count; } sub _setup_environment { # should we override or set defaults? If this were a true interactive # session, we'd be in the CPAN shell. # https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md $ENV{NONINTERACTIVE_TESTING} = 1 unless defined $ENV{NONINTERACTIVE_TESTING}; $ENV{PERL_MM_USE_DEFAULT} = 1 unless defined $ENV{PERL_MM_USE_DEFAULT}; } =item run( ARGS ) Just do it. The C<run> method returns 0 on success and a positive number on failure. See the section on EXIT CODES for details on the values. =cut my $logger; sub run { my( $class, @args ) = @_; local @ARGV = @args; my $return_value = HEY_IT_WORKED; # assume that things will work $logger = $class->_init_logger; $logger->debug( "Using logger from @{[ref $logger]}" ); $class->_hook_into_CPANpm_report; $logger->debug( "Hooked into output" ); $class->_stupid_interface_hack_for_non_rtfmers; $logger->debug( "Patched cargo culting" ); my $options = $class->_process_options; $logger->debug( "Options are @{[Dumper($options)]}" ); $class->_process_setup_options( $options ); $class->_setup_environment( $options ); OPTION: foreach my $option ( @option_order ) { next unless $options->{$option}; my( $sub, $takes_args, $description ) = map { $Method_table{$option}[ $Method_table_index{$_} ] } qw( code takes_args description ); unless( ref $sub eq ref sub {} ) { $return_value = THE_PROGRAMMERS_AN_IDIOT; last OPTION; } $logger->info( "[$option] $description -- ignoring other arguments" ) if( @ARGV && ! $takes_args ); $return_value = $sub->( \ @ARGV, $options ); last; } return $return_value; } my $LEVEL; { package Local::Null::Logger; # hide from PAUSE my @LOGLEVELS = qw(TRACE DEBUG INFO WARN ERROR FATAL); $LEVEL = uc($ENV{CPANSCRIPT_LOGLEVEL} || 'INFO'); my %LL = map { $LOGLEVELS[$_] => $_ } 0..$#LOGLEVELS; unless (defined $LL{$LEVEL}){ warn "Unsupported loglevel '$LEVEL', setting to INFO"; $LEVEL = 'INFO'; } sub new { bless \ my $x, $_[0] } sub AUTOLOAD { my $autoload = our $AUTOLOAD; $autoload =~ s/.*://; return if $LL{uc $autoload} < $LL{$LEVEL}; $CPAN::Frontend->mywarn(">($autoload): $_\n") for split /[\r\n]+/, $_[1]; } sub DESTROY { 1 } } # load a module without searching the default entry for the current # directory sub _safe_load_module { my $name = shift; local @INC = @INC; pop @INC if $INC[-1] eq '.'; eval "require $name; 1"; } sub _init_logger { my $log4perl_loaded = _safe_load_module("Log::Log4perl"); unless( $log4perl_loaded ) { print STDOUT "Loading internal logger. Log::Log4perl recommended for better logging\n"; $logger = Local::Null::Logger->new; return $logger; } Log::Log4perl::init( \ <<"HERE" ); log4perl.rootLogger=$LEVEL, A1 log4perl.appender.A1=Log::Log4perl::Appender::Screen log4perl.appender.A1.layout=PatternLayout log4perl.appender.A1.layout.ConversionPattern=%m%n HERE $logger = Log::Log4perl->get_logger( 'App::Cpan' ); } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub _default { my( $args, $options ) = @_; my $switch = ''; # choose the option that we're going to use # we'll deal with 'f' (force) later, so skip it foreach my $option ( @CPAN_OPTIONS ) { next if ( $option eq 'f' or $option eq 'T' ); next unless $options->{$option}; $switch = $option; last; } # 1. with no switches, but arguments, use the default switch (install) # 2. with no switches and no args, start the shell # 3. With a switch but no args, die! These switches need arguments. if( not $switch and @$args ) { $switch = $Default; } elsif( not $switch and not @$args ) { return CPAN::shell() } elsif( $switch and not @$args ) { die "Nothing to $CPAN_METHODS{$switch}!\n"; } # Get and check the method from CPAN::Shell my $method = $CPAN_METHODS{$switch}; die "CPAN.pm cannot $method!\n" unless CPAN::Shell->can( $method ); # call the CPAN::Shell method, with force or notest if specified my $action = do { if( $options->{f} ) { sub { CPAN::Shell->force( $method, @_ ) } } elsif( $options->{T} ) { sub { CPAN::Shell->notest( $method, @_ ) } } else { sub { CPAN::Shell->$method( @_ ) } } }; # How do I handle exit codes for multiple arguments? my @errors = (); $options->{x} or _disable_guessers(); foreach my $arg ( @$args ) { # check the argument and perhaps capture typos my $module = _expand_module( $arg ) or do { $logger->error( "Skipping $arg because I couldn't find a matching namespace." ); next; }; _clear_cpanpm_output(); $action->( $arg ); my $error = _cpanpm_output_indicates_failure(); push @errors, $error if $error; } return do { if( @errors ) { $errors[0] } else { HEY_IT_WORKED } }; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =for comment CPAN.pm sends all the good stuff either to STDOUT, or to a temp file if $CPAN::Be_Silent is set. I have to intercept that output so I can find out what happened. =cut BEGIN { my $scalar = ''; sub _hook_into_CPANpm_report { no warnings 'redefine'; *CPAN::Shell::myprint = sub { my($self,$what) = @_; $scalar .= $what if defined $what; $self->print_ornamented($what, $CPAN::Config->{colorize_print}||'bold blue on_white', ); }; *CPAN::Shell::mywarn = sub { my($self,$what) = @_; $scalar .= $what if defined $what; $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white' ); }; } sub _clear_cpanpm_output { $scalar = '' } sub _get_cpanpm_output { $scalar } # These are lines I don't care about in CPAN.pm output. If I can # filter out the informational noise, I have a better chance to # catch the error signal my @skip_lines = ( qr/^\QWarning \(usually harmless\)/, qr/\bwill not store persistent state\b/, qr(//hint//), qr/^\s+reports\s+/, qr/^Try the command/, qr/^\s+$/, qr/^to find objects/, qr/^\s*Database was generated on/, qr/^Going to read/, qr|^\s+i\s+/|, # the i /Foo::Whatever/ line when it doesn't know ); sub _get_cpanpm_last_line { my $fh; if( $] < 5.008 ) { $fh = IO::Scalar->new( \ $scalar ); } else { eval q{ open $fh, '<', \\ $scalar; }; } my @lines = <$fh>; # This is a bit ugly. Once we examine a line, we have to # examine the line before it and go through all of the same # regexes. I could do something fancy, but this works. REGEXES: { foreach my $regex ( @skip_lines ) { if( $lines[-1] =~ m/$regex/ ) { pop @lines; redo REGEXES; # we have to go through all of them for every line! } } } $logger->debug( "Last interesting line of CPAN.pm output is:\n\t$lines[-1]" ); $lines[-1]; } } BEGIN { my $epic_fail_words = join '|', qw( Error stop(?:ping)? problems force not unsupported fail(?:ed)? Cannot\s+install ); sub _cpanpm_output_indicates_failure { my $last_line = _get_cpanpm_last_line(); my $result = $last_line =~ /\b(?:$epic_fail_words)\b/i; return A_MODULE_FAILED_TO_INSTALL if $last_line =~ /\b(?:Cannot\s+install)\b/i; $result || (); } } sub _cpanpm_output_indicates_success { my $last_line = _get_cpanpm_last_line(); my $result = $last_line =~ /\b(?:\s+-- OK|PASS)\b/; $result || (); } sub _cpanpm_output_is_vague { return FALSE if _cpanpm_output_indicates_failure() || _cpanpm_output_indicates_success(); return TRUE; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub _turn_on_warnings { carp "Warnings are implemented yet"; return HEY_IT_WORKED; } sub _turn_off_testing { $logger->debug( 'Trusting test report history' ); $CPAN::Config->{trust_test_report_history} = 1; return HEY_IT_WORKED; } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub _print_help { $logger->info( "Use perldoc to read the documentation" ); my $HAVE_PERLDOC = eval { require Pod::Perldoc; 1; }; if ($HAVE_PERLDOC) { system qq{"$^X" -e "require Pod::Perldoc; Pod::Perldoc->run()" $0}; exit; } else { warn "Please install Pod::Perldoc, maybe try 'cpan -i Pod::Perldoc'\n"; return HEY_IT_WORKED; } } sub _print_version # -v { $logger->info( "$0 script version $VERSION, CPAN.pm version " . CPAN->VERSION ); return HEY_IT_WORKED; } sub _print_details # -V { _print_version(); _check_install_dirs(); $logger->info( '-' x 50 . "\nChecking configured mirrors..." ); foreach my $mirror ( @{ $CPAN::Config->{urllist} } ) { _print_ping_report( $mirror ); } $logger->info( '-' x 50 . "\nChecking for faster mirrors..." ); { require CPAN::Mirrors; if ( $CPAN::Config->{connect_to_internet_ok} ) { $CPAN::Frontend->myprint(qq{Trying to fetch a mirror list from the Internet\n}); eval { CPAN::FTP->localize('MIRRORED.BY',File::Spec->catfile($CPAN::Config->{keep_source_where},'MIRRORED.BY'),3,1) } or $CPAN::Frontend->mywarn(<<'HERE'); We failed to get a copy of the mirror list from the Internet. You will need to provide CPAN mirror URLs yourself. HERE $CPAN::Frontend->myprint("\n"); } my $mirrors = CPAN::Mirrors->new( _mirror_file() ); my @continents = $mirrors->find_best_continents; my @mirrors = $mirrors->get_mirrors_by_continents( $continents[0] ); my @timings = $mirrors->get_mirrors_timings( \@mirrors ); foreach my $timing ( @timings ) { $logger->info( sprintf "%s (%0.2f ms)", $timing->hostname, $timing->rtt ); } } return HEY_IT_WORKED; } sub _check_install_dirs { my $makepl_arg = $CPAN::Config->{makepl_arg}; my $mbuildpl_arg = $CPAN::Config->{mbuildpl_arg}; my @custom_dirs; # PERL_MM_OPT push @custom_dirs, $makepl_arg =~ m/INSTALL_BASE\s*=\s*(\S+)/g, $mbuildpl_arg =~ m/--install_base\s*=\s*(\S+)/g; if( @custom_dirs ) { foreach my $dir ( @custom_dirs ) { _print_inc_dir_report( $dir ); } } # XXX: also need to check makepl_args, etc my @checks = ( [ 'core', [ grep $_, @Config{qw(installprivlib installarchlib)} ] ], [ 'vendor', [ grep $_, @Config{qw(installvendorlib installvendorarch)} ] ], [ 'site', [ grep $_, @Config{qw(installsitelib installsitearch)} ] ], [ 'PERL5LIB', _split_paths( $ENV{PERL5LIB} ) ], [ 'PERLLIB', _split_paths( $ENV{PERLLIB} ) ], ); $logger->info( '-' x 50 . "\nChecking install dirs..." ); foreach my $tuple ( @checks ) { my( $label ) = $tuple->[0]; $logger->info( "Checking $label" ); $logger->info( "\tno directories for $label" ) unless @{ $tuple->[1] }; foreach my $dir ( @{ $tuple->[1] } ) { _print_inc_dir_report( $dir ); } } } sub _split_paths { [ map { _expand_filename( $_ ) } split /$Config{path_sep}/, $_[0] || '' ]; } =pod Stolen from File::Path::Expand =cut sub _expand_filename { my( $path ) = @_; no warnings 'uninitialized'; $logger->debug( "Expanding path $path\n" ); $path =~ s{\A~([^/]+)?}{ _home_of( $1 || $> ) || "~$1" }e; return $path; } sub _home_of { require User::pwent; my( $user ) = @_; my $ent = User::pwent::getpw($user) or return; return $ent->dir; } sub _get_default_inc { require Config; [ @Config::Config{ _vars() }, '.' ]; } sub _vars { qw( installarchlib installprivlib installsitearch installsitelib ); } sub _ping_mirrors { my $urls = $CPAN::Config->{urllist}; require URI; foreach my $url ( @$urls ) { my( $obj ) = URI->new( $url ); next unless _is_pingable_scheme( $obj ); my $host = $obj->host; _print_ping_report( $obj ); } } sub _is_pingable_scheme { my( $uri ) = @_; $uri->scheme eq 'file' } sub _mirror_file { my $file = do { my $file = 'MIRRORED.BY'; my $local_path = File::Spec->catfile( $CPAN::Config->{keep_source_where}, $file ); if( -e $local_path ) { $local_path } else { require CPAN::FTP; CPAN::FTP->localize( $file, $local_path, 3, 1 ); $local_path; } }; } sub _find_good_mirrors { require CPAN::Mirrors; my $mirrors = CPAN::Mirrors->new( _mirror_file() ); my @mirrors = $mirrors->best_mirrors( how_many => 5, verbose => 1, ); foreach my $mirror ( @mirrors ) { next unless eval { $mirror->can( 'http' ) }; _print_ping_report( $mirror->http ); } $CPAN::Config->{urllist} = [ map { $_->http } @mirrors ]; } sub _print_inc_dir_report { my( $dir ) = shift; my $writeable = -w $dir ? '+' : '!!! (not writeable)'; $logger->info( "\t$writeable $dir" ); return -w $dir; } sub _print_ping_report { my( $mirror ) = @_; my $rtt = eval { _get_ping_report( $mirror ) }; my $result = $rtt ? sprintf "+ (%4d ms)", $rtt * 1000 : '!'; $logger->info( sprintf "\t%s %s", $result, $mirror ); } sub _get_ping_report { require URI; my( $mirror ) = @_; my( $url ) = ref $mirror ? $mirror : URI->new( $mirror ); #XXX require Net::Ping; my $ping = Net::Ping->new( 'tcp', 1 ); if( $url->scheme eq 'file' ) { return -e $url->file; } my( $port ) = $url->port; return unless $port; if ( $ping->can('port_number') ) { $ping->port_number($port); } else { $ping->{'port_num'} = $port; } $ping->hires(1) if $ping->can( 'hires' ); my( $alive, $rtt ) = eval{ $ping->ping( $url->host ) }; $alive ? $rtt : undef; } sub _load_local_lib # -I { $logger->debug( "Loading local::lib" ); my $rc = _safe_load_module("local::lib"); unless( $rc ) { $logger->logdie( "Could not load local::lib" ); } local::lib->import; return HEY_IT_WORKED; } sub _use_these_mirrors # -M { $logger->debug( "Setting per session mirrors" ); unless( $_[0] ) { $logger->logdie( "The -M switch requires a comma-separated list of mirrors" ); } $CPAN::Config->{urllist} = [ split /,/, $_[0] ]; $logger->debug( "Mirrors are @{$CPAN::Config->{urllist}}" ); } sub _create_autobundle { $logger->info( "Creating autobundle in $CPAN::Config->{cpan_home}/Bundle" ); CPAN::Shell->autobundle; return HEY_IT_WORKED; } sub _recompile { $logger->info( "Recompiling dynamically-loaded extensions" ); CPAN::Shell->recompile; return HEY_IT_WORKED; } sub _upgrade { $logger->info( "Upgrading all modules" ); CPAN::Shell->upgrade(); return HEY_IT_WORKED; } sub _shell { $logger->info( "Dropping into shell" ); CPAN::shell(); return HEY_IT_WORKED; } sub _load_config # -j { my $argument = shift; my $file = file_name_is_absolute( $argument ) ? $argument : rel2abs( $argument ); croak( "cpan config file [$file] for -j does not exist!\n" ) unless -e $file; # should I clear out any existing config here? $CPAN::Config = {}; delete $INC{'CPAN/Config.pm'}; my $rc = eval "require '$file'"; # CPAN::HandleConfig::require_myconfig_or_config looks for this $INC{'CPAN/MyConfig.pm'} = 'fake out!'; # CPAN::HandleConfig::load looks for this $CPAN::Config_loaded = 'fake out'; croak( "Could not load [$file]: $@\n") unless $rc; return HEY_IT_WORKED; } sub _dump_config # -J { my $args = shift; require Data::Dumper; my $fh = $args->[0] || \*STDOUT; local $Data::Dumper::Sortkeys = 1; my $dd = Data::Dumper->new( [$CPAN::Config], ['$CPAN::Config'] ); print $fh $dd->Dump, "\n1;\n__END__\n"; return HEY_IT_WORKED; } sub _lock_lobotomy # -F { no warnings 'redefine'; *CPAN::_flock = sub { 1 }; *CPAN::checklock = sub { 1 }; return HEY_IT_WORKED; } sub _download { my $args = shift; local $CPAN::DEBUG = 1; my %paths; foreach my $arg ( @$args ) { $logger->info( "Checking $arg" ); my $module = _expand_module( $arg ) or next; my $path = $module->cpan_file; $logger->debug( "Inst file would be $path\n" ); $paths{$module} = _get_file( _make_path( $path ) ); $logger->info( "Downloaded [$arg] to [$paths{$arg}]" ); } return \%paths; } sub _make_path { join "/", qw(authors id), $_[0] } sub _get_file { my $path = shift; my $loaded = _safe_load_module("LWP::Simple"); croak "You need LWP::Simple to use features that fetch files from CPAN\n" unless $loaded; my $file = substr $path, rindex( $path, '/' ) + 1; my $store_path = catfile( cwd(), $file ); $logger->debug( "Store path is $store_path" ); foreach my $site ( @{ $CPAN::Config->{urllist} } ) { my $fetch_path = join "/", $site, $path; $logger->debug( "Trying $fetch_path" ); my $status_code = LWP::Simple::getstore( $fetch_path, $store_path ); last if( 200 <= $status_code and $status_code <= 300 ); $logger->warn( "Could not get [$fetch_path]: Status code $status_code" ); } return $store_path; } sub _gitify { my $args = shift; my $loaded = _safe_load_module("Archive::Extract"); croak "You need Archive::Extract to use features that gitify distributions\n" unless $loaded; my $starting_dir = cwd(); foreach my $arg ( @$args ) { $logger->info( "Checking $arg" ); my $store_paths = _download( [ $arg ] ); $logger->debug( "gitify Store path is $store_paths->{$arg}" ); my $dirname = dirname( $store_paths->{$arg} ); my $ae = Archive::Extract->new( archive => $store_paths->{$arg} ); $ae->extract( to => $dirname ); chdir $ae->extract_path; my $git = $ENV{GIT_COMMAND} || '/usr/local/bin/git'; croak "Could not find $git" unless -e $git; croak "$git is not executable" unless -x $git; # can we do this in Pure Perl? system( $git, 'init' ); system( $git, qw( add . ) ); system( $git, qw( commit -a -m ), 'initial import' ); } chdir $starting_dir; return HEY_IT_WORKED; } sub _show_Changes { my $args = shift; foreach my $arg ( @$args ) { $logger->info( "Checking $arg\n" ); my $module = _expand_module( $arg ) or next; my $out = _get_cpanpm_output(); next unless eval { $module->inst_file }; #next if $module->uptodate; ( my $id = $module->id() ) =~ s/::/\-/; my $url = "http://search.cpan.org/~" . lc( $module->userid ) . "/" . $id . "-" . $module->cpan_version() . "/"; #print "URL: $url\n"; _get_changes_file($url); } return HEY_IT_WORKED; } sub _get_changes_file { croak "Reading Changes files requires LWP::Simple and URI\n" unless _safe_load_module("LWP::Simple") && _safe_load_module("URI"); my $url = shift; my $content = LWP::Simple::get( $url ); $logger->info( "Got $url ..." ) if defined $content; #print $content; my( $change_link ) = $content =~ m|<a href="(.*?)">Changes</a>|gi; my $changes_url = URI->new_abs( $change_link, $url ); $logger->debug( "Change link is: $changes_url" ); my $changes = LWP::Simple::get( $changes_url ); print $changes; return HEY_IT_WORKED; } sub _show_Author { my $args = shift; foreach my $arg ( @$args ) { my $module = _expand_module( $arg ) or next; unless( $module ) { $logger->info( "Didn't find a $arg module, so no author!" ); next; } my $author = CPAN::Shell->expand( "Author", $module->userid ); next unless $module->userid; printf "%-25s %-8s %-25s %s\n", $arg, $module->userid, $author->email, $author->name; } return HEY_IT_WORKED; } sub _show_Details { my $args = shift; foreach my $arg ( @$args ) { my $module = _expand_module( $arg ) or next; my $author = CPAN::Shell->expand( "Author", $module->userid ); next unless $module->userid; print "$arg\n", "-" x 73, "\n\t"; print join "\n\t", $module->description ? $module->description : "(no description)", $module->cpan_file ? $module->cpan_file : "(no cpanfile)", $module->inst_file ? $module->inst_file :"(no installation file)" , 'Installed: ' . ($module->inst_version ? $module->inst_version : "not installed"), 'CPAN: ' . $module->cpan_version . ' ' . ($module->uptodate ? "" : "Not ") . "up to date", $author->fullname . " (" . $module->userid . ")", $author->email; print "\n\n"; } return HEY_IT_WORKED; } BEGIN { my $modules; sub _get_all_namespaces { return $modules if $modules; $modules = [ map { $_->id } CPAN::Shell->expand( "Module", "/./" ) ]; } } sub _show_out_of_date { my $modules = _get_all_namespaces(); printf "%-40s %6s %6s\n", "Module Name", "Local", "CPAN"; print "-" x 73, "\n"; foreach my $module ( @$modules ) { next unless $module = _expand_module($module); next unless $module->inst_file; next if $module->uptodate; printf "%-40s %.4f %.4f\n", $module->id, $module->inst_version ? $module->inst_version : '', $module->cpan_version; } return HEY_IT_WORKED; } sub _show_author_mods { my $args = shift; my %hash = map { lc $_, 1 } @$args; my $modules = _get_all_namespaces(); foreach my $module ( @$modules ) { next unless exists $hash{ lc $module->userid }; print $module->id, "\n"; } return HEY_IT_WORKED; } sub _list_all_mods # -l { require File::Find; my $args = shift; my $fh = \*STDOUT; INC: foreach my $inc ( @INC ) { my( $wanted, $reporter ) = _generator(); File::Find::find( { wanted => $wanted }, $inc ); my $count = 0; FILE: foreach my $file ( @{ $reporter->() } ) { my $version = _parse_version_safely( $file ); my $module_name = _path_to_module( $inc, $file ); next FILE unless defined $module_name; print $fh "$module_name\t$version\n"; #last if $count++ > 5; } } return HEY_IT_WORKED; } sub _generator { my @files = (); sub { push @files, File::Spec->canonpath( $File::Find::name ) if m/\A\w+\.pm\z/ }, sub { \@files }, } sub _parse_version_safely # stolen from PAUSE's mldistwatch, but refactored { my( $file ) = @_; local $/ = "\n"; local $_; # don't mess with the $_ in the map calling this return unless open FILE, "<$file"; my $in_pod = 0; my $version; while( <FILE> ) { chomp; $in_pod = /^=(?!cut)/ ? 1 : /^=cut/ ? 0 : $in_pod; next if $in_pod || /^\s*#/; next unless /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/; my( $sigil, $var ) = ( $1, $2 ); $version = _eval_version( $_, $sigil, $var ); last; } close FILE; return 'undef' unless defined $version; return $version; } sub _eval_version { my( $line, $sigil, $var ) = @_; # split package line to hide from PAUSE my $eval = qq{ package ExtUtils::MakeMaker::_version; local $sigil$var; \$$var=undef; do { $line }; \$$var }; my $version = do { local $^W = 0; no strict; eval( $eval ); }; return $version; } sub _path_to_module { my( $inc, $path ) = @_; return if length $path < length $inc; my $module_path = substr( $path, length $inc ); $module_path =~ s/\.pm\z//; # XXX: this is cheating and doesn't handle everything right my @dirs = grep { ! /\W/ } File::Spec->splitdir( $module_path ); shift @dirs; my $module_name = join "::", @dirs; return $module_name; } sub _expand_module { my( $module ) = @_; my $expanded = CPAN::Shell->expandany( $module ); return $expanded if $expanded; $expanded = CPAN::Shell->expand( "Module", $module ); unless( defined $expanded ) { $logger->error( "Could not expand [$module]. Check the module name." ); my $threshold = ( grep { int } sort { length $a <=> length $b } length($module)/4, 4 )[0]; my $guesses = _guess_at_module_name( $module, $threshold ); if( defined $guesses and @$guesses ) { $logger->info( "Perhaps you meant one of these:" ); foreach my $guess ( @$guesses ) { $logger->info( "\t$guess" ); } } return; } return $expanded; } my $guessers = [ [ qw( Text::Levenshtein::XS distance 7 1 ) ], [ qw( Text::Levenshtein::Damerau::XS xs_edistance 7 1 ) ], [ qw( Text::Levenshtein distance 7 1 ) ], [ qw( Text::Levenshtein::Damerau::PP pp_edistance 7 1 ) ], ]; sub _disable_guessers { $_->[-1] = 0 for @$guessers; } # for -x sub _guess_namespace { my $args = shift; foreach my $arg ( @$args ) { $logger->debug( "Checking $arg" ); my $guesses = _guess_at_module_name( $arg ); foreach my $guess ( @$guesses ) { print $guess, "\n"; } } return HEY_IT_WORKED; } sub _list_all_namespaces { my $modules = _get_all_namespaces(); foreach my $module ( @$modules ) { print $module, "\n"; } } BEGIN { my $distance; my $_threshold; my $can_guess; my $shown_help = 0; sub _guess_at_module_name { my( $target, $threshold ) = @_; unless( defined $distance ) { foreach my $try ( @$guessers ) { $can_guess = eval "require $try->[0]; 1" or next; $try->[-1] or next; # disabled no strict 'refs'; $distance = \&{ join "::", @$try[0,1] }; $threshold ||= $try->[2]; } } $_threshold ||= $threshold; unless( $distance ) { unless( $shown_help ) { my $modules = join ", ", map { $_->[0] } @$guessers; substr $modules, rindex( $modules, ',' ), 1, ', and'; # Should this be colorized? if( $can_guess ) { $logger->info( "I can suggest names if you provide the -x option on invocation." ); } else { $logger->info( "I can suggest names if you install one of $modules" ); $logger->info( "and you provide the -x option on invocation." ); } $shown_help++; } return; } my $modules = _get_all_namespaces(); $logger->info( "Checking " . @$modules . " namespaces for close match suggestions" ); my %guesses; foreach my $guess ( @$modules ) { my $distance = $distance->( $target, $guess ); next if $distance > $_threshold; $guesses{$guess} = $distance; } my @guesses = sort { $guesses{$a} <=> $guesses{$b} } keys %guesses; return [ grep { defined } @guesses[0..9] ]; } } 1; =back =head1 EXIT VALUES The script exits with zero if it thinks that everything worked, or a positive number if it thinks that something failed. Note, however, that in some cases it has to divine a failure by the output of things it does not control. For now, the exit codes are vague: 1 An unknown error 2 The was an external problem 4 There was an internal problem with the script 8 A module failed to install =head1 TO DO * There is initial support for Log4perl if it is available, but I haven't gone through everything to make the NullLogger work out correctly if Log4perl is not installed. * When I capture CPAN.pm output, I need to check for errors and report them to the user. * Warnings switch * Check then exit =head1 BUGS * none noted =head1 SEE ALSO L<CPAN>, L<App::cpanminus> =head1 SOURCE AVAILABILITY This code is in Github in the CPAN.pm repository: https://github.com/andk/cpanpm The source used to be tracked separately in another GitHub repo, but the canonical source is now in the above repo. =head1 CREDITS Japheth Cleaver added the bits to allow a forced install (C<-f>). Jim Brandt suggested and provided the initial implementation for the up-to-date and Changes features. Adam Kennedy pointed out that C<exit()> causes problems on Windows where this script ends up with a .bat extension David Golden helps integrate this into the C<CPAN.pm> repos. Jim Keenan fixed up various issues with _download =head1 AUTHOR brian d foy, C<< <bdfoy@cpan.org> >> =head1 COPYRIGHT Copyright (c) 2001-2021, brian d foy, All Rights Reserved. You may redistribute this under the same terms as Perl itself. =cut # Local Variables: # mode: cperl # indent-tabs-mode: t # cperl-indent-level: 8 # cperl-continued-statement-offset: 8 # End: �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/HTTP-Tiny-0.090/MYMETA.json�����������������������������������������0000444�����������������00000013371�15111204742�0016273 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "A small, simple, correct HTTP/1.1 client", "author" : [ "Christian Hansen <chansen@cpan.org>", "David Golden <dagolden@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "HTTP-Tiny", "no_index" : { "directory" : [ "corpus", "examples", "t", "xt" ], "package" : [ "DB" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.17" }, "suggests" : { "JSON::PP" : "2.27300" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::Plugin::ReleaseStatus::FromVersion" : "0", "Dist::Zilla::Plugin::RemovePrereqs" : "0", "Dist::Zilla::PluginBundle::DAGOLDEN" : "0.072", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Perl::Critic::Policy::Lax::ProhibitStringyEval::ExceptForRequire" : "0", "Pod::Coverage::TrustPod" : "0", "Pod::Wordlist" : "0", "Software::License::Perl_5" : "0", "Test::CPAN::Meta" : "0", "Test::MinimumVersion" : "0", "Test::More" : "0", "Test::Perl::Critic" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Portability::Files" : "0", "Test::Spelling" : "0.17", "Test::Version" : "1", "perl" : "5.006" } }, "runtime" : { "recommends" : { "HTTP::CookieJar" : "0.001", "IO::Socket::IP" : "0.32", "IO::Socket::SSL" : "1.968", "Net::SSLeay" : "1.49" }, "requires" : { "Carp" : "0", "Fcntl" : "0", "IO::Socket" : "0", "MIME::Base64" : "0", "Socket" : "0", "Time::Local" : "0", "bytes" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "Data::Dumper" : "0", "Exporter" : "0", "ExtUtils::MakeMaker" : "0", "File::Basename" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Dir" : "0", "IO::File" : "0", "IO::Socket::INET" : "0", "IPC::Cmd" : "0", "Test::More" : "0.96", "lib" : "0", "open" : "0" } } }, "provides" : { "HTTP::Tiny" : { "file" : "lib/HTTP/Tiny.pm", "version" : "0.090" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Perl-Toolchain-Gang/HTTP-Tiny/issues" }, "homepage" : "https://github.com/Perl-Toolchain-Gang/HTTP-Tiny", "repository" : { "type" : "git", "url" : "https://github.com/Perl-Toolchain-Gang/HTTP-Tiny.git", "web" : "https://github.com/Perl-Toolchain-Gang/HTTP-Tiny" } }, "version" : "0.090", "x_authority" : "cpan:DAGOLDEN", "x_contributors" : [ "Alan Gardner <gardner@pythian.com>", "Alessandro Ghedini <al3xbio@gmail.com>", "A. Sinan Unur <nanis@cpan.org>", "Brad Gilbert <bgills@cpan.org>", "brian m. carlson <sandals@crustytoothpaste.net>", "Chris Nehren <apeiron@cpan.org>", "Chris Weyl <cweyl@alumni.drew.edu>", "Claes Jakobsson <claes@surfar.nu>", "Clinton Gormley <clint@traveljury.com>", "Craig A. Berry <craigberry@mac.com>", "David Golden <xdg@xdg.me>", "David Mitchell <davem@iabyn.com>", "Dean Pearce <pearce@pythian.com>", "Edward Zborowski <ed@rubensteintech.com>", "Felipe Gasper <felipe@felipegasper.com>", "Graham Knop <haarg@haarg.org>", "Greg Kennedy <kennedy.greg@gmail.com>", "James E Keenan <jkeenan@cpan.org>", "James Raspass <jraspass@gmail.com>", "Jeremy Mates <jmates@cpan.org>", "Jess Robinson <castaway@desert-island.me.uk>", "Karen Etheridge <ether@cpan.org>", "Lukas Eklund <leklund@gmail.com>", "Martin J. Evans <mjegh@ntlworld.com>", "Martin-Louis Bright <mlbright@gmail.com>", "Matthew Horsfall <wolfsage@gmail.com>", "Michael R. Davis <mrdvt92@users.noreply.github.com>", "Mike Doherty <doherty@cpan.org>", "Nicolas Rochelemagne <rochelemagne@cpanel.net>", "Olaf Alders <olaf@wundersolutions.com>", "Olivier Mengué <dolmen@cpan.org>", "Petr Písař <ppisar@redhat.com>", "sanjay-cpu <snjkmr32@gmail.com>", "Serguei Trouchelle <stro@cpan.org>", "Shoichi Kaji <skaji@cpan.org>", "SkyMarshal <skymarshal1729@gmail.com>", "Sören Kornetzki <soeren.kornetzki@delti.com>", "Steve Grazzini <steve.grazzini@grantstreet.com>", "Stig Palmquist <git@stig.io>", "Syohei YOSHIDA <syohex@gmail.com>", "Tatsuhiko Miyagawa <miyagawa@bulknews.net>", "Tom Hukins <tom@eborcom.com>", "Tony Cook <tony@develop-help.com>", "Xavier Guimard <yadd@debian.org>" ], "x_generated_by_perl" : "v5.38.2", "x_serialization_backend" : "JSON::PP version 4.06", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/HTTP-Tiny-0.090/install.json����������������������������������������0000444�����������������00000000315�15111204742�0016737 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"dist":"HTTP-Tiny-0.090","provides":{"HTTP::Tiny":{"file":"lib/HTTP/Tiny.pm","version":"0.090"}},"version":"0.090","pathname":"H/HA/HAARG/HTTP-Tiny-0.090.tar.gz","target":"HTTP::Tiny","name":"HTTP::Tiny"}�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/common-sense-3.75/MYMETA.json���������������������������������������0000444�����������������00000001474�15111204742�0017125 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150001, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "common-sense", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } } }, "release_status" : "stable", "version" : 3.75, "x_serialization_backend" : "JSON::PP version 4.06" } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/common-sense-3.75/install.json��������������������������������������0000444�����������������00000000322�15111204742�0017566 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"dist":"common-sense-3.75","provides":{"common::sense":{"version":3.75,"file":"sense.pm.PL"}},"pathname":"M/ML/MLEHMANN/common-sense-3.75.tar.gz","version":3.75,"name":"common::sense","target":"common::sense"}��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/Canary-Stability-2013/MYMETA.json�����������������������������������0000444�����������������00000001432�15111204742�0017664 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Canary-Stability", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } } }, "release_status" : "stable", "version" : "2013", "x_serialization_backend" : "JSON::PP version 4.06" } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/Canary-Stability-2013/install.json����������������������������������0000444�����������������00000000347�15111204742�0020342 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"provides":{"Canary::Stability":{"version":2013,"file":"Stability.pm"}},"dist":"Canary-Stability-2013","target":"Canary::Stability","name":"Canary::Stability","version":2013,"pathname":"M/ML/MLEHMANN/Canary-Stability-2013.tar.gz"}�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/CPAN-Meta-YAML-0.020/MYMETA.json������������������������������������0000444�����������������00000012501�15111204742�0016723 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "Read and write a subset of YAML for CPAN Meta files", "author" : [ "Adam Kennedy <adamk@cpan.org>", "David Golden <dagolden@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "CPAN-Meta-YAML", "no_index" : { "directory" : [ "corpus", "examples", "t", "xt" ], "package" : [ "DB" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.17" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::AppendExternalData" : "0", "Dist::Zilla::Plugin::Authority" : "0", "Dist::Zilla::Plugin::AutoPrereqs" : "0", "Dist::Zilla::Plugin::CheckChangesHasContent" : "0", "Dist::Zilla::Plugin::CheckMetaResources" : "0", "Dist::Zilla::Plugin::CheckPrereqsIndexed" : "0", "Dist::Zilla::Plugin::ConfirmRelease" : "0", "Dist::Zilla::Plugin::Doppelgaenger" : "0.007", "Dist::Zilla::Plugin::Encoding" : "0", "Dist::Zilla::Plugin::Git::Check" : "0", "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch" : "0", "Dist::Zilla::Plugin::Git::Commit" : "0", "Dist::Zilla::Plugin::Git::Contributors" : "0", "Dist::Zilla::Plugin::Git::GatherDir" : "0", "Dist::Zilla::Plugin::Git::NextVersion" : "0", "Dist::Zilla::Plugin::Git::Push" : "0", "Dist::Zilla::Plugin::Git::Tag" : "0", "Dist::Zilla::Plugin::GithubMeta" : "0", "Dist::Zilla::Plugin::License" : "0", "Dist::Zilla::Plugin::MakeMaker" : "0", "Dist::Zilla::Plugin::MakeMaker::Highlander" : "0.003", "Dist::Zilla::Plugin::Manifest" : "0", "Dist::Zilla::Plugin::MetaJSON" : "0", "Dist::Zilla::Plugin::MetaNoIndex" : "0", "Dist::Zilla::Plugin::MetaProvides::Package" : "0", "Dist::Zilla::Plugin::MetaResources" : "0", "Dist::Zilla::Plugin::MetaTests" : "0", "Dist::Zilla::Plugin::MetaYAML" : "0", "Dist::Zilla::Plugin::MinimumPerl" : "0", "Dist::Zilla::Plugin::NextRelease" : "5.033", "Dist::Zilla::Plugin::PkgVersion" : "0", "Dist::Zilla::Plugin::Pod2Readme" : "0", "Dist::Zilla::Plugin::PodSyntaxTests" : "0", "Dist::Zilla::Plugin::PodWeaver" : "0", "Dist::Zilla::Plugin::Prereqs::AuthorDeps" : "0", "Dist::Zilla::Plugin::PromptIfStale" : "0", "Dist::Zilla::Plugin::RemovePrereqs" : "0", "Dist::Zilla::Plugin::RunExtraTests" : "0", "Dist::Zilla::Plugin::Test::Compile" : "0", "Dist::Zilla::Plugin::Test::ReportPrereqs" : "0", "Dist::Zilla::Plugin::Test::Version" : "0", "Dist::Zilla::Plugin::TestRelease" : "0", "Dist::Zilla::Plugin::UploadToCPAN" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Software::License::Perl_5" : "0", "Test::CPAN::Meta" : "0", "Test::More" : "0", "Test::Pod" : "1.41", "Test::Version" : "1" } }, "runtime" : { "requires" : { "B" : "0", "Carp" : "0", "Exporter" : "0", "Fcntl" : "0", "Scalar::Util" : "0", "perl" : "5.008001", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Basename" : "0", "File::Find" : "0", "File::Spec" : "0", "File::Spec::Functions" : "0", "File::Temp" : "0.19", "IO::Dir" : "0", "JSON::PP" : "0", "Test::More" : "0.88", "base" : "0", "lib" : "0", "open" : "0", "utf8" : "0", "vars" : "0" } } }, "provides" : { "CPAN::Meta::YAML" : { "file" : "lib/CPAN/Meta/YAML.pm", "version" : "0.020" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Perl-Toolchain-Gang/YAML-Tiny/issues" }, "homepage" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-YAML", "repository" : { "type" : "git", "url" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-YAML.git", "web" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-YAML" } }, "version" : "0.020", "x_authority" : "cpan:DAGOLDEN", "x_contributors" : [ "Karen Etheridge <ether@cpan.org>" ], "x_generated_by_perl" : "v5.41.6", "x_serialization_backend" : "JSON::PP version 4.06", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/CPAN-Meta-YAML-0.020/install.json�����������������������������������0000444�����������������00000000356�15111204742�0017402 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"name":"CPAN::Meta::YAML","target":"CPAN::Meta::YAML","pathname":"E/ET/ETHER/CPAN-Meta-YAML-0.020.tar.gz","version":"0.020","provides":{"CPAN::Meta::YAML":{"version":"0.020","file":"lib/CPAN/Meta/YAML.pm"}},"dist":"CPAN-Meta-YAML-0.020"}����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/LWP-UserAgent-DNS-Hosts-0.14/MYMETA.json����������������������������0000444�����������������00000006022�15111204742�0020557 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "Override LWP HTTP/HTTPS request's host like /etc/hosts", "author" : [ "NAKAGAWA Masaki <masaki@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "Minilla/v3.1.10, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "LWP-UserAgent-DNS-Hosts", "no_index" : { "directory" : [ "t", "xt", "inc", "share", "eg", "examples", "author", "builder" ] }, "optional_features" : { "https" : { "description" : "SSL support", "prereqs" : { "runtime" : { "recommends" : { "LWP::Protocol::https" : "0" } }, "test" : { "requires" : { "HTTP::Daemon::SSL" : "0" } } } } }, "prereqs" : { "configure" : { "requires" : { "Module::Build::Tiny" : "0.035" } }, "develop" : { "requires" : { "Test::CPAN::Meta" : "0", "Test::MinimumVersion::Fast" : "0.04", "Test::PAUSE::Permissions" : "0.07", "Test::Pod" : "1.41", "Test::Spellunker" : "v0.2.7" } }, "runtime" : { "requires" : { "LWP::Protocol" : "0", "LWP::Protocol::http" : "0", "Scope::Guard" : "0", "parent" : "0", "perl" : "5.008001" } }, "test" : { "requires" : { "File::Temp" : "0", "LWP::UserAgent" : "0", "Test::Fake::HTTPD" : "0.08", "Test::More" : "0.98", "Test::UseAllModules" : "0" } } }, "provides" : { "LWP::Protocol::http::hosts" : { "file" : "lib/LWP/Protocol/http/hosts.pm" }, "LWP::Protocol::https::hosts" : { "file" : "lib/LWP/Protocol/https/hosts.pm" }, "LWP::UserAgent::DNS::Hosts" : { "file" : "lib/LWP/UserAgent/DNS/Hosts.pm", "version" : "0.14" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/masaki/p5-LWP-UserAgent-DNS-Hosts/issues" }, "homepage" : "https://github.com/masaki/p5-LWP-UserAgent-DNS-Hosts", "repository" : { "type" : "git", "url" : "git://github.com/masaki/p5-LWP-UserAgent-DNS-Hosts.git", "web" : "https://github.com/masaki/p5-LWP-UserAgent-DNS-Hosts" } }, "version" : "0.14", "x_authority" : "cpan:MASAKI", "x_contributors" : [ "Anirvan Chatterjee <anirvan@users.noreply.github.com>", "Masaki Nakagawa <masaki.nakagawa@gmail.com>", "Petr Písař <ppisar@redhat.com>", "Tatsuhiko Miyagawa <miyagawa@bulknews.net>" ], "x_serialization_backend" : "JSON::PP version 2.97001", "x_static_install" : 1 } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/LWP-UserAgent-DNS-Hosts-0.14/install.json���������������������������0000444�����������������00000000664�15111204742�0021237 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"dist":"LWP-UserAgent-DNS-Hosts-0.14","provides":{"LWP::Protocol::http::hosts":{"file":"lib/LWP/Protocol/http/hosts.pm"},"LWP::UserAgent::DNS::Hosts":{"file":"lib/LWP/UserAgent/DNS/Hosts.pm","version":"0.14"},"LWP::Protocol::https::hosts":{"file":"lib/LWP/Protocol/https/hosts.pm"}},"pathname":"M/MA/MASAKI/LWP-UserAgent-DNS-Hosts-0.14.tar.gz","version":"0.14","target":"LWP::UserAgent::DNS::Hosts","name":"LWP::UserAgent::DNS::Hosts"}����������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/LWP-Protocol-https-6.14/MYMETA.json���������������������������������0000444�����������������00000061656�15111204742�0020127 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "Provide https support for LWP::UserAgent", "author" : [ "Gisle Aas <gisle@activestate.com>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.031, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "LWP-Protocol-https", "no_index" : { "directory" : [ "t", "xt" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" }, "suggests" : { "JSON::PP" : "2.27300" } }, "develop" : { "recommends" : { "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007" }, "requires" : { "Capture::Tiny" : "0.48", "File::Spec" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Pod::Coverage::TrustPod" : "0", "Test::CPAN::Changes" : "0.19", "Test::CPAN::Meta" : "0", "Test::CheckManifest" : "1.29", "Test::CleanNamespaces" : "0.15", "Test::EOL" : "0", "Test::Kwalitee" : "1.22", "Test::MinimumVersion" : "0", "Test::Mojibake" : "0", "Test::More" : "0.94", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Pod::Spelling::CommonMistakes" : "1.000", "Test::Portability::Files" : "0", "Test::Spelling" : "0.12", "Test::Version" : "1" } }, "runtime" : { "requires" : { "IO::Socket::SSL" : "1.970", "LWP::Protocol::http" : "0", "LWP::UserAgent" : "6.06", "Net::HTTPS" : "6", "base" : "0", "perl" : "5.008001", "strict" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Select" : "0", "IO::Socket::INET" : "0", "IO::Socket::SSL" : "1.970", "IO::Socket::SSL::Utils" : "0", "LWP::UserAgent" : "6.06", "Socket" : "0", "Test::More" : "0.96", "Test::Needs" : "0.002010", "Test::RequiresInternet" : "0", "warnings" : "0" } } }, "provides" : { "LWP::Protocol::https" : { "file" : "lib/LWP/Protocol/https.pm", "version" : "6.14" }, "LWP::Protocol::https::Socket" : { "file" : "lib/LWP/Protocol/https.pm", "version" : "6.14" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/libwww-perl/LWP-Protocol-https/issues" }, "homepage" : "https://github.com/libwww-perl/LWP-Protocol-https", "repository" : { "type" : "git", "url" : "https://github.com/libwww-perl/LWP-Protocol-https.git", "web" : "https://github.com/libwww-perl/LWP-Protocol-https" }, "x_IRC" : "irc://irc.perl.org/#lwp", "x_MailingList" : "mailto:libwww@perl.org" }, "version" : "6.14", "x_Dist_Zilla" : { "perl" : { "version" : "5.034000" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::Git::GatherDir", "config" : { "Dist::Zilla::Plugin::GatherDir" : { "exclude_filename" : [ "LICENSE", "META.json", "Makefile.PL", "README.md" ], "exclude_match" : [], "include_dotfiles" : 0, "prefix" : "", "prune_directory" : [], "root" : "." }, "Dist::Zilla::Plugin::Git::GatherDir" : { "include_untracked" : 0 } }, "name" : "Git::GatherDir", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::PruneCruft", "name" : "PruneCruft", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "MetaConfig", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Package", "config" : { "Dist::Zilla::Plugin::MetaProvides::Package" : { "finder_objects" : [ { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "6.031" } ], "include_underscores" : 0 }, "Dist::Zilla::Role::MetaProvider::Provider" : { "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004", "inherit_missing" : 1, "inherit_version" : 1, "meta_noindex" : 1 }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000037", "version" : "0.006" } }, "name" : "MetaProvides::Package", "version" : "2.004003" }, { "class" : "Dist::Zilla::Plugin::MetaNoIndex", "name" : "MetaNoIndex", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "MetaYAML", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "MetaJSON", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::MetaResources", "name" : "MetaResources", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "config" : { "Dist::Zilla::Plugin::Git::Check" : { "untracked_files" : "die" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "LICENSE", "META.json", "Makefile.PL", "README.md" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." } }, "name" : "Git::Check", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::Git::Contributors", "config" : { "Dist::Zilla::Plugin::Git::Contributors" : { "git_version" : "2.34.1", "include_authors" : 0, "include_releaser" : 1, "order_by" : "name", "paths" : [] } }, "name" : "Git::Contributors", "version" : "0.036" }, { "class" : "Dist::Zilla::Plugin::GithubMeta", "name" : "GithubMeta", "version" : "0.58" }, { "class" : "Dist::Zilla::Plugin::Manifest", "name" : "Manifest", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "License", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "config" : { "Dist::Zilla::Role::FileWatcher" : { "version" : "0.006" } }, "name" : "Markdown_Readme", "version" : "0.163250" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "recommends" } }, "name" : "@Git::VersionManager/pluginbundle version", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::RewriteVersion::Transitional", "config" : { "Dist::Zilla::Plugin::RewriteVersion" : { "add_tarball_name" : 0, "finders" : [ ":ExecFiles", ":InstallModules" ], "global" : 0, "skip_version_provider" : 0 }, "Dist::Zilla::Plugin::RewriteVersion::Transitional" : {} }, "name" : "@Git::VersionManager/RewriteVersion::Transitional", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Update", "name" : "@Git::VersionManager/MetaProvides::Update", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", "config" : { "Dist::Zilla::Plugin::CopyFilesFromRelease" : { "filename" : [ "Changes" ], "match" : [] } }, "name" : "@Git::VersionManager/CopyFilesFromRelease", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [], "commit_msg" : "v%V%n%n%c", "signoff" : 0 }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Changes", "LICENSE", "META.json", "Makefile.PL" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Git::VersionManager/release snapshot", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::Git::Tag", "config" : { "Dist::Zilla::Plugin::Git::Tag" : { "branch" : null, "changelog" : "Changes", "signed" : 0, "tag" : "v6.14", "tag_format" : "v%V", "tag_message" : "v%V" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Git::VersionManager/Git::Tag", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional", "config" : { "Dist::Zilla::Plugin::BumpVersionAfterRelease" : { "finders" : [ ":ExecFiles", ":InstallModules" ], "global" : 0, "munge_makefile_pl" : 1 }, "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : {} }, "name" : "@Git::VersionManager/BumpVersionAfterRelease::Transitional", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::NextRelease", "name" : "@Git::VersionManager/NextRelease", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [], "commit_msg" : "increment $VERSION after %v release", "signoff" : 0 }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Build.PL", "Changes", "Makefile.PL" ], "allow_dirty_match" : [ "(?^:^lib/.*\\.pm$)" ], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Git::VersionManager/post-release commit", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::Prereqs::FromCPANfile", "name" : "Prereqs::FromCPANfile", "version" : "0.08" }, { "class" : "Dist::Zilla::Plugin::MakeMaker::Awesome", "config" : { "Dist::Zilla::Plugin::MakeMaker" : { "make_path" : "make", "version" : "6.031" }, "Dist::Zilla::Role::TestRunner" : { "default_jobs" : "8", "version" : "6.031" } }, "name" : "MakeMaker::Awesome", "version" : "0.49" }, { "class" : "Dist::Zilla::Plugin::CheckChangeLog", "name" : "CheckChangeLog", "version" : "0.05" }, { "class" : "Dist::Zilla::Plugin::CheckChangesHasContent", "name" : "CheckChangesHasContent", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::Test::Kwalitee", "config" : { "Dist::Zilla::Plugin::Test::Kwalitee" : { "filename" : "xt/author/kwalitee.t", "skiptest" : [ "has_readme" ] } }, "name" : "Test::Kwalitee", "version" : "2.12" }, { "class" : "Dist::Zilla::Plugin::MojibakeTests", "name" : "MojibakeTests", "version" : "0.8" }, { "class" : "Dist::Zilla::Plugin::Test::Version", "name" : "Test::Version", "version" : "1.09" }, { "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", "name" : "Test::ReportPrereqs", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::Test::Compile", "config" : { "Dist::Zilla::Plugin::Test::Compile" : { "bail_out_on_fail" : "1", "fail_on_warning" : "author", "fake_home" : 0, "filename" : "xt/author/00-compile.t", "module_finder" : [ ":InstallModules" ], "needs_display" : 0, "phase" : "develop", "script_finder" : [ ":PerlExecFiles" ], "skips" : [], "switch" : [] } }, "name" : "Test::Compile", "version" : "2.058" }, { "class" : "Dist::Zilla::Plugin::Test::Portability", "config" : { "Dist::Zilla::Plugin::Test::Portability" : { "options" : "" } }, "name" : "Test::Portability", "version" : "2.001001" }, { "class" : "Dist::Zilla::Plugin::Test::CleanNamespaces", "config" : { "Dist::Zilla::Plugin::Test::CleanNamespaces" : { "filename" : "xt/author/clean-namespaces.t", "skips" : [] } }, "name" : "Test::CleanNamespaces", "version" : "0.006" }, { "class" : "Dist::Zilla::Plugin::Test::EOL", "config" : { "Dist::Zilla::Plugin::Test::EOL" : { "filename" : "xt/author/eol.t", "finder" : [ ":ExecFiles", ":InstallModules", ":TestFiles" ], "trailing_whitespace" : 1 } }, "name" : "Test::EOL", "version" : "0.19" }, { "class" : "Dist::Zilla::Plugin::MetaTests", "name" : "MetaTests", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Test::MinimumVersion", "config" : { "Dist::Zilla::Plugin::Test::MinimumVersion" : { "max_target_perl" : null } }, "name" : "Test::MinimumVersion", "version" : "2.000010" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "PodSyntaxTests", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable", "name" : "Test::Pod::Coverage::Configurable", "version" : "0.07" }, { "class" : "Dist::Zilla::Plugin::Test::PodSpelling", "config" : { "Dist::Zilla::Plugin::Test::PodSpelling" : { "directories" : [ "bin", "lib" ], "spell_cmd" : "aspell list", "stopwords" : [], "wordlist" : "Pod::Wordlist" } }, "name" : "Test::PodSpelling", "version" : "2.007005" }, { "class" : "Dist::Zilla::Plugin::RunExtraTests", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : "8" } }, "name" : "RunExtraTests", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::CheckStrictVersion", "name" : "CheckStrictVersion", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromBuild", "name" : "CopyFilesFromBuild", "version" : "0.170880" }, { "class" : "Dist::Zilla::Plugin::TestRelease", "name" : "TestRelease", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::ConfirmRelease", "name" : "ConfirmRelease", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN", "name" : "UploadToCPAN", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::Git::Push", "config" : { "Dist::Zilla::Plugin::Git::Push" : { "push_to" : [ "origin" ], "remotes_must_exist" : 1 }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." } }, "name" : "Git::Push", "version" : "2.049" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":TestFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExtraTestFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":PerlExecFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":AllFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":NoFiles", "version" : "6.031" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "6.031" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : 0 }, "version" : "6.031" } }, "x_contributors" : [ "Adam Kennedy <adamk@cpan.org>", "Adam Sjogren <asjo@koldfront.dk>", "Alexandr Ciornii <alexchorny@gmail.com>", "Alexey Tourbin <at@altlinux.ru>", "Alex Kapranoff <ka@nadoby.ru>", "amire80 <amir.aharoni@gmail.com>", "Andreas J. Koenig <andreas.koenig@anima.de>", "Axel Burri <axel@tty0.ch>", "Bill Mann <wfmann@alum.mit.edu>", "Bron Gondwana <brong@fastmail.fm>", "Chase Whitener <capoeirab@cpan.org>", "Christopher J. Madsen <cjm@cpan.org>", "cpansprout <cpansprout@gmail.com>", "Dan Book <grinnz@grinnz.com>", "Daniel Hedlund <Daniel.Hedlund@eprize.com>", "David E. Wheeler <david@justatheory.com>", "David Golden <dagolden@cpan.org>", "DAVIDRW <davidrw@cpan.org>", "dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "Dmitriy Shamatrin <dshamatrin@cloudbees.com>", "drieux <drieux@yahoo-inc.com>", "Father Chrysostomos <sprout@cpan.org>", "FWILES <FWILES@cpan.org>", "Gavin Peters <gpeters@deepsky.com>", "Gianni Ceccarelli <gianni.ceccarelli@broadbean.com>", "Gisle Aas <gisle@aas.no>", "Graeme Thompson <Graeme.Thompson@mobilecohesion.com>", "Hans-H. Froehlich <hfroehlich@co-de-co.de>", "Ian Kilgore <iank@cpan.org>", "Jacob J <waif@chaos2.org>", "Jakub Wilk <jwilk@jwilk.net>", "jefflee <shaohua@gmail.com>", "JJ Merelo <jjmerelo@gmail.com>", "john9art <john9art@yahoo.com>", "Jon Jensen <jon@endpoint.com>", "Karen Etheridge <ether@cpan.org>", "Leo Lapworth <leo@cuckoo.org>", "Marin Tsanov <btg.tsanov.marin@gmail.com>", "Mark Stosberg <mark@stosberg.com>", "Michael G. Schwern <schwern@pobox.com>", "Mike Schilli <github@perlmeister.com>", "Mohammad S Anwar <mohammad.anwar@yahoo.com>", "murphy <murphy@genome.chop.edu>", "Nicolas R <nicolas@atoomic.org>", "Olaf Alders <olaf@wundersolutions.com>", "Ondrej Hanak <ondrej.hanak@ubs.com>", "openstrike <git@openstrike.co.uk>", "Peter Rabbitson <ribasushi@cpan.org>", "phrstbrn <phrstbrn@gmail.com>", "Randy Stauner <randy@magnificent-tears.com>", "Robert Stone <talby@trap.mtview.ca.us>", "Rolf Grossmann <rg@progtech.net>", "ruff <ruff@ukrpost.net>", "sasao <sasao@yugen.org>", "Sean M. Burke <sburke@cpan.org>", "Shoichi Kaji <skaji@cpan.org>", "Slaven Rezic <slaven@rezic.de>", "Spiros Denaxas <s.denaxas@gmail.com>", "Steffen Ullrich <Steffen_Ullrich@genua.de>", "Steve Hay <SteveHay@planit.com>", "Tim Couzins <tim.couzins@sophos.com>", "Todd Lipcon <todd@amiestreet.com>", "Tom Hukins <tom@eborcom.com>", "Tony Finch <dot@dotat.at>", "Toru Yamaguchi <zigorou@cpan.org>", "uid39246 <uid39246>", "Ville Skyttä <ville.skytta@iki.fi>", "Yuri Karaban <tech@askold.net>", "Yury Zavarin <yury.zavarin@gmail.com>", "Zefram <zefram@fysh.org>" ], "x_generated_by_perl" : "v5.34.0", "x_serialization_backend" : "JSON::PP version 4.06", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } ����������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/LWP-Protocol-https-6.14/install.json��������������������������������0000444�����������������00000000531�15111204742�0020562 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"dist":"LWP-Protocol-https-6.14","provides":{"LWP::Protocol::https::Socket":{"version":"6.14","file":"lib/LWP/Protocol/https.pm"},"LWP::Protocol::https":{"file":"lib/LWP/Protocol/https.pm","version":"6.14"}},"pathname":"O/OA/OALDERS/LWP-Protocol-https-6.14.tar.gz","target":"LWP::Protocol::https","version":"6.14","name":"LWP::Protocol::https"}�����������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/CPAN-2.38/MYMETA.json�����������������������������������������������0000444�����������������00000004713�15111204742�0015240 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "query, download and build perl modules from CPAN sites", "author" : [ "Andreas Koenig <andreas.koenig.gmwojprw@franz.ak.mind.de>" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010", "keywords" : [ "CPAN", "module", "module installation" ], "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "CPAN", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Archive::Tar" : "0", "Archive::Zip" : "0", "CPAN::Meta" : "0", "CPAN::Meta::Requirements" : "2.121", "CPAN::Meta::YAML" : "0", "Compress::Bzip2" : "0", "Compress::Zlib" : "0", "Data::Dumper" : "0", "Digest::MD5" : "0", "Digest::SHA" : "0", "Exporter" : "0", "Exporter::Heavy" : "0", "ExtUtils::CBuilder" : "0", "File::Copy" : "0", "File::HomeDir" : "0", "File::Spec" : "0", "File::Temp" : "0", "File::Which" : "0", "HTTP::Tiny" : "0.005", "IO::Compress::Base" : "0", "IO::Zlib" : "0", "JSON::PP" : "0", "MIME::Base64" : "0", "Module::Build" : "0", "Module::Signature" : "0", "Net::FTP" : "0", "Net::Ping" : "0", "Parse::CPAN::Meta" : "0", "Pod::Perldoc" : "0", "Pod::Perldoc::ToMan" : "0", "Scalar::Util" : "0", "Socket" : "0", "Term::ReadKey" : "0", "Test::Harness" : "2.62", "Test::More" : "0", "Text::Glob" : "0", "Text::ParseWords" : "0", "Text::Wrap" : "0", "perl" : "5.006002" } } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "https://github.com/andk/cpanpm" } }, "version" : "2.38", "x_serialization_backend" : "JSON::PP version 4.06" } �����������������������������������������������������x86_64-linux-thread-multi/.meta/CPAN-2.38/install.json����������������������������������������������0000444�����������������00000006750�15111204742�0015715 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"provides":{"CPAN::Kwalify":{"version":"5.50","file":"lib/CPAN/Kwalify.pm"},"CPAN::Distrostatus":{"version":5.5,"file":"lib/CPAN/Distrostatus.pm"},"CPAN::Distroprefs":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Distroprefs::Result::Warning":{"version":6.0001,"file":"lib/CPAN/Distroprefs.pm"},"CPAN::Distroprefs::Result::Success":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Distroprefs::Result::Error":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Debug":{"file":"lib/CPAN/Debug.pm","version":5.5001},"CPAN::Distribution":{"version":2.34,"file":"lib/CPAN/Distribution.pm"},"CPAN::Eval":{"version":6.0001,"file":"lib/CPAN/Distroprefs.pm"},"CPAN::Nox":{"file":"lib/CPAN/Nox.pm","version":5.5001},"CPAN::Exception::RecursiveDependency":{"version":5.5001,"file":"lib/CPAN/Exception/RecursiveDependency.pm"},"CPAN::Complete":{"file":"lib/CPAN/Complete.pm","version":5.5001},"CPAN::Distroprefs::Result":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Index":{"version":2.29,"file":"lib/CPAN/Index.pm"},"CPAN::Exception::yaml_not_installed":{"version":5.5,"file":"lib/CPAN/Exception/yaml_not_installed.pm"},"CPAN::LWP::UserAgent":{"version":1.9601,"file":"lib/CPAN/LWP/UserAgent.pm"},"CPAN::Exception::yaml_process_error":{"file":"lib/CPAN/Exception/yaml_process_error.pm","version":5.5},"CPAN::FTP::netrc":{"version":1.01,"file":"lib/CPAN/FTP/netrc.pm"},"CPAN::CacheMgr":{"version":5.5002,"file":"lib/CPAN/CacheMgr.pm"},"CPAN::HandleConfig":{"file":"lib/CPAN/HandleConfig.pm","version":5.5013},"CPAN::DeferredCode":{"version":"5.50","file":"lib/CPAN/DeferredCode.pm"},"CPAN::Author":{"version":5.5002,"file":"lib/CPAN/Author.pm"},"CPAN::HTTP::Client":{"version":1.9602,"file":"lib/CPAN/HTTP/Client.pm"},"CPAN::Distroprefs::Pref":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Bundle":{"file":"lib/CPAN/Bundle.pm","version":5.5005},"App::Cpan":{"version":1.678,"file":"lib/App/Cpan.pm"},"CPAN::Shell":{"file":"lib/CPAN/Shell.pm","version":5.5009},"CPAN::Exception::blocked_urllist":{"version":1.001,"file":"lib/CPAN/Exception/blocked_urllist.pm"},"CPAN::URL":{"version":5.5,"file":"lib/CPAN/URL.pm"},"CPAN::Queue":{"version":5.5003,"file":"lib/CPAN/Queue.pm"},"CPAN::Module":{"version":5.5003,"file":"lib/CPAN/Module.pm"},"CPAN::Admin":{"file":"lib/CPAN/Admin.pm","version":5.501},"CPAN::InfoObj":{"file":"lib/CPAN/InfoObj.pm","version":5.5},"CPAN::Distroprefs::Iterator":{"version":6.0001,"file":"lib/CPAN/Distroprefs.pm"},"CPAN::Distroprefs::Result::Fatal":{"file":"lib/CPAN/Distroprefs.pm","version":6.0001},"CPAN::Queue::Item":{"version":5.5003,"file":"lib/CPAN/Queue.pm"},"CPAN::Exception::RecursiveDependency::na":{"file":"lib/CPAN/Exception/RecursiveDependency.pm","version":5.5001},"CPAN::FirstTime":{"version":5.5317,"file":"lib/CPAN/FirstTime.pm"},"CPAN::Prompt":{"file":"lib/CPAN/Prompt.pm","version":5.5},"CPAN::FTP":{"file":"lib/CPAN/FTP.pm","version":5.5016},"CPAN::HTTP::Credentials":{"file":"lib/CPAN/HTTP/Credentials.pm","version":1.9601},"CPAN::Version":{"version":5.5003,"file":"lib/CPAN/Version.pm"},"CPAN::Mirrors":{"version":2.27,"file":"lib/CPAN/Mirrors.pm"},"CPAN::Tarzip":{"file":"lib/CPAN/Tarzip.pm","version":5.5013},"CPAN":{"file":"lib/CPAN.pm","version":2.38},"CPAN::Plugin::Specfile":{"file":"lib/CPAN/Plugin/Specfile.pm","version":0.02},"CPAN::Mirrored::By":{"file":"lib/CPAN/Mirrors.pm","version":2.27},"CPAN::Plugin":{"version":0.97,"file":"lib/CPAN/Plugin.pm"}},"dist":"CPAN-2.38","name":"CPAN","target":"CPAN","pathname":"A/AN/ANDK/CPAN-2.38.tar.gz","version":2.38}������������������������x86_64-linux-thread-multi/.meta/Types-Serialiser-1.01/MYMETA.json�����������������������������������0000444�����������������00000001644�15111204742�0017710 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150001, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Types-Serialiser", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "common::sense" : "0" } } }, "release_status" : "stable", "version" : "1.01", "x_serialization_backend" : "JSON::PP version 4.06" } ��������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/Types-Serialiser-1.01/install.json����������������������������������0000444�����������������00000000660�15111204742�0020357 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"target":"Types::Serialiser","name":"Types::Serialiser","version":1.01,"pathname":"M/ML/MLEHMANN/Types-Serialiser-1.01.tar.gz","provides":{"Types::Serialiser::Error":{"file":"Serialiser.pm","version":1.01},"Types::Serialiser":{"version":1.01,"file":"Serialiser.pm"},"JSON::PP::Boolean":{"file":"Serialiser.pm","version":1.01},"Types::Serialiser::BooleanBase":{"version":1.01,"file":"Serialiser.pm"}},"dist":"Types-Serialiser-1.01"}��������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/Switch-2.17/MYMETA.json���������������������������������������������0000444�����������������00000002333�15111204742�0015751 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "A switch statement for Perl, do not use if you can use given/when", "author" : [ "Damian Conway", "Rafael Garcia-Suarez", "Alexandr Ciornii" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 6.78, CPAN::Meta::Converter version 2.131490, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Switch", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Filter::Util::Call" : "0", "Text::Balanced" : "2", "if" : "0", "perl" : "5.005" } } }, "release_status" : "stable", "resources" : { "repository" : { "url" : "http://github.com/chorny/Switch" } }, "version" : "2.17", "x_serialization_backend" : "JSON::PP version 4.06" } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/Switch-2.17/install.json��������������������������������������������0000444�����������������00000000255�15111204742�0016424 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"dist":"Switch-2.17","pathname":"C/CH/CHORNY/Switch-2.17.tar.gz","provides":{"Switch":{"file":"Switch.pm","version":2.17}},"name":"Switch","version":2.17,"target":"Switch"}���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/CPAN-Meta-Requirements-2.143/MYMETA.json����������������������������0000444�����������������00000014573�15111204742�0020667 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "a set of version requirements for a CPAN dist", "author" : [ "David Golden <dagolden@cpan.org>", "Ricardo Signes <rjbs@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.030, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "CPAN-Meta-Requirements", "no_index" : { "directory" : [ "corpus", "examples", "t", "xt" ], "package" : [ "DB" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.17" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::Authority" : "0", "Dist::Zilla::Plugin::AutoPrereqs" : "0", "Dist::Zilla::Plugin::BumpVersionAfterRelease" : "0", "Dist::Zilla::Plugin::CPANFile" : "0", "Dist::Zilla::Plugin::CheckChangesHasContent" : "0", "Dist::Zilla::Plugin::CheckMetaResources" : "0", "Dist::Zilla::Plugin::CheckPrereqsIndexed" : "0", "Dist::Zilla::Plugin::ConfirmRelease" : "0", "Dist::Zilla::Plugin::CopyFilesFromBuild::Filtered" : "0", "Dist::Zilla::Plugin::ExecDir" : "0", "Dist::Zilla::Plugin::Git::Check" : "0", "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch" : "0", "Dist::Zilla::Plugin::Git::Commit" : "0", "Dist::Zilla::Plugin::Git::Contributors" : "0", "Dist::Zilla::Plugin::Git::GatherDir" : "0", "Dist::Zilla::Plugin::Git::Push" : "0", "Dist::Zilla::Plugin::Git::Tag" : "0", "Dist::Zilla::Plugin::GithubMeta" : "0", "Dist::Zilla::Plugin::InsertCopyright" : "0", "Dist::Zilla::Plugin::License" : "0", "Dist::Zilla::Plugin::MakeMaker" : "0", "Dist::Zilla::Plugin::MakeMaker::Highlander" : "0.003", "Dist::Zilla::Plugin::Manifest" : "0", "Dist::Zilla::Plugin::ManifestSkip" : "0", "Dist::Zilla::Plugin::MetaJSON" : "0", "Dist::Zilla::Plugin::MetaNoIndex" : "0", "Dist::Zilla::Plugin::MetaProvides::Package" : "0", "Dist::Zilla::Plugin::MetaTests" : "0", "Dist::Zilla::Plugin::MetaYAML" : "0", "Dist::Zilla::Plugin::MinimumPerl" : "0", "Dist::Zilla::Plugin::NextRelease" : "0", "Dist::Zilla::Plugin::OnlyCorePrereqs" : "0.014", "Dist::Zilla::Plugin::Pod2Readme" : "0", "Dist::Zilla::Plugin::PodCoverageTests" : "0", "Dist::Zilla::Plugin::PodSyntaxTests" : "0", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::Plugin::Prereqs::AuthorDeps" : "0", "Dist::Zilla::Plugin::PromptIfStale" : "0", "Dist::Zilla::Plugin::PruneCruft" : "0", "Dist::Zilla::Plugin::RewriteVersion" : "0", "Dist::Zilla::Plugin::RunExtraTests" : "0", "Dist::Zilla::Plugin::ShareDir" : "0", "Dist::Zilla::Plugin::SurgicalPodWeaver" : "0", "Dist::Zilla::Plugin::Test::Compile" : "0", "Dist::Zilla::Plugin::Test::MinimumVersion" : "0", "Dist::Zilla::Plugin::Test::Perl::Critic" : "0", "Dist::Zilla::Plugin::Test::PodSpelling" : "0", "Dist::Zilla::Plugin::Test::Portability" : "0", "Dist::Zilla::Plugin::Test::ReportPrereqs" : "0", "Dist::Zilla::Plugin::Test::Version" : "0", "Dist::Zilla::Plugin::TestRelease" : "0", "Dist::Zilla::Plugin::UploadToCPAN" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Pod::Coverage::TrustPod" : "0", "Pod::Weaver::PluginBundle::DAGOLDEN" : "0", "Pod::Wordlist" : "0", "Software::License::Perl_5" : "0", "Test::CPAN::Meta" : "0", "Test::MinimumVersion" : "0", "Test::More" : "0", "Test::Perl::Critic" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Portability::Files" : "0", "Test::Spelling" : "0.12", "Test::Version" : "1" } }, "runtime" : { "requires" : { "B" : "0", "Carp" : "0", "perl" : "5.010000", "strict" : "0", "version" : "0.88", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "Test::More" : "0.88", "version" : "0.88" } } }, "provides" : { "CPAN::Meta::Requirements" : { "file" : "lib/CPAN/Meta/Requirements.pm", "version" : "2.143" }, "CPAN::Meta::Requirements::Range" : { "file" : "lib/CPAN/Meta/Requirements/Range.pm", "version" : "2.143" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements/issues" }, "homepage" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements", "repository" : { "type" : "git", "url" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements.git", "web" : "https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements" } }, "version" : "2.143", "x_authority" : "cpan:DAGOLDEN", "x_contributors" : [ "Ed J <mohawk2@users.noreply.github.com>", "Graham Knop <haarg@haarg.org>", "Karen Etheridge <ether@cpan.org>", "Leon Timmermans <fawaka@gmail.com>", "Paul Howarth <paul@city-fan.org>", "Ricardo Signes <rjbs@semiotic.systems>", "robario <webmaster@robario.com>", "Tatsuhiko Miyagawa <miyagawa@bulknews.net>", "Tatsuhiko Miyagawa <miyagawa@gmail.com>" ], "x_generated_by_perl" : "v5.37.10", "x_serialization_backend" : "JSON::PP version 4.06", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } �������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/CPAN-Meta-Requirements-2.143/install.json���������������������������0000444�����������������00000000600�15111204742�0021323 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"name":"CPAN::Meta::Requirements","target":"CPAN::Meta::Requirements","pathname":"R/RJ/RJBS/CPAN-Meta-Requirements-2.143.tar.gz","version":"2.143","provides":{"CPAN::Meta::Requirements::Range":{"file":"lib/CPAN/Meta/Requirements/Range.pm","version":"2.143"},"CPAN::Meta::Requirements":{"file":"lib/CPAN/Meta/Requirements.pm","version":"2.143"}},"dist":"CPAN-Meta-Requirements-2.143"}��������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/Scope-Guard-0.21/MYMETA.json����������������������������������������0000444�����������������00000002337�15111204742�0016616 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "abstract" : "lexically-scoped resource management", "author" : [ "chocolateboy <chocolate@cpan.org>" ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.0401, CPAN::Meta::Converter version 2.150005, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Scope-Guard", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "Test::More" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "perl" : "5.006001" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/chocolateboy/Scope-Guard/issues" }, "repository" : { "url" : "https://github.com/chocolateboy/Scope-Guard" } }, "version" : "0.21", "x_serialization_backend" : "JSON::PP version 4.06", "x_test_requires" : { "Test::More" : 0 } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x86_64-linux-thread-multi/.meta/Scope-Guard-0.21/install.json���������������������������������������0000444�����������������00000000325�15111204742�0017263 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"name":"Scope::Guard","target":"Scope::Guard","dist":"Scope-Guard-0.21","version":0.21,"pathname":"C/CH/CHOCOLATE/Scope-Guard-0.21.tar.gz","provides":{"Scope::Guard":{"version":0.21,"file":"lib/Scope/Guard.pm"}}}�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Switch.pm�������������������������������������������������������������������������������������������0000444�����������������00000070215�15111204742�0006343 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Switch; use 5.005; use strict; use vars qw($VERSION); use Carp; use if $] >= 5.011, 'deprecate'; $VERSION = '2.17'; # LOAD FILTERING MODULE... use Filter::Util::Call; sub __(); # CATCH ATTEMPTS TO CALL case OUTSIDE THE SCOPE OF ANY switch $::_S_W_I_T_C_H = sub { croak "case/when statement not in switch/given block" }; my $offset; my $fallthrough; my ($Perl5, $Perl6) = (0,0); sub import { $fallthrough = grep /\bfallthrough\b/, @_; $offset = (caller)[2]+1; filter_add({}) unless @_>1 && $_[1] eq 'noimport'; my $pkg = caller; no strict 'refs'; for ( qw( on_defined on_exists ) ) { *{"${pkg}::$_"} = \&$_; } *{"${pkg}::__"} = \&__ if grep /__/, @_; $Perl6 = 1 if grep(/Perl\s*6/i, @_); $Perl5 = 1 if grep(/Perl\s*5/i, @_) || !grep(/Perl\s*6/i, @_); 1; } sub unimport { filter_del() } sub filter { my($self) = @_ ; local $Switch::file = (caller)[1]; my $status = 1; $status = filter_read(1_000_000); return $status if $status<0; $_ = filter_blocks($_,$offset); $_ = "# line $offset\n" . $_ if $offset; undef $offset; return $status; } use Text::Balanced ':ALL'; sub line { my ($pretext,$offset) = @_; ($pretext=~tr/\n/\n/)+($offset||0); } sub is_block { local $SIG{__WARN__}=sub{die$@}; local $^W=1; my $ishash = defined eval 'my $hr='.$_[0]; undef $@; return !$ishash; } my $pod_or_DATA = qr/ ^=[A-Za-z] .*? ^=cut (?![A-Za-z]) .*? $ | ^__(DATA|END)__\n.* /smx; my $casecounter = 1; sub filter_blocks { my ($source, $line) = @_; return $source unless $Perl5 && $source =~ /case|switch/ || $Perl6 && $source =~ /when|given|default/; pos $source = 0; my $text = ""; component: while (pos $source < length $source) { if ($source =~ m/(\G\s*use\s+Switch\b)/gc) { $text .= q{use Switch 'noimport'}; next component; } my @pos = Text::Balanced::_match_quotelike(\$source,qr/\s*/,1,0); if (defined $pos[0]) { my $pre = substr($source,$pos[0],$pos[1]); # matched prefix my $iEol; if( substr($source,$pos[4],$pos[5]) eq '/' && # 1st delimiter substr($source,$pos[2],$pos[3]) eq '' && # no op like 'm' index( substr($source,$pos[16],$pos[17]), 'x' ) == -1 && # no //x ($iEol = index( $source, "\n", $pos[4] )) > 0 && $iEol < $pos[8] ){ # embedded newlines # If this is a pattern, it isn't compatible with Switch. Backup past 1st '/'. pos( $source ) = $pos[6]; $text .= $pre . substr($source,$pos[2],$pos[6]-$pos[2]); } else { $text .= $pre . substr($source,$pos[2],$pos[18]-$pos[2]); } next component; } if ($source =~ m/(\G\s*$pod_or_DATA)/gc) { $text .= $1; next component; } @pos = Text::Balanced::_match_variable(\$source,qr/\s*/); if (defined $pos[0]) { $text .= " " if $pos[0] < $pos[2]; $text .= substr($source,$pos[0],$pos[4]-$pos[0]); next component; } if ($Perl5 && $source =~ m/\G(\n*)(\s*)(switch)\b(?=\s*[(])/gc || $Perl6 && $source =~ m/\G(\n*)(\s*)(given)\b(?=\s*[(])/gc || $Perl6 && $source =~ m/\G(\n*)(\s*)(given)\b(.*)(?=\{)/gc) { my $keyword = $3; my $arg = $4; $text .= $1.$2.'S_W_I_T_C_H: while (1) '; unless ($arg) { @pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\(/,qr/\)/,qr/[[{(<]/,qr/[]})>]/,undef) or do { die "Bad $keyword statement (problem in the parentheses?) near $Switch::file line ", line(substr($source,0,pos $source),$line), "\n"; }; $arg = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); } $arg =~ s {^\s*[(]\s*%} { ( \\\%} || $arg =~ s {^\s*[(]\s*m\b} { ( qr} || $arg =~ s {^\s*[(]\s*/} { ( qr/} || $arg =~ s {^\s*[(]\s*qw} { ( \\qw}; @pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef) or do { die "Bad $keyword statement (problem in the code block?) near $Switch::file line ", line(substr($source,0, pos $source), $line), "\n"; }; my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); $code =~ s/{/{ local \$::_S_W_I_T_C_H; Switch::switch($arg);/; $text .= $code . 'continue {last}'; next component; } elsif ($Perl5 && $source =~ m/\G(\s*)(case\b)(?!\s*=>)/gc || $Perl6 && $source =~ m/\G(\s*)(when\b)(?!\s*=>)/gc || $Perl6 && $source =~ m/\G(\s*)(default\b)(?=\s*\{)/gc) { my $keyword = $2; $text .= $1 . ($keyword eq "default" ? "if (1)" : "if (Switch::case"); if ($keyword eq "default") { # Nothing to do } elsif (@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef)) { my $code = substr($source,$pos[0],$pos[4]-$pos[0]); $text .= " " if $pos[0] < $pos[2]; $text .= "sub " if is_block $code; $text .= filter_blocks($code,line(substr($source,0,$pos[0]),$line)) . ")"; } elsif (@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/[[(]/,qr/[])]/,qr/[[({]/,qr/[])}]/,undef)) { my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); $code =~ s {^\s*[(]\s*%} { ( \\\%} || $code =~ s {^\s*[(]\s*m\b} { ( qr} || $code =~ s {^\s*[(]\s*/} { ( qr/} || $code =~ s {^\s*[(]\s*qw} { ( \\qw}; $text .= " " if $pos[0] < $pos[2]; $text .= "$code)"; } elsif ($Perl6 && do{@pos = Text::Balanced::_match_variable(\$source,qr/\s*/)}) { my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); $code =~ s {^\s*%} { \%} || $code =~ s {^\s*@} { \@}; $text .= " " if $pos[0] < $pos[2]; $text .= "$code)"; } elsif ( @pos = Text::Balanced::_match_quotelike(\$source,qr/\s*/,1,0)) { my $code = substr($source,$pos[2],$pos[18]-$pos[2]); $code = filter_blocks($code,line(substr($source,0,$pos[2]),$line)); $code =~ s {^\s*m} { qr} || $code =~ s {^\s*/} { qr/} || $code =~ s {^\s*qw} { \\qw}; $text .= " " if $pos[0] < $pos[2]; $text .= "$code)"; } elsif ($Perl5 && $source =~ m/\G\s*(([^\$\@{])[^\$\@{]*)(?=\s*{)/gc || $Perl6 && $source =~ m/\G\s*([^;{]*)()/gc) { my $code = filter_blocks($1,line(substr($source,0,pos $source),$line)); $text .= ' \\' if $2 eq '%'; $text .= " $code)"; } else { die "Bad $keyword statement (invalid $keyword value?) near $Switch::file line ", line(substr($source,0,pos $source), $line), "\n"; } die "Missing opening brace or semi-colon after 'when' value near $Switch::file line ", line(substr($source,0,pos $source), $line), "\n" unless !$Perl6 || $source =~ m/\G(\s*)(?=;|\{)/gc; do{@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef)} or do { if ($source =~ m/\G\s*(?=([};]|\Z))/gc) { $casecounter++; next component; } die "Bad $keyword statement (problem in the code block?) near $Switch::file line ", line(substr($source,0,pos $source),$line), "\n"; }; my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line)); $code =~ s/}(?=\s*\Z)/;last S_W_I_T_C_H }/ unless $fallthrough; $text .= "{ while (1) $code continue { goto C_A_S_E_$casecounter } last S_W_I_T_C_H; C_A_S_E_$casecounter: }"; $casecounter++; next component; } $source =~ m/\G(\s*(-[sm]\s+|\w+|#.*\n|\W))/gc; $text .= $1; } $text; } sub in { my ($x,$y) = @_; my @numy; for my $nextx ( @$x ) { my $numx = ref($nextx) || defined $nextx && (~$nextx&$nextx) eq 0; for my $j ( 0..$#$y ) { my $nexty = $y->[$j]; push @numy, ref($nexty) || defined $nexty && (~$nexty&$nexty) eq 0 if @numy <= $j; return 1 if $numx && $numy[$j] && $nextx==$nexty || $nextx eq $nexty; } } return ""; } sub on_exists { my $ref = @_==1 && ref($_[0]) eq 'HASH' ? $_[0] : { @_ }; [ keys %$ref ] } sub on_defined { my $ref = @_==1 && ref($_[0]) eq 'HASH' ? $_[0] : { @_ }; [ grep { defined $ref->{$_} } keys %$ref ] } sub switch(;$) { my ($s_val) = @_ ? $_[0] : $_; my $s_ref = ref $s_val; if ($s_ref eq 'CODE') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; return $s_val == $c_val if ref $c_val eq 'CODE'; return $s_val->(@$c_val) if ref $c_val eq 'ARRAY'; return $s_val->($c_val); }; } elsif ($s_ref eq "" && defined $s_val && (~$s_val&$s_val) eq 0) # NUMERIC SCALAR { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return $s_val == $c_val if $c_ref eq "" && defined $c_val && (~$c_val&$c_val) eq 0; return $s_val eq $c_val if $c_ref eq ""; return in([$s_val],$c_val) if $c_ref eq 'ARRAY'; return $c_val->($s_val) if $c_ref eq 'CODE'; return $c_val->call($s_val) if $c_ref eq 'Switch'; return scalar $s_val=~/$c_val/ if $c_ref eq 'Regexp'; return scalar $c_val->{$s_val} if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq "") # STRING SCALAR { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return $s_val eq $c_val if $c_ref eq ""; return in([$s_val],$c_val) if $c_ref eq 'ARRAY'; return $c_val->($s_val) if $c_ref eq 'CODE'; return $c_val->call($s_val) if $c_ref eq 'Switch'; return scalar $s_val=~/$c_val/ if $c_ref eq 'Regexp'; return scalar $c_val->{$s_val} if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq 'ARRAY') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return in($s_val,[$c_val]) if $c_ref eq ""; return in($s_val,$c_val) if $c_ref eq 'ARRAY'; return $c_val->(@$s_val) if $c_ref eq 'CODE'; return $c_val->call(@$s_val) if $c_ref eq 'Switch'; return scalar grep {$_=~/$c_val/} @$s_val if $c_ref eq 'Regexp'; return scalar grep {$c_val->{$_}} @$s_val if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq 'Regexp') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return $c_val=~/s_val/ if $c_ref eq ""; return scalar grep {$_=~/s_val/} @$c_val if $c_ref eq 'ARRAY'; return $c_val->($s_val) if $c_ref eq 'CODE'; return $c_val->call($s_val) if $c_ref eq 'Switch'; return $s_val eq $c_val if $c_ref eq 'Regexp'; return grep {$_=~/$s_val/ && $c_val->{$_}} keys %$c_val if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq 'HASH') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; my $c_ref = ref $c_val; return $s_val->{$c_val} if $c_ref eq ""; return scalar grep {$s_val->{$_}} @$c_val if $c_ref eq 'ARRAY'; return $c_val->($s_val) if $c_ref eq 'CODE'; return $c_val->call($s_val) if $c_ref eq 'Switch'; return grep {$_=~/$c_val/ && $s_val->{"$_"}} keys %$s_val if $c_ref eq 'Regexp'; return $s_val==$c_val if $c_ref eq 'HASH'; return; }; } elsif ($s_ref eq 'Switch') { $::_S_W_I_T_C_H = sub { my $c_val = $_[0]; return $s_val == $c_val if ref $c_val eq 'Switch'; return $s_val->call(@$c_val) if ref $c_val eq 'ARRAY'; return $s_val->call($c_val); }; } else { croak "Cannot switch on $s_ref"; } return 1; } sub case($) { local $SIG{__WARN__} = \&carp; $::_S_W_I_T_C_H->(@_); } # IMPLEMENT __ my $placeholder = bless { arity=>1, impl=>sub{$_[1+$_[0]]} }; sub __() { $placeholder } sub __arg($) { my $index = $_[0]+1; bless { arity=>0, impl=>sub{$_[$index]} }; } sub hosub(&@) { # WRITE THIS } sub call { my ($self,@args) = @_; return $self->{impl}->(0,@args); } sub meta_bop(&) { my ($op) = @_; sub { my ($left, $right, $reversed) = @_; ($right,$left) = @_ if $reversed; my $rop = ref $right eq 'Switch' ? $right : bless { arity=>0, impl=>sub{$right} }; my $lop = ref $left eq 'Switch' ? $left : bless { arity=>0, impl=>sub{$left} }; my $arity = $lop->{arity} + $rop->{arity}; return bless { arity => $arity, impl => sub { my $start = shift; return $op->($lop->{impl}->($start,@_), $rop->{impl}->($start+$lop->{arity},@_)); } }; }; } sub meta_uop(&) { my ($op) = @_; sub { my ($left) = @_; my $lop = ref $left eq 'Switch' ? $left : bless { arity=>0, impl=>sub{$left} }; my $arity = $lop->{arity}; return bless { arity => $arity, impl => sub { $op->($lop->{impl}->(@_)) } }; }; } use overload "+" => meta_bop {$_[0] + $_[1]}, "-" => meta_bop {$_[0] - $_[1]}, "*" => meta_bop {$_[0] * $_[1]}, "/" => meta_bop {$_[0] / $_[1]}, "%" => meta_bop {$_[0] % $_[1]}, "**" => meta_bop {$_[0] ** $_[1]}, "<<" => meta_bop {$_[0] << $_[1]}, ">>" => meta_bop {$_[0] >> $_[1]}, "x" => meta_bop {$_[0] x $_[1]}, "." => meta_bop {$_[0] . $_[1]}, "<" => meta_bop {$_[0] < $_[1]}, "<=" => meta_bop {$_[0] <= $_[1]}, ">" => meta_bop {$_[0] > $_[1]}, ">=" => meta_bop {$_[0] >= $_[1]}, "==" => meta_bop {$_[0] == $_[1]}, "!=" => meta_bop {$_[0] != $_[1]}, "<=>" => meta_bop {$_[0] <=> $_[1]}, "lt" => meta_bop {$_[0] lt $_[1]}, "le" => meta_bop {$_[0] le $_[1]}, "gt" => meta_bop {$_[0] gt $_[1]}, "ge" => meta_bop {$_[0] ge $_[1]}, "eq" => meta_bop {$_[0] eq $_[1]}, "ne" => meta_bop {$_[0] ne $_[1]}, "cmp" => meta_bop {$_[0] cmp $_[1]}, "\&" => meta_bop {$_[0] & $_[1]}, "^" => meta_bop {$_[0] ^ $_[1]}, "|" => meta_bop {$_[0] | $_[1]}, "atan2" => meta_bop {atan2 $_[0], $_[1]}, "neg" => meta_uop {-$_[0]}, "!" => meta_uop {!$_[0]}, "~" => meta_uop {~$_[0]}, "cos" => meta_uop {cos $_[0]}, "sin" => meta_uop {sin $_[0]}, "exp" => meta_uop {exp $_[0]}, "abs" => meta_uop {abs $_[0]}, "log" => meta_uop {log $_[0]}, "sqrt" => meta_uop {sqrt $_[0]}, "bool" => sub { croak "Can't use && or || in expression containing __" }, # "&()" => sub { $_[0]->{impl} }, # "||" => meta_bop {$_[0] || $_[1]}, # "&&" => meta_bop {$_[0] && $_[1]}, # fallback => 1, ; 1; __END__ =head1 NAME Switch - A switch statement for Perl, do not use if you can use given/when =head1 SYNOPSIS use Switch; switch ($val) { case 1 { print "number 1" } case "a" { print "string a" } case [1..10,42] { print "number in list" } case (\@array) { print "number in list" } case /\w+/ { print "pattern" } case qr/\w+/ { print "pattern" } case (\%hash) { print "entry in hash" } case (\&sub) { print "arg to subroutine" } else { print "previous case not true" } } =head1 BACKGROUND [Skip ahead to L<"DESCRIPTION"> if you don't care about the whys and wherefores of this control structure] In seeking to devise a "Swiss Army" case mechanism suitable for Perl, it is useful to generalize this notion of distributed conditional testing as far as possible. Specifically, the concept of "matching" between the switch value and the various case values need not be restricted to numeric (or string or referential) equality, as it is in other languages. Indeed, as Table 1 illustrates, Perl offers at least eighteen different ways in which two values could generate a match. Table 1: Matching a switch value ($s) with a case value ($c) Switch Case Type of Match Implied Matching Code Value Value ====== ===== ===================== ============= number same numeric or referential match if $s == $c; or ref equality object method result of method call match if $s->$c(); ref name match if defined $s->$c(); or ref other other string equality match if $s eq $c; non-ref non-ref scalar scalar string regexp pattern match match if $s =~ /$c/; array scalar array entry existence match if 0<=$c && $c<@$s; ref array entry definition match if defined $s->[$c]; array entry truth match if $s->[$c]; array array array intersection match if intersects(@$s, @$c); ref ref (apply this table to all pairs of elements $s->[$i] and $c->[$j]) array regexp array grep match if grep /$c/, @$s; ref hash scalar hash entry existence match if exists $s->{$c}; ref hash entry definition match if defined $s->{$c}; hash entry truth match if $s->{$c}; hash regexp hash grep match if grep /$c/, keys %$s; ref sub scalar return value defn match if defined $s->($c); ref return value truth match if $s->($c); sub array return value defn match if defined $s->(@$c); ref ref return value truth match if $s->(@$c); In reality, Table 1 covers 31 alternatives, because only the equality and intersection tests are commutative; in all other cases, the roles of the C<$s> and C<$c> variables could be reversed to produce a different test. For example, instead of testing a single hash for the existence of a series of keys (C<match if exists $s-E<gt>{$c}>), one could test for the existence of a single key in a series of hashes (C<match if exists $c-E<gt>{$s}>). =head1 DESCRIPTION The Switch.pm module implements a generalized case mechanism that covers most (but not all) of the numerous possible combinations of switch and case values described above. The module augments the standard Perl syntax with two new control statements: C<switch> and C<case>. The C<switch> statement takes a single scalar argument of any type, specified in parentheses. C<switch> stores this value as the current switch value in a (localized) control variable. The value is followed by a block which may contain one or more Perl statements (including the C<case> statement described below). The block is unconditionally executed once the switch value has been cached. A C<case> statement takes a single scalar argument (in mandatory parentheses if it's a variable; otherwise the parens are optional) and selects the appropriate type of matching between that argument and the current switch value. The type of matching used is determined by the respective types of the switch value and the C<case> argument, as specified in Table 1. If the match is successful, the mandatory block associated with the C<case> statement is executed. In most other respects, the C<case> statement is semantically identical to an C<if> statement. For example, it can be followed by an C<else> clause, and can be used as a postfix statement qualifier. However, when a C<case> block has been executed control is automatically transferred to the statement after the immediately enclosing C<switch> block, rather than to the next statement within the block. In other words, the success of any C<case> statement prevents other cases in the same scope from executing. But see L<"Allowing fall-through"> below. Together these two new statements provide a fully generalized case mechanism: use Switch; # AND LATER... %special = ( woohoo => 1, d'oh => 1 ); while (<>) { chomp; switch ($_) { case (%special) { print "homer\n"; } # if $special{$_} case /[a-z]/i { print "alpha\n"; } # if $_ =~ /a-z/i case [1..9] { print "small num\n"; } # if $_ in [1..9] case { $_[0] >= 10 } { print "big num\n"; } # if $_ >= 10 print "must be punctuation\n" case /\W/; # if $_ ~= /\W/ } } Note that C<switch>es can be nested within C<case> (or any other) blocks, and a series of C<case> statements can try different types of matches -- hash membership, pattern match, array intersection, simple equality, etc. -- against the same switch value. The use of intersection tests against an array reference is particularly useful for aggregating integral cases: sub classify_digit { switch ($_[0]) { case 0 { return 'zero' } case [2,4,6,8] { return 'even' } case [1,3,5,7,9] { return 'odd' } case /[A-F]/i { return 'hex' } } } =head2 Allowing fall-through Fall-though (trying another case after one has already succeeded) is usually a Bad Idea in a switch statement. However, this is Perl, not a police state, so there I<is> a way to do it, if you must. If a C<case> block executes an untargeted C<next>, control is immediately transferred to the statement I<after> the C<case> statement (i.e. usually another case), rather than out of the surrounding C<switch> block. For example: switch ($val) { case 1 { handle_num_1(); next } # and try next case... case "1" { handle_str_1(); next } # and try next case... case [0..9] { handle_num_any(); } # and we're done case /\d/ { handle_dig_any(); next } # and try next case... case /.*/ { handle_str_any(); next } # and try next case... } If $val held the number C<1>, the above C<switch> block would call the first three C<handle_...> subroutines, jumping to the next case test each time it encountered a C<next>. After the third C<case> block was executed, control would jump to the end of the enclosing C<switch> block. On the other hand, if $val held C<10>, then only the last two C<handle_...> subroutines would be called. Note that this mechanism allows the notion of I<conditional fall-through>. For example: switch ($val) { case [0..9] { handle_num_any(); next if $val < 7; } case /\d/ { handle_dig_any(); } } If an untargeted C<last> statement is executed in a case block, this immediately transfers control out of the enclosing C<switch> block (in other words, there is an implicit C<last> at the end of each normal C<case> block). Thus the previous example could also have been written: switch ($val) { case [0..9] { handle_num_any(); last if $val >= 7; next; } case /\d/ { handle_dig_any(); } } =head2 Automating fall-through In situations where case fall-through should be the norm, rather than an exception, an endless succession of terminal C<next>s is tedious and ugly. Hence, it is possible to reverse the default behaviour by specifying the string "fallthrough" when importing the module. For example, the following code is equivalent to the first example in L<"Allowing fall-through">: use Switch 'fallthrough'; switch ($val) { case 1 { handle_num_1(); } case "1" { handle_str_1(); } case [0..9] { handle_num_any(); last } case /\d/ { handle_dig_any(); } case /.*/ { handle_str_any(); } } Note the explicit use of a C<last> to preserve the non-fall-through behaviour of the third case. =head2 Alternative syntax Perl 6 will provide a built-in switch statement with essentially the same semantics as those offered by Switch.pm, but with a different pair of keywords. In Perl 6 C<switch> will be spelled C<given>, and C<case> will be pronounced C<when>. In addition, the C<when> statement will not require switch or case values to be parenthesized. This future syntax is also (largely) available via the Switch.pm module, by importing it with the argument C<"Perl6">. For example: use Switch 'Perl6'; given ($val) { when 1 { handle_num_1(); } when ($str1) { handle_str_1(); } when [0..9] { handle_num_any(); last } when /\d/ { handle_dig_any(); } when /.*/ { handle_str_any(); } default { handle anything else; } } Note that scalars still need to be parenthesized, since they would be ambiguous in Perl 5. Note too that you can mix and match both syntaxes by importing the module with: use Switch 'Perl5', 'Perl6'; =head2 Higher-order Operations One situation in which C<switch> and C<case> do not provide a good substitute for a cascaded C<if>, is where a switch value needs to be tested against a series of conditions. For example: sub beverage { switch (shift) { case { $_[0] < 10 } { return 'milk' } case { $_[0] < 20 } { return 'coke' } case { $_[0] < 30 } { return 'beer' } case { $_[0] < 40 } { return 'wine' } case { $_[0] < 50 } { return 'malt' } case { $_[0] < 60 } { return 'Moet' } else { return 'milk' } } } (This is equivalent to writing C<case (sub { $_[0] < 10 })>, etc.; C<$_[0]> is the argument to the anonymous subroutine.) The need to specify each condition as a subroutine block is tiresome. To overcome this, when importing Switch.pm, a special "placeholder" subroutine named C<__> [sic] may also be imported. This subroutine converts (almost) any expression in which it appears to a reference to a higher-order function. That is, the expression: use Switch '__'; __ < 2 is equivalent to: sub { $_[0] < 2 } With C<__>, the previous ugly case statements can be rewritten: case __ < 10 { return 'milk' } case __ < 20 { return 'coke' } case __ < 30 { return 'beer' } case __ < 40 { return 'wine' } case __ < 50 { return 'malt' } case __ < 60 { return 'Moet' } else { return 'milk' } The C<__> subroutine makes extensive use of operator overloading to perform its magic. All operations involving __ are overloaded to produce an anonymous subroutine that implements a lazy version of the original operation. The only problem is that operator overloading does not allow the boolean operators C<&&> and C<||> to be overloaded. So a case statement like this: case 0 <= __ && __ < 10 { return 'digit' } doesn't act as expected, because when it is executed, it constructs two higher order subroutines and then treats the two resulting references as arguments to C<&&>: sub { 0 <= $_[0] } && sub { $_[0] < 10 } This boolean expression is inevitably true, since both references are non-false. Fortunately, the overloaded C<'bool'> operator catches this situation and flags it as an error. =head1 DEPENDENCIES The module is implemented using Filter::Util::Call and Text::Balanced and requires both these modules to be installed. =head1 AUTHOR Damian Conway (damian@conway.org). This module is now maintained by Alexandr Ciornii (alexchorny@gmail.com). Previously was maintained by Rafael Garcia-Suarez and perl5 porters. =head1 BUGS There are undoubtedly serious bugs lurking somewhere in code this funky :-) Bug reports and other feedback are most welcome. May create syntax errors in other parts of code. On perl 5.10.x may cause syntax error if "case" is present inside heredoc. In general, use given/when instead. It were introduced in perl 5.10.0. Perl 5.10.0 was released in 2007. =head1 LIMITATIONS Due to the heuristic nature of Switch.pm's source parsing, the presence of regexes with embedded newlines that are specified with raw C</.../> delimiters and don't have a modifier C<//x> are indistinguishable from code chunks beginning with the division operator C</>. As a workaround you must use C<m/.../> or C<m?...?> for such patterns. Also, the presence of regexes specified with raw C<?...?> delimiters may cause mysterious errors. The workaround is to use C<m?...?> instead. Due to the way source filters work in Perl, you can't use Switch inside an string C<eval>. May not work if sub prototypes are used (RT#33988). Regex captures in when are not available to code. If your source file is longer then 1 million characters and you have a switch statement that crosses the 1 million (or 2 million, etc.) character boundary you will get mysterious errors. The workaround is to use smaller source files. =head1 COPYRIGHT Copyright (c) 1997-2008, Damian Conway. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Canary/Stability.pm���������������������������������������������������������������������������������0000444�����������������00000015452�15111204742�0010265 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=head1 NAME Canary::Stability - canary to check perl compatibility for schmorp's modules =head1 SYNOPSIS # in Makefile.PL use Canary::Stability DISTNAME => 2001, MINIMUM_PERL_VERSION; =head1 DESCRIPTION This module is used by Schmorp's modules during configuration stage to test the installed perl for compatibility with his modules. It's not, at this stage, meant as a tool for other module authors, although in principle nothing prevents them from subscribing to the same ideas. See the F<Makefile.PL> in L<Coro> or L<AnyEvent> for usage examples. =cut package Canary::Stability; BEGIN { $VERSION = 2013; } sub sgr { # we just assume ANSI almost everywhere # red 31, yellow 33, green 32 local $| = 1; $ENV{PERL_CANARY_STABILITY_COLOUR} ne 0 and ((-t STDOUT and length $ENV{TERM}) or $ENV{PERL_CANARY_STABILITY_COLOUR}) and print "\e[$_[0]m"; } sub import { my (undef, $distname, $minvers, $minperl) = @_; $ENV{PERL_CANARY_STABILITY_DISABLE} and return; $minperl ||= 5.008002; print <<EOF; *** *** Canary::Stability COMPATIBILITY AND SUPPORT CHECK *** ================================================= *** *** Hi! *** *** I do my best to provide predictable and reliable software. *** *** However, in recent releases, P5P (who maintain perl) have been *** introducing regressions that are sometimes subtle and at other times *** catastrophic, often for personal preferences with little or no concern *** for existing code, most notably CPAN. *** *** For this reason, it has become very hard for me to maintain the level *** of reliability and support I have committed myself to in the past, at *** least with some perl versions: I simply can't keep up working around new *** bugs or gratituous incompatibilities, and in turn you might suffer from *** unanticipated problems. *** *** Therefore I have introduced a support and compatibility check, the results *** of which follow below, together with a FAQ and some recommendations. *** *** This check is just to let you know that there might be a risk, so you can *** make judgement calls on how to proceed - it will not keep the module from *** installing or working. *** EOF if ($minvers > $VERSION) { sgr 33; print <<EOF; *** The stability canary says: (nothing, it died of old age). *** *** Your Canary::Stability module (used by $distname) is too old. *** This is not a fatal problem - while you might want to upgrade to version *** $minvers (currently installed version: $VERSION) to get better support *** status testing, you might also not want to care at all, and all will *** be well as long $distname works well enough for you, as the stability *** canary is only used when installing the distribution. *** EOF } elsif ($] < $minperl) { sgr 33; print <<EOF; *** The stability canary says: chirp (it seems concerned about something). *** *** Your perl version ($]) is older than the $distname distribution *** likes ($minperl). This is not a fatal problem - the module might work *** well with your version of perl, but it does mean the author likely *** won't do anything to make it work if it breaks. *** EOF if ($ENV{AUTOMATED_TESTING}) { print <<EOF; *** Since this is an AUTOMATED_TESTING environment, the stability canary *** decided to fail cleanly here, rather than to generate a false test *** result. *** EOF exit 0; } } elsif (defined $Internals::StabilityBranchVersion) { # note to people studying this modules sources: # the above test is not considered a clean or stable way to # test for the stability branch. sgr 32; print <<EOF; *** The stability canary says: chirp! chirp! (it seems to be quite excited) *** *** It seems you are running schmorp's stability branch of perl. *** All should be well, and if it isn't, you should report this as a bug *** to the $distname author. *** EOF } elsif ($] < 5.021) { #sgr 32; print <<EOF; *** The stability canary says: chirp! chirp! (it seems to be quite happy) *** *** Your version of perl ($]) is quite supported by $distname, nothing *** else to be said, hope it comes in handy. *** EOF } else { sgr 31; print <<EOF; *** The stability canary says: (nothing, it was driven away by harsh weather) *** *** It seems you are running perl version $], likely the "official" or *** "standard" version. While there is nothing wrong with doing that, *** standard perl versions 5.022 and up are not supported by $distname. *** While this might be fatal, it might also be all right - if you run into *** problems, you might want to downgrade your perl or switch to the *** stability branch. *** *** If everything works fine, you can ignore this message. *** EOF sgr 0; print <<EOF; *** *** Stability canary mini-FAQ: *** *** Do I need to do anything? *** With luck, no. While some distributions are known to fail *** already, most should probably work. This message is here *** to alert you that your perl is not supported by $distname, *** and if things go wrong, you either need to downgrade, or *** sidegrade to the stability variant of your perl version, *** or simply live with the consequences. *** *** What is this canary thing? *** It's purpose is to check support status of $distname with *** respect to your perl version. *** *** What is this "stability branch"? *** It's a branch or fork of the official perl, by schmorp, to *** improve stability and compatibility with existing modules. *** *** How can I skip this prompt on automated installs? *** Set PERL_CANARY_STABILITY_NOPROMPT=1 in your environment. *** More info is in the Canary::Stability manpage. *** *** Long version of this FAQ: http://stableperl.schmorp.de/faq.html *** Stability Branch homepage: http://stableperl.schmorp.de/ *** EOF unless ($ENV{PERL_CANARY_STABILITY_NOPROMPT}) { require ExtUtils::MakeMaker; ExtUtils::MakeMaker::prompt ("Continue anyways? ", "y") =~ /^y/i or die "FATAL: User aborted configuration of $distname.\n"; } } sgr 0; } =head1 ENVIRONMENT VARIABLES =over 4 =item C<PERL_CANARY_STABILITY_NOPROMPT=1> Do not prompt the user on alert messages. =item C<PERL_CANARY_STABILITY_COLOUR=0> Disable use of colour. =item C<PERL_CANARY_STABILITY_COLOUR=1> Force use of colour. =item C<PERL_CANARY_STABILITY_DISABLE=1> Disable this modules functionality completely. =item C<AUTOMATED_TESTING=1> When this variable is set to a true value and the perl minimum version requirement is not met, the module will exit, which should skip testing under automated testing environments. This is done to avoid false failure or success reports when the chances of success are already quite low and the failures are not supported by the author. =back =head1 AUTHOR Marc Lehmann <schmorp@schmorp.de> http://software.schmorp.de/pkg/Canary-Stability.html =cut 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������CPAN.pm���������������������������������������������������������������������������������������������0000444�����������������00000444131�15111204742�0005625 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- # vim: ts=4 sts=4 sw=4: use strict; package CPAN; $CPAN::VERSION = '2.38'; $CPAN::VERSION =~ s/_//; # we need to run chdir all over and we would get at wrong libraries # there use File::Spec (); BEGIN { if (File::Spec->can("rel2abs")) { for my $inc (@INC) { $inc = File::Spec->rel2abs($inc) unless ref $inc; } } $SIG{WINCH} = 'IGNORE' if exists $SIG{WINCH}; } use CPAN::Author; use CPAN::HandleConfig; use CPAN::Version; use CPAN::Bundle; use CPAN::CacheMgr; use CPAN::Complete; use CPAN::Debug; use CPAN::Distribution; use CPAN::Distrostatus; use CPAN::FTP; use CPAN::Index 1.93; # https://rt.cpan.org/Ticket/Display.html?id=43349 use CPAN::InfoObj; use CPAN::Module; use CPAN::Prompt; use CPAN::URL; use CPAN::Queue; use CPAN::Tarzip; use CPAN::DeferredCode; use CPAN::Shell; use CPAN::LWP::UserAgent; use CPAN::Exception::RecursiveDependency; use CPAN::Exception::yaml_not_installed; use CPAN::Exception::yaml_process_error; use Carp (); use Config (); use Cwd qw(chdir); use DirHandle (); use Exporter (); use ExtUtils::MakeMaker qw(prompt); # for some unknown reason, # 5.005_04 does not work without # this use File::Basename (); use File::Copy (); use File::Find; use File::Path (); use FileHandle (); use Fcntl qw(:flock); use Safe (); use Sys::Hostname qw(hostname); use Text::ParseWords (); use Text::Wrap (); # protect against "called too early" sub find_perl (); sub anycwd (); sub _uniq; no lib "."; require Mac::BuildTools if $^O eq 'MacOS'; if ($ENV{PERL5_CPAN_IS_RUNNING} && $$ != $ENV{PERL5_CPAN_IS_RUNNING}) { $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION} ||= $ENV{PERL5_CPAN_IS_RUNNING}; my @rec = _uniq split(/,/, $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION}), $$; $ENV{PERL5_CPAN_IS_RUNNING_IN_RECURSION} = join ",", @rec; # warn "# Note: Recursive call of CPAN.pm detected\n"; my $w = sprintf "# Note: CPAN.pm is running in process %d now", pop @rec; my %sleep = ( 5 => 30, 6 => 60, 7 => 120, ); my $sleep = @rec > 7 ? 300 : ($sleep{scalar @rec}||0); my $verbose = @rec >= 4; while (@rec) { $w .= sprintf " which has been called by process %d", pop @rec; } if ($sleep) { $w .= ".\n\n# Sleeping $sleep seconds to protect other processes\n"; } if ($verbose) { warn $w; } local $| = 1; my $have_been_sleeping = 0; while ($sleep > 0) { printf "\r#%5d", --$sleep; sleep 1; ++$have_been_sleeping; } print "\n" if $have_been_sleeping; } $ENV{PERL5_CPAN_IS_RUNNING}=$$; $ENV{PERL5_CPANPLUS_IS_RUNNING}=$$; # https://rt.cpan.org/Ticket/Display.html?id=23735 END { $CPAN::End++; &cleanup; } $CPAN::Signal ||= 0; $CPAN::Frontend ||= "CPAN::Shell"; unless (@CPAN::Defaultsites) { @CPAN::Defaultsites = map { CPAN::URL->new(TEXT => $_, FROM => "DEF") } "http://www.perl.org/CPAN/", "ftp://ftp.perl.org/pub/CPAN/"; } # $CPAN::iCwd (i for initial) $CPAN::iCwd ||= CPAN::anycwd(); $CPAN::Perl ||= CPAN::find_perl(); $CPAN::Defaultdocs ||= "http://search.cpan.org/perldoc?"; $CPAN::Defaultrecent ||= "http://search.cpan.org/uploads.rdf"; $CPAN::Defaultrecent ||= "http://cpan.uwinnipeg.ca/htdocs/cpan.xml"; # our globals are getting a mess use vars qw( $AUTOLOAD $Be_Silent $CONFIG_DIRTY $Defaultdocs $Echo_readline $Frontend $GOTOSHELL $HAS_USABLE $Have_warned $MAX_RECURSION $META $RUN_DEGRADED $Signal $SQLite $Suppress_readline $VERSION $autoload_recursion $term @Defaultsites @EXPORT ); $MAX_RECURSION = 32; @CPAN::ISA = qw(CPAN::Debug Exporter); # note that these functions live in CPAN::Shell and get executed via # AUTOLOAD when called directly @EXPORT = qw( autobundle bundle clean cvs_import expand force fforce get install install_tested is_tested make mkmyconfig notest perldoc readme recent recompile report shell smoke test upgrade ); sub soft_chdir_with_alternatives ($); { $autoload_recursion ||= 0; #-> sub CPAN::AUTOLOAD ; sub AUTOLOAD { ## no critic $autoload_recursion++; my($l) = $AUTOLOAD; $l =~ s/.*:://; if ($CPAN::Signal) { warn "Refusing to autoload '$l' while signal pending"; $autoload_recursion--; return; } if ($autoload_recursion > 1) { my $fullcommand = join " ", map { "'$_'" } $l, @_; warn "Refusing to autoload $fullcommand in recursion\n"; $autoload_recursion--; return; } my(%export); @export{@EXPORT} = ''; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; if (exists $export{$l}) { CPAN::Shell->$l(@_); } else { die(qq{Unknown CPAN command "$AUTOLOAD". }. qq{Type ? for help.\n}); } $autoload_recursion--; } } { my $x = *SAVEOUT; # avoid warning open($x,">&STDOUT") or die "dup failed"; my $redir = 0; sub _redirect(@) { #die if $redir; local $_; push(@_,undef); while(defined($_=shift)) { if (s/^\s*>//){ my ($m) = s/^>// ? ">" : ""; s/\s+//; $_=shift unless length; die "no dest" unless defined; open(STDOUT,">$m$_") or die "open:$_:$!\n"; $redir=1; } elsif ( s/^\s*\|\s*// ) { my $pipe="| $_"; while(defined($_[0])){ $pipe .= ' ' . shift; } open(STDOUT,$pipe) or die "open:$pipe:$!\n"; $redir=1; } else { push(@_,$_); } } return @_; } sub _unredirect { return unless $redir; $redir = 0; ## redirect: unredirect and propagate errors. explicit close to wait for pipe. close(STDOUT); open(STDOUT,">&SAVEOUT"); die "$@" if "$@"; ## redirect: done } } sub _uniq { my(@list) = @_; my %seen; return grep { !$seen{$_}++ } @list; } #-> sub CPAN::shell ; sub shell { my($self) = @_; $Suppress_readline = ! -t STDIN unless defined $Suppress_readline; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; my $oprompt = shift || CPAN::Prompt->new; my $prompt = $oprompt; my $commandline = shift || ""; $CPAN::CurrentCommandId ||= 1; local($^W) = 1; unless ($Suppress_readline) { require Term::ReadLine; if (! $term or $term->ReadLine eq "Term::ReadLine::Stub" ) { $term = Term::ReadLine->new('CPAN Monitor'); } if ($term->ReadLine eq "Term::ReadLine::Gnu") { my $attribs = $term->Attribs; $attribs->{attempted_completion_function} = sub { &CPAN::Complete::gnu_cpl; } } else { $readline::rl_completion_function = $readline::rl_completion_function = 'CPAN::Complete::cpl'; } if (my $histfile = $CPAN::Config->{'histfile'}) {{ unless ($term->can("AddHistory")) { $CPAN::Frontend->mywarn("Terminal does not support AddHistory.\n"); unless ($CPAN::META->has_inst('Term::ReadLine::Perl')) { $CPAN::Frontend->mywarn("\nTo fix that, maybe try> install Term::ReadLine::Perl\n\n"); } last; } $META->readhist($term,$histfile); }} for ($CPAN::Config->{term_ornaments}) { # alias local $Term::ReadLine::termcap_nowarn = 1; $term->ornaments($_) if defined; } # $term->OUT is autoflushed anyway my $odef = select STDERR; $| = 1; select STDOUT; $| = 1; select $odef; } $META->checklock(); my @cwd = grep { defined $_ and length $_ } CPAN::anycwd(), File::Spec->can("tmpdir") ? File::Spec->tmpdir() : (), File::Spec->rootdir(); my $try_detect_readline; $try_detect_readline = $term->ReadLine eq "Term::ReadLine::Stub" if $term; unless ($CPAN::Config->{inhibit_startup_message}) { my $rl_avail = $Suppress_readline ? "suppressed" : ($term->ReadLine ne "Term::ReadLine::Stub") ? "enabled" : "available (maybe install Bundle::CPAN or Bundle::CPANxxl?)"; $CPAN::Frontend->myprint( sprintf qq{ cpan shell -- CPAN exploration and modules installation (v%s) Enter 'h' for help. }, $CPAN::VERSION, ) } my($continuation) = ""; my $last_term_ornaments; SHELLCOMMAND: while () { if ($Suppress_readline) { if ($Echo_readline) { $|=1; } print $prompt; last SHELLCOMMAND unless defined ($_ = <> ); if ($Echo_readline) { # backdoor: I could not find a way to record sessions print $_; } chomp; } else { last SHELLCOMMAND unless defined ($_ = $term->readline($prompt, $commandline)); } $_ = "$continuation$_" if $continuation; s/^\s+//; next SHELLCOMMAND if /^$/; s/^\s*\?\s*/help /; if (/^(?:q(?:uit)?|bye|exit)\s*$/i) { last SHELLCOMMAND; } elsif (s/\\$//s) { chomp; $continuation = $_; $prompt = " > "; } elsif (/^\!/) { s/^\!//; my($eval) = $_; package CPAN::Eval; # hide from the indexer use strict; use vars qw($import_done); CPAN->import(':DEFAULT') unless $import_done++; CPAN->debug("eval[$eval]") if $CPAN::DEBUG; eval($eval); warn $@ if $@; $continuation = ""; $prompt = $oprompt; } elsif (/./) { my(@line); eval { @line = Text::ParseWords::shellwords($_) }; warn($@), next SHELLCOMMAND if $@; warn("Text::Parsewords could not parse the line [$_]"), next SHELLCOMMAND unless @line; $CPAN::META->debug("line[".join("|",@line)."]") if $CPAN::DEBUG; my $command = shift @line; eval { local (*STDOUT)=*STDOUT; @line = _redirect(@line); CPAN::Shell->$command(@line) }; my $command_error = $@; _unredirect; my $reported_error; if ($command_error) { my $err = $command_error; if (ref $err and $err->isa('CPAN::Exception::blocked_urllist')) { $CPAN::Frontend->mywarn("Client not fully configured, please proceed with configuring.$err"); $reported_error = ref $err; } else { # I'd prefer never to arrive here and make all errors exception objects if ($err =~ /\S/) { require Carp; require Dumpvalue; my $dv = Dumpvalue->new(tick => '"'); Carp::cluck(sprintf "Catching error: %s", $dv->stringify($err)); } } } if ($command =~ /^( # classic commands make |test |install |clean # pragmas for classic commands |ff?orce |notest # compounds |report |smoke |upgrade )$/x) { # only commands that tell us something about failed distros # eval necessary for people without an urllist eval {CPAN::Shell->failed($CPAN::CurrentCommandId,1);}; if (my $err = $@) { unless (ref $err and $reported_error eq ref $err) { die $@; } } } soft_chdir_with_alternatives(\@cwd); $CPAN::Frontend->myprint("\n"); $continuation = ""; $CPAN::CurrentCommandId++; $prompt = $oprompt; } } continue { $commandline = ""; # I do want to be able to pass a default to # shell, but on the second command I see no # use in that $Signal=0; CPAN::Queue->nullify_queue; if ($try_detect_readline) { if ($CPAN::META->has_inst("Term::ReadLine::Gnu") || $CPAN::META->has_inst("Term::ReadLine::Perl") ) { delete $INC{"Term/ReadLine.pm"}; my $redef = 0; local($SIG{__WARN__}) = CPAN::Shell::paintdots_onreload(\$redef); require Term::ReadLine; $CPAN::Frontend->myprint("\n$redef subroutines in ". "Term::ReadLine redefined\n"); $GOTOSHELL = 1; } } if ($term and $term->can("ornaments")) { for ($CPAN::Config->{term_ornaments}) { # alias if (defined $_) { if (not defined $last_term_ornaments or $_ != $last_term_ornaments ) { local $Term::ReadLine::termcap_nowarn = 1; $term->ornaments($_); $last_term_ornaments = $_; } } else { undef $last_term_ornaments; } } } for my $class (qw(Module Distribution)) { # again unsafe meta access? for my $dm (sort keys %{$CPAN::META->{readwrite}{"CPAN::$class"}}) { next unless $CPAN::META->{readwrite}{"CPAN::$class"}{$dm}{incommandcolor}; CPAN->debug("BUG: $class '$dm' was in command state, resetting"); delete $CPAN::META->{readwrite}{"CPAN::$class"}{$dm}{incommandcolor}; } } if ($GOTOSHELL) { $GOTOSHELL = 0; # not too often $META->savehist if $CPAN::term && $CPAN::term->can("GetHistory"); @_ = ($oprompt,""); goto &shell; } } soft_chdir_with_alternatives(\@cwd); } #-> CPAN::soft_chdir_with_alternatives ; sub soft_chdir_with_alternatives ($) { my($cwd) = @_; unless (@$cwd) { my $root = File::Spec->rootdir(); $CPAN::Frontend->mywarn(qq{Warning: no good directory to chdir to! Trying '$root' as temporary haven. }); push @$cwd, $root; } while () { if (chdir "$cwd->[0]") { return; } else { if (@$cwd>1) { $CPAN::Frontend->mywarn(qq{Could not chdir to "$cwd->[0]": $! Trying to chdir to "$cwd->[1]" instead. }); shift @$cwd; } else { $CPAN::Frontend->mydie(qq{Could not chdir to "$cwd->[0]": $!}); } } } } sub _flock { my($fh,$mode) = @_; if ( $Config::Config{d_flock} || $Config::Config{d_fcntl_can_lock} ) { return flock $fh, $mode; } elsif (!$Have_warned->{"d_flock"}++) { $CPAN::Frontend->mywarn("Your OS does not seem to support locking; continuing and ignoring all locking issues\n"); $CPAN::Frontend->mysleep(5); return 1; } else { return 1; } } sub _yaml_module () { my $yaml_module = $CPAN::Config->{yaml_module} || "YAML"; if ( $yaml_module ne "YAML" && !$CPAN::META->has_inst($yaml_module) ) { # $CPAN::Frontend->mywarn("'$yaml_module' not installed, falling back to 'YAML'\n"); $yaml_module = "YAML"; } if ($yaml_module eq "YAML" && $CPAN::META->has_inst($yaml_module) && $YAML::VERSION < 0.60 && !$Have_warned->{"YAML"}++ ) { $CPAN::Frontend->mywarn("Warning: YAML version '$YAML::VERSION' is too low, please upgrade!\n". "I'll continue but problems are *very* likely to happen.\n" ); $CPAN::Frontend->mysleep(5); } return $yaml_module; } # CPAN::_yaml_loadfile sub _yaml_loadfile { my($self,$local_file,$opt) = @_; return +[] unless -s $local_file; my $opt_loadblessed = $opt->{loadblessed} || $CPAN::Config->{yaml_load_code} || 0; my $yaml_module = _yaml_module; if ($CPAN::META->has_inst($yaml_module)) { # temporarily enable yaml code deserialisation no strict 'refs'; # 5.6.2 could not do the local() with the reference # so we do it manually instead my $old_loadcode = ${"$yaml_module\::LoadCode"}; my $old_loadblessed = ${"$yaml_module\::LoadBlessed"}; ${ "$yaml_module\::LoadCode" } = $CPAN::Config->{yaml_load_code} || 0; ${ "$yaml_module\::LoadBlessed" } = $opt_loadblessed ? 1 : 0; my ($code, @yaml); if ($code = UNIVERSAL::can($yaml_module, "LoadFile")) { eval { @yaml = $code->($local_file); }; if ($@) { # this shall not be done by the frontend die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"parse",$@); } } elsif ($code = UNIVERSAL::can($yaml_module, "Load")) { local *FH; if (open FH, $local_file) { local $/; my $ystream = <FH>; eval { @yaml = $code->($ystream); }; if ($@) { # this shall not be done by the frontend die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"parse",$@); } } else { $CPAN::Frontend->mywarn("Could not open '$local_file': $!"); } } ${"$yaml_module\::LoadCode"} = $old_loadcode; ${"$yaml_module\::LoadBlessed"} = $old_loadblessed; return \@yaml; } else { # this shall not be done by the frontend die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "parse"); } return +[]; } # CPAN::_yaml_dumpfile sub _yaml_dumpfile { my($self,$local_file,@what) = @_; my $yaml_module = _yaml_module; if ($CPAN::META->has_inst($yaml_module)) { my $code; if (UNIVERSAL::isa($local_file, "FileHandle")) { $code = UNIVERSAL::can($yaml_module, "Dump"); eval { print $local_file $code->(@what) }; } elsif ($code = UNIVERSAL::can($yaml_module, "DumpFile")) { eval { $code->($local_file,@what); }; } elsif ($code = UNIVERSAL::can($yaml_module, "Dump")) { local *FH; open FH, ">$local_file" or die "Could not open '$local_file': $!"; print FH $code->(@what); } if ($@) { die CPAN::Exception::yaml_process_error->new($yaml_module,$local_file,"dump",$@); } } else { if (UNIVERSAL::isa($local_file, "FileHandle")) { # I think this case does not justify a warning at all } else { die CPAN::Exception::yaml_not_installed->new($yaml_module, $local_file, "dump"); } } } sub _init_sqlite () { unless ($CPAN::META->has_inst("CPAN::SQLite")) { $CPAN::Frontend->mywarn(qq{CPAN::SQLite not installed, trying to work without\n}) unless $Have_warned->{"CPAN::SQLite"}++; return; } require CPAN::SQLite::META; # not needed since CVS version of 2006-12-17 $CPAN::SQLite ||= CPAN::SQLite::META->new($CPAN::META); } { my $negative_cache = {}; sub _sqlite_running { if ($negative_cache->{time} && time < $negative_cache->{time} + 60) { # need to cache the result, otherwise too slow return $negative_cache->{fact}; } else { $negative_cache = {}; # reset } my $ret = $CPAN::Config->{use_sqlite} && ($CPAN::SQLite || _init_sqlite()); return $ret if $ret; # fast anyway $negative_cache->{time} = time; return $negative_cache->{fact} = $ret; } } $META ||= CPAN->new; # In case we re-eval ourselves we need the || # from here on only subs. ################################################################################ sub _perl_fingerprint { my($self,$other_fingerprint) = @_; my $dll = eval {OS2::DLLname()}; my $mtime_dll = 0; if (defined $dll) { $mtime_dll = (-f $dll ? (stat(_))[9] : '-1'); } my $mtime_perl = (-f CPAN::find_perl ? (stat(_))[9] : '-1'); my $this_fingerprint = { '$^X' => CPAN::find_perl, sitearchexp => $Config::Config{sitearchexp}, 'mtime_$^X' => $mtime_perl, 'mtime_dll' => $mtime_dll, }; if ($other_fingerprint) { if (exists $other_fingerprint->{'stat($^X)'}) { # repair fp from rev. 1.88_57 $other_fingerprint->{'mtime_$^X'} = $other_fingerprint->{'stat($^X)'}[9]; } # mandatory keys since 1.88_57 for my $key (qw($^X sitearchexp mtime_dll mtime_$^X)) { return unless $other_fingerprint->{$key} eq $this_fingerprint->{$key}; } return 1; } else { return $this_fingerprint; } } sub suggest_myconfig () { SUGGEST_MYCONFIG: if(!$INC{'CPAN/MyConfig.pm'}) { $CPAN::Frontend->myprint("You don't seem to have a user ". "configuration (MyConfig.pm) yet.\n"); my $new = CPAN::Shell::colorable_makemaker_prompt("Do you want to create a ". "user configuration now? (Y/n)", "yes"); if($new =~ m{^y}i) { CPAN::Shell->mkmyconfig(); return &checklock; } else { $CPAN::Frontend->mydie("OK, giving up."); } } } #-> sub CPAN::all_objects ; sub all_objects { my($mgr,$class) = @_; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; CPAN->debug("mgr[$mgr] class[$class]") if $CPAN::DEBUG; CPAN::Index->reload; values %{ $META->{readwrite}{$class} }; # unsafe meta access, ok } # Called by shell, not in batch mode. In batch mode I see no risk in # having many processes updating something as installations are # continually checked at runtime. In shell mode I suspect it is # unintentional to open more than one shell at a time #-> sub CPAN::checklock ; sub checklock { my($self) = @_; my $lockfile = File::Spec->catfile($CPAN::Config->{cpan_home},".lock"); if (-f $lockfile && -M _ > 0) { my $fh = FileHandle->new($lockfile) or $CPAN::Frontend->mydie("Could not open lockfile '$lockfile': $!"); my $otherpid = <$fh>; my $otherhost = <$fh>; $fh->close; if (defined $otherpid && length $otherpid) { chomp $otherpid; } if (defined $otherhost && length $otherhost) { chomp $otherhost; } my $thishost = hostname(); my $ask_if_degraded_wanted = 0; if (defined $otherhost && defined $thishost && $otherhost ne '' && $thishost ne '' && $otherhost ne $thishost) { $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile '$lockfile'\n". "reports other host $otherhost and other ". "process $otherpid.\n". "Cannot proceed.\n")); } elsif ($RUN_DEGRADED) { $CPAN::Frontend->mywarn("Running in downgraded mode (experimental)\n"); } elsif (defined $otherpid && $otherpid) { return if $$ == $otherpid; # should never happen $CPAN::Frontend->mywarn( qq{ There seems to be running another CPAN process (pid $otherpid). Contacting... }); if (kill 0, $otherpid or $!{EPERM}) { $CPAN::Frontend->mywarn(qq{Other job is running.\n}); $ask_if_degraded_wanted = 1; } elsif (-w $lockfile) { my($ans) = CPAN::Shell::colorable_makemaker_prompt (qq{Other job not responding. Shall I overwrite }. qq{the lockfile '$lockfile'? (Y/n)},"y"); $CPAN::Frontend->myexit("Ok, bye\n") unless $ans =~ /^y/i; } else { Carp::croak( qq{Lockfile '$lockfile' not writable by you. }. qq{Cannot proceed.\n}. qq{ On UNIX try:\n}. qq{ rm '$lockfile'\n}. qq{ and then rerun us.\n} ); } } elsif ($^O eq "MSWin32") { $CPAN::Frontend->mywarn( qq{ There seems to be running another CPAN process according to '$lockfile'. }); $ask_if_degraded_wanted = 1; } else { $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Found invalid lockfile ". "'$lockfile', please remove. Cannot proceed.\n")); } if ($ask_if_degraded_wanted) { my($ans) = CPAN::Shell::colorable_makemaker_prompt (qq{Shall I try to run in downgraded }. qq{mode? (Y/n)},"y"); if ($ans =~ /^y/i) { $CPAN::Frontend->mywarn("Running in downgraded mode (experimental). Please report if something unexpected happens\n"); $RUN_DEGRADED = 1; for ($CPAN::Config) { # XXX # $_->{build_dir_reuse} = 0; # 2006-11-17 akoenig Why was that? $_->{commandnumber_in_prompt} = 0; # visibility $_->{histfile} = ""; # who should win otherwise? $_->{cache_metadata} = 0; # better would be a lock? $_->{use_sqlite} = 0; # better would be a write lock! $_->{auto_commit} = 0; # we are violent, do not persist $_->{test_report} = 0; # Oliver Paukstadt had sent wrong reports in degraded mode } } else { my $msg = "You may want to kill the other job and delete the lockfile."; if (defined $otherpid) { $msg .= " Something like: kill $otherpid rm $lockfile "; } $CPAN::Frontend->mydie("\n$msg"); } } } my $dotcpan = $CPAN::Config->{cpan_home}; eval { File::Path::mkpath($dotcpan);}; if ($@) { # A special case at least for Jarkko. my $firsterror = $@; my $seconderror; my $symlinkcpan; if (-l $dotcpan) { $symlinkcpan = readlink $dotcpan; die "readlink $dotcpan failed: $!" unless defined $symlinkcpan; eval { File::Path::mkpath($symlinkcpan); }; if ($@) { $seconderror = $@; } else { $CPAN::Frontend->mywarn(qq{ Working directory $symlinkcpan created. }); } } unless (-d $dotcpan) { my $mess = qq{ Your configuration suggests "$dotcpan" as your CPAN.pm working directory. I could not create this directory due to this error: $firsterror\n}; $mess .= qq{ As "$dotcpan" is a symlink to "$symlinkcpan", I tried to create that, but I failed with this error: $seconderror } if $seconderror; $mess .= qq{ Please make sure the directory exists and is writable. }; $CPAN::Frontend->mywarn($mess); return suggest_myconfig; } } # $@ after eval mkpath $dotcpan if (0) { # to test what happens when a race condition occurs for (reverse 1..10) { print $_, "\n"; sleep 1; } } # locking if (!$RUN_DEGRADED && !$self->{LOCKFH}) { my $fh; unless ($fh = FileHandle->new("+>>$lockfile")) { $CPAN::Frontend->mywarn(qq{ Your configuration suggests that CPAN.pm should use a working directory of $CPAN::Config->{cpan_home} Unfortunately we could not create the lock file $lockfile due to '$!'. Please make sure that the configuration variable \$CPAN::Config->{cpan_home} points to a directory where you can write a .lock file. You can set this variable in either a CPAN/MyConfig.pm or a CPAN/Config.pm in your \@INC path; }); return suggest_myconfig; } my $sleep = 1; while (!CPAN::_flock($fh, LOCK_EX|LOCK_NB)) { my $err = $! || "unknown error"; if ($sleep>3) { $CPAN::Frontend->mydie("Could not lock '$lockfile' with flock: $err; giving up\n"); } $CPAN::Frontend->mysleep($sleep+=0.1); $CPAN::Frontend->mywarn("Could not lock '$lockfile' with flock: $err; retrying\n"); } seek $fh, 0, 0; truncate $fh, 0; $fh->autoflush(1); $fh->print($$, "\n"); $fh->print(hostname(), "\n"); $self->{LOCK} = $lockfile; $self->{LOCKFH} = $fh; } $SIG{TERM} = sub { my $sig = shift; &cleanup; $CPAN::Frontend->mydie("Got SIG$sig, leaving"); }; $SIG{INT} = sub { # no blocks!!! my $sig = shift; &cleanup if $Signal; die "Got yet another signal" if $Signal > 1; $CPAN::Frontend->mydie("Got another SIG$sig") if $Signal; $CPAN::Frontend->mywarn("Caught SIG$sig, trying to continue\n"); $Signal++; }; # From: Larry Wall <larry@wall.org> # Subject: Re: deprecating SIGDIE # To: perl5-porters@perl.org # Date: Thu, 30 Sep 1999 14:58:40 -0700 (PDT) # # The original intent of __DIE__ was only to allow you to substitute one # kind of death for another on an application-wide basis without respect # to whether you were in an eval or not. As a global backstop, it should # not be used any more lightly (or any more heavily :-) than class # UNIVERSAL. Any attempt to build a general exception model on it should # be politely squashed. Any bug that causes every eval {} to have to be # modified should be not so politely squashed. # # Those are my current opinions. It is also my opinion that polite # arguments degenerate to personal arguments far too frequently, and that # when they do, it's because both people wanted it to, or at least didn't # sufficiently want it not to. # # Larry # global backstop to cleanup if we should really die $SIG{__DIE__} = \&cleanup; $self->debug("Signal handler set.") if $CPAN::DEBUG; } #-> sub CPAN::DESTROY ; sub DESTROY { &cleanup; # need an eval? } #-> sub CPAN::anycwd ; sub anycwd () { my $getcwd; $getcwd = $CPAN::Config->{'getcwd'} || 'cwd'; CPAN->$getcwd(); } #-> sub CPAN::cwd ; sub cwd {Cwd::cwd();} #-> sub CPAN::getcwd ; sub getcwd {Cwd::getcwd();} #-> sub CPAN::fastcwd ; sub fastcwd {Cwd::fastcwd();} #-> sub CPAN::getdcwd ; sub getdcwd {Cwd::getdcwd();} #-> sub CPAN::backtickcwd ; sub backtickcwd {my $cwd = `cwd`; chomp $cwd; $cwd} # Adapted from Probe::Perl #-> sub CPAN::_perl_is_same sub _perl_is_same { my ($perl) = @_; return MM->maybe_command($perl) && `$perl -MConfig=myconfig -e print -e myconfig` eq Config->myconfig; } # Adapted in part from Probe::Perl #-> sub CPAN::find_perl ; sub find_perl () { if ( File::Spec->file_name_is_absolute($^X) ) { return $^X; } else { my $exe = $Config::Config{exe_ext}; my @candidates = ( File::Spec->catfile($CPAN::iCwd,$^X), $Config::Config{'perlpath'}, ); for my $perl_name ($^X, 'perl', 'perl5', "perl$]") { for my $path (File::Spec->path(), $Config::Config{'binexp'}) { if ( defined($path) && length $path && -d $path ) { my $perl = File::Spec->catfile($path,$perl_name); push @candidates, $perl; # try with extension if not provided already if ($^O eq 'VMS') { # VMS might have a file version at the end push @candidates, $perl . $exe unless $perl =~ m/$exe(;\d+)?$/i; } elsif (defined $exe && length $exe) { push @candidates, $perl . $exe unless $perl =~ m/$exe$/i; } } } } for my $perl ( @candidates ) { if (MM->maybe_command($perl) && _perl_is_same($perl)) { $^X = $perl; return $perl; } } } return $^X; # default fall back } #-> sub CPAN::exists ; sub exists { my($mgr,$class,$id) = @_; CPAN::HandleConfig->load unless $CPAN::Config_loaded++; CPAN::Index->reload; ### Carp::croak "exists called without class argument" unless $class; $id ||= ""; $id =~ s/:+/::/g if $class eq "CPAN::Module"; my $exists; if (CPAN::_sqlite_running) { $exists = (exists $META->{readonly}{$class}{$id} or $CPAN::SQLite->set($class, $id)); } else { $exists = exists $META->{readonly}{$class}{$id}; } $exists ||= exists $META->{readwrite}{$class}{$id}; # unsafe meta access, ok } #-> sub CPAN::delete ; sub delete { my($mgr,$class,$id) = @_; delete $META->{readonly}{$class}{$id}; # unsafe meta access, ok delete $META->{readwrite}{$class}{$id}; # unsafe meta access, ok } #-> sub CPAN::has_usable # has_inst is sometimes too optimistic, we should replace it with this # has_usable whenever a case is given sub has_usable { my($self,$mod,$message) = @_; return 1 if $HAS_USABLE->{$mod}; my $has_inst = $self->has_inst($mod,$message); return unless $has_inst; my $usable; $usable = { # # most of these subroutines warn on the frontend, then # die if the installed version is unusable for some # reason; has_usable() then returns false when it caught # an exception, otherwise returns true and caches that; # 'CPAN::Meta' => [ sub { require CPAN::Meta; unless (CPAN::Version->vge(CPAN::Meta->VERSION, 2.110350)) { for ("Will not use CPAN::Meta, need version 2.110350\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'CPAN::Meta::Requirements' => [ sub { if (defined $CPAN::Meta::Requirements::VERSION && CPAN::Version->vlt($CPAN::Meta::Requirements::VERSION, "2.120920") ) { delete $INC{"CPAN/Meta/Requirements.pm"}; } require CPAN::Meta::Requirements; unless (CPAN::Version->vge(CPAN::Meta::Requirements->VERSION, 2.120920)) { for ("Will not use CPAN::Meta::Requirements, need version 2.120920\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'CPAN::Reporter' => [ sub { if (defined $CPAN::Reporter::VERSION && CPAN::Version->vlt($CPAN::Reporter::VERSION, "1.2011") ) { delete $INC{"CPAN/Reporter.pm"}; } require CPAN::Reporter; unless (CPAN::Version->vge(CPAN::Reporter->VERSION, "1.2011")) { for ("Will not use CPAN::Reporter, need version 1.2011\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], LWP => [ # we frequently had "Can't locate object # method "new" via package "LWP::UserAgent" at # (eval 69) line 2006 sub {require LWP}, sub {require LWP::UserAgent}, sub {require HTTP::Request}, sub {require URI::URL; unless (CPAN::Version->vge(URI::URL::->VERSION,0.08)) { for ("Will not use URI::URL, need 0.08\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'Net::FTP' => [ sub { my $var = $CPAN::Config->{ftp_proxy} || $ENV{ftp_proxy}; if ($var and $var =~ /^http:/i) { # rt #110833 for ("Net::FTP cannot handle http proxy") { $CPAN::Frontend->mywarn($_); die $_; } } }, sub {require Net::FTP}, sub {require Net::Config}, ], 'IO::Socket::SSL' => [ sub { require IO::Socket::SSL; unless (CPAN::Version->vge(IO::Socket::SSL::->VERSION,1.56)) { for ("Will not use IO::Socket::SSL, need 1.56\n") { $CPAN::Frontend->mywarn($_); die $_; } } } ], 'Net::SSLeay' => [ sub { require Net::SSLeay; unless (CPAN::Version->vge(Net::SSLeay::->VERSION,1.49)) { for ("Will not use Net::SSLeay, need 1.49\n") { $CPAN::Frontend->mywarn($_); die $_; } } } ], 'HTTP::Tiny' => [ sub { require HTTP::Tiny; unless (CPAN::Version->vge(HTTP::Tiny->VERSION, 0.005)) { for ("Will not use HTTP::Tiny, need version 0.005\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'File::HomeDir' => [ sub {require File::HomeDir; unless (CPAN::Version->vge(File::HomeDir::->VERSION, 0.52)) { for ("Will not use File::HomeDir, need 0.52\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ], 'Archive::Tar' => [ sub {require Archive::Tar; my $demand = "1.50"; unless (CPAN::Version->vge(Archive::Tar::->VERSION, $demand)) { my $atv = Archive::Tar->VERSION; for ("You have Archive::Tar $atv, but $demand or later is recommended. Please upgrade.\n") { $CPAN::Frontend->mywarn($_); # don't die, because we may need # Archive::Tar to upgrade } } }, ], 'File::Temp' => [ # XXX we should probably delete from # %INC too so we can load after we # installed a new enough version -- # I'm not sure. sub {require File::Temp; unless (CPAN::Version->vge(File::Temp::->VERSION,0.16)) { for ("Will not use File::Temp, need 0.16\n") { $CPAN::Frontend->mywarn($_); die $_; } } }, ] }; if ($usable->{$mod}) { local @INC = @INC; pop @INC if $INC[-1] eq '.'; for my $c (0..$#{$usable->{$mod}}) { my $code = $usable->{$mod}[$c]; my $ret = eval { &$code() }; $ret = "" unless defined $ret; if ($@) { # warn "DEBUG: c[$c]\$\@[$@]ret[$ret]"; return; } } } return $HAS_USABLE->{$mod} = 1; } sub frontend { shift; $CPAN::Frontend = shift if @_; $CPAN::Frontend; } sub use_inst { my ($self, $module) = @_; unless ($self->has_inst($module)) { $self->frontend->mydie("$module not installed, cannot continue"); } } #-> sub CPAN::has_inst sub has_inst { my($self,$mod,$message) = @_; Carp::croak("CPAN->has_inst() called without an argument") unless defined $mod; my %dont = map { $_ => 1 } keys %{$CPAN::META->{dontload_hash}||{}}, keys %{$CPAN::Config->{dontload_hash}||{}}, @{$CPAN::Config->{dontload_list}||[]}; if (defined $message && $message eq "no" # as far as I remember only used by Nox || $dont{$mod} ) { $CPAN::META->{dontload_hash}{$mod}||=1; # unsafe meta access, ok return 0; } local @INC = @INC; pop @INC if $INC[-1] eq '.'; my $file = $mod; my $obj; $file =~ s|::|/|g; $file .= ".pm"; if ($INC{$file}) { # checking %INC is wrong, because $INC{LWP} may be true # although $INC{"URI/URL.pm"} may have failed. But as # I really want to say "blah loaded OK", I have to somehow # cache results. ### warn "$file in %INC"; #debug return 1; } elsif (eval { require $file }) { # eval is good: if we haven't yet read the database it's # perfect and if we have installed the module in the meantime, # it tries again. The second require is only a NOOP returning # 1 if we had success, otherwise it's retrying my $mtime = (stat $INC{$file})[9]; # privileged files loaded by has_inst; Note: we use $mtime # as a proxy for a checksum. $CPAN::Shell::reload->{$file} = $mtime; my $v = eval "\$$mod\::VERSION"; $v = $v ? " (v$v)" : ""; CPAN::Shell->optprint("load_module","CPAN: $mod loaded ok$v\n"); if ($mod eq "CPAN::WAIT") { push @CPAN::Shell::ISA, 'CPAN::WAIT'; } return 1; } elsif ($mod eq "Net::FTP") { $CPAN::Frontend->mywarn(qq{ Please, install Net::FTP as soon as possible. CPAN.pm installs it for you if you just type install Bundle::libnet }) unless $Have_warned->{"Net::FTP"}++; $CPAN::Frontend->mysleep(3); } elsif ($mod eq "Digest::SHA") { if ($Have_warned->{"Digest::SHA"}++) { $CPAN::Frontend->mywarn(qq{CPAN: checksum security checks disabled }. qq{because Digest::SHA not installed.\n}); } else { $CPAN::Frontend->mywarn(qq{ CPAN: checksum security checks disabled because Digest::SHA not installed. Please consider installing the Digest::SHA module. }); $CPAN::Frontend->mysleep(2); } } elsif ($mod eq "Module::Signature") { # NOT prefs_lookup, we are not a distro my $check_sigs = $CPAN::Config->{check_sigs}; if (not $check_sigs) { # they do not want us:-( } elsif (not $Have_warned->{"Module::Signature"}++) { # No point in complaining unless the user can # reasonably install and use it. if (eval { require Crypt::OpenPGP; 1 } || ( defined $CPAN::Config->{'gpg'} && $CPAN::Config->{'gpg'} =~ /\S/ ) ) { $CPAN::Frontend->mywarn(qq{ CPAN: Module::Signature security checks disabled because Module::Signature not installed. Please consider installing the Module::Signature module. You may also need to be able to connect over the Internet to the public key servers like pool.sks-keyservers.net or pgp.mit.edu. }); $CPAN::Frontend->mysleep(2); } } } else { delete $INC{$file}; # if it inc'd LWP but failed during, say, URI } return 0; } #-> sub CPAN::instance ; sub instance { my($mgr,$class,$id) = @_; CPAN::Index->reload; $id ||= ""; # unsafe meta access, ok? return $META->{readwrite}{$class}{$id} if exists $META->{readwrite}{$class}{$id}; $META->{readwrite}{$class}{$id} ||= $class->new(ID => $id); } #-> sub CPAN::new ; sub new { bless {}, shift; } #-> sub CPAN::_exit_messages ; sub _exit_messages { my ($self) = @_; $self->{exit_messages} ||= []; } #-> sub CPAN::cleanup ; sub cleanup { # warn "cleanup called with arg[@_] End[$CPAN::End] Signal[$Signal]"; local $SIG{__DIE__} = ''; my($message) = @_; my $i = 0; my $ineval = 0; my($subroutine); while ((undef,undef,undef,$subroutine) = caller(++$i)) { $ineval = 1, last if $subroutine eq '(eval)'; } return if $ineval && !$CPAN::End; return unless defined $META->{LOCK}; return unless -f $META->{LOCK}; $META->savehist; $META->{cachemgr} ||= CPAN::CacheMgr->new('atexit'); close $META->{LOCKFH}; unlink $META->{LOCK}; # require Carp; # Carp::cluck("DEBUGGING"); if ( $CPAN::CONFIG_DIRTY ) { $CPAN::Frontend->mywarn("Warning: Configuration not saved.\n"); } $CPAN::Frontend->myprint("Lockfile removed.\n"); for my $msg ( @{ $META->_exit_messages } ) { $CPAN::Frontend->myprint($msg); } } #-> sub CPAN::readhist sub readhist { my($self,$term,$histfile) = @_; my $histsize = $CPAN::Config->{'histsize'} || 100; $term->Attribs->{'MaxHistorySize'} = $histsize if (defined($term->Attribs->{'MaxHistorySize'})); my($fh) = FileHandle->new; open $fh, "<$histfile" or return; local $/ = "\n"; while (<$fh>) { chomp; $term->AddHistory($_); } close $fh; } #-> sub CPAN::savehist sub savehist { my($self) = @_; my($histfile,$histsize); unless ($histfile = $CPAN::Config->{'histfile'}) { $CPAN::Frontend->mywarn("No history written (no histfile specified).\n"); return; } $histsize = $CPAN::Config->{'histsize'} || 100; if ($CPAN::term) { unless ($CPAN::term->can("GetHistory")) { $CPAN::Frontend->mywarn("Terminal does not support GetHistory.\n"); return; } } else { return; } my @h = $CPAN::term->GetHistory; splice @h, 0, @h-$histsize if @h>$histsize; my($fh) = FileHandle->new; open $fh, ">$histfile" or $CPAN::Frontend->mydie("Couldn't open >$histfile: $!"); local $\ = local $, = "\n"; print $fh @h; close $fh; } #-> sub CPAN::is_tested sub is_tested { my($self,$what,$when) = @_; unless ($what) { Carp::cluck("DEBUG: empty what"); return; } $self->{is_tested}{$what} = $when; } #-> sub CPAN::reset_tested # forget all distributions tested -- resets what gets included in PERL5LIB sub reset_tested { my ($self) = @_; $self->{is_tested} = {}; } #-> sub CPAN::is_installed # unsets the is_tested flag: as soon as the thing is installed, it is # not needed in set_perl5lib anymore sub is_installed { my($self,$what) = @_; delete $self->{is_tested}{$what}; } sub _list_sorted_descending_is_tested { my($self) = @_; my $foul = 0; my @sorted = sort { ($self->{is_tested}{$b}||0) <=> ($self->{is_tested}{$a}||0) } grep { if ($foul){ 0 } elsif (-e) { 1 } else { $foul = $_; 0 } } keys %{$self->{is_tested}}; if ($foul) { $CPAN::Frontend->mywarn("Lost build_dir detected ($foul), giving up all cached test results of currently running session.\n"); for my $dbd (sort keys %{$self->{is_tested}}) { # distro-build-dir SEARCH: for my $d (sort { $a->id cmp $b->id } $CPAN::META->all_objects("CPAN::Distribution")) { if ($d->{build_dir} && $d->{build_dir} eq $dbd) { $CPAN::Frontend->mywarn(sprintf "Flushing cache for %s\n", $d->pretty_id); $d->fforce(""); last SEARCH; } } delete $self->{is_tested}{$dbd}; } return (); } else { return @sorted; } } #-> sub CPAN::set_perl5lib # Notes on max environment variable length: # - Win32 : XP or later, 8191; Win2000 or NT4, 2047 { my $fh; sub set_perl5lib { my($self,$for) = @_; unless ($for) { (undef,undef,undef,$for) = caller(1); $for =~ s/.*://; } $self->{is_tested} ||= {}; return unless %{$self->{is_tested}}; my $env = $ENV{PERL5LIB}; $env = $ENV{PERLLIB} unless defined $env; my @env; push @env, split /\Q$Config::Config{path_sep}\E/, $env if defined $env and length $env; #my @dirs = map {("$_/blib/arch", "$_/blib/lib")} keys %{$self->{is_tested}}; #$CPAN::Frontend->myprint("Prepending @dirs to PERL5LIB.\n"); my @dirs = map {("$_/blib/arch", "$_/blib/lib")} $self->_list_sorted_descending_is_tested; return if !@dirs; if (@dirs < 12) { $CPAN::Frontend->optprint('perl5lib', "Prepending @dirs to PERL5LIB for '$for'\n"); $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env; } elsif (@dirs < 24 ) { my @d = map {my $cp = $_; $cp =~ s/^\Q$CPAN::Config->{build_dir}\E/%BUILDDIR%/; $cp } @dirs; $CPAN::Frontend->optprint('perl5lib', "Prepending @d to PERL5LIB; ". "%BUILDDIR%=$CPAN::Config->{build_dir} ". "for '$for'\n" ); $ENV{PERL5LIB} = join $Config::Config{path_sep}, @dirs, @env; } else { my $cnt = keys %{$self->{is_tested}}; my $newenv = join $Config::Config{path_sep}, @dirs, @env; $CPAN::Frontend->optprint('perl5lib', sprintf ("Prepending blib/arch and blib/lib of ". "%d build dirs to PERL5LIB, reaching size %d; ". "for '%s'\n", $cnt, length($newenv), $for) ); $ENV{PERL5LIB} = $newenv; } }} 1; __END__ =head1 NAME CPAN - query, download and build perl modules from CPAN sites =head1 SYNOPSIS Interactive mode: perl -MCPAN -e shell --or-- cpan Basic commands: # Modules: cpan> install Acme::Meta # in the shell CPAN::Shell->install("Acme::Meta"); # in perl # Distributions: cpan> install NWCLARK/Acme-Meta-0.02.tar.gz # in the shell CPAN::Shell-> install("NWCLARK/Acme-Meta-0.02.tar.gz"); # in perl # module objects: $mo = CPAN::Shell->expandany($mod); $mo = CPAN::Shell->expand("Module",$mod); # same thing # distribution objects: $do = CPAN::Shell->expand("Module",$mod)->distribution; $do = CPAN::Shell->expandany($distro); # same thing $do = CPAN::Shell->expand("Distribution", $distro); # same thing =head1 DESCRIPTION The CPAN module automates or at least simplifies the make and install of perl modules and extensions. It includes some primitive searching capabilities and knows how to use LWP, HTTP::Tiny, Net::FTP and certain external download clients to fetch distributions from the net. These are fetched from one or more mirrored CPAN (Comprehensive Perl Archive Network) sites and unpacked in a dedicated directory. The CPAN module also supports named and versioned I<bundles> of modules. Bundles simplify handling of sets of related modules. See Bundles below. The package contains a session manager and a cache manager. The session manager keeps track of what has been fetched, built, and installed in the current session. The cache manager keeps track of the disk space occupied by the make processes and deletes excess space using a simple FIFO mechanism. All methods provided are accessible in a programmer style and in an interactive shell style. =head2 CPAN::shell([$prompt, $command]) Starting Interactive Mode Enter interactive mode by running perl -MCPAN -e shell or cpan which puts you into a readline interface. If C<Term::ReadKey> and either of C<Term::ReadLine::Perl> or C<Term::ReadLine::Gnu> are installed, history and command completion are supported. Once at the command line, type C<h> for one-page help screen; the rest should be self-explanatory. The function call C<shell> takes two optional arguments: one the prompt, the second the default initial command line (the latter only works if a real ReadLine interface module is installed). The most common uses of the interactive modes are =over 2 =item Searching for authors, bundles, distribution files and modules There are corresponding one-letter commands C<a>, C<b>, C<d>, and C<m> for each of the four categories and another, C<i> for any of the mentioned four. Each of the four entities is implemented as a class with slightly differing methods for displaying an object. Arguments to these commands are either strings exactly matching the identification string of an object, or regular expressions matched case-insensitively against various attributes of the objects. The parser only recognizes a regular expression when you enclose it with slashes. The principle is that the number of objects found influences how an item is displayed. If the search finds one item, the result is displayed with the rather verbose method C<as_string>, but if more than one is found, each object is displayed with the terse method C<as_glimpse>. Examples: cpan> m Acme::MetaSyntactic Module id = Acme::MetaSyntactic CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) CPAN_VERSION 0.99 CPAN_FILE B/BO/BOOK/Acme-MetaSyntactic-0.99.tar.gz UPLOAD_DATE 2006-11-06 MANPAGE Acme::MetaSyntactic - Themed metasyntactic variables names INST_FILE /usr/local/lib/perl/5.10.0/Acme/MetaSyntactic.pm INST_VERSION 0.99 cpan> a BOOK Author id = BOOK EMAIL [...] FULLNAME Philippe Bruhat (BooK) cpan> d BOOK/Acme-MetaSyntactic-0.99.tar.gz Distribution id = B/BO/BOOK/Acme-MetaSyntactic-0.99.tar.gz CPAN_USERID BOOK (Philippe Bruhat (BooK) <[...]>) CONTAINSMODS Acme::MetaSyntactic Acme::MetaSyntactic::Alias [...] UPLOAD_DATE 2006-11-06 cpan> m /lorem/ Module = Acme::MetaSyntactic::loremipsum (BOOK/Acme-MetaSyntactic-0.99.tar.gz) Module Text::Lorem (ADEOLA/Text-Lorem-0.3.tar.gz) Module Text::Lorem::More (RKRIMEN/Text-Lorem-More-0.12.tar.gz) Module Text::Lorem::More::Source (RKRIMEN/Text-Lorem-More-0.12.tar.gz) cpan> i /berlin/ Distribution BEATNIK/Filter-NumberLines-0.02.tar.gz Module = DateTime::TimeZone::Europe::Berlin (DROLSKY/DateTime-TimeZone-0.7904.tar.gz) Module Filter::NumberLines (BEATNIK/Filter-NumberLines-0.02.tar.gz) Author [...] The examples illustrate several aspects: the first three queries target modules, authors, or distros directly and yield exactly one result. The last two use regular expressions and yield several results. The last one targets all of bundles, modules, authors, and distros simultaneously. When more than one result is available, they are printed in one-line format. =item C<get>, C<make>, C<test>, C<install>, C<clean> modules or distributions These commands take any number of arguments and investigate what is necessary to perform the action. Argument processing is as follows: known module name in format Foo/Bar.pm module other embedded slash distribution - with trailing slash dot directory enclosing slashes regexp known module name in format Foo::Bar module If the argument is a distribution file name (recognized by embedded slashes), it is processed. If it is a module, CPAN determines the distribution file in which this module is included and processes that, following any dependencies named in the module's META.yml or Makefile.PL (this behavior is controlled by the configuration parameter C<prerequisites_policy>). If an argument is enclosed in slashes it is treated as a regular expression: it is expanded and if the result is a single object (distribution, bundle or module), this object is processed. Example: install Dummy::Perl # installs the module install AUXXX/Dummy-Perl-3.14.tar.gz # installs that distribution install /Dummy-Perl-3.14/ # same if the regexp is unambiguous C<get> downloads a distribution file and untars or unzips it, C<make> builds it, C<test> runs the test suite, and C<install> installs it. Any C<make> or C<test> is run unconditionally. An install <distribution_file> is also run unconditionally. But for install <module> CPAN checks whether an install is needed and prints I<module up to date> if the distribution file containing the module doesn't need updating. CPAN also keeps track of what it has done within the current session and doesn't try to build a package a second time regardless of whether it succeeded or not. It does not repeat a test run if the test has been run successfully before. Same for install runs. The C<force> pragma may precede another command (currently: C<get>, C<make>, C<test>, or C<install>) to execute the command from scratch and attempt to continue past certain errors. See the section below on the C<force> and the C<fforce> pragma. The C<notest> pragma skips the test part in the build process. Example: cpan> notest install Tk A C<clean> command results in a make clean being executed within the distribution file's working directory. =item C<readme>, C<perldoc>, C<look> module or distribution C<readme> displays the README file of the associated distribution. C<Look> gets and untars (if not yet done) the distribution file, changes to the appropriate directory and opens a subshell process in that directory. C<perldoc> displays the module's pod documentation in html or plain text format. =item C<ls> author =item C<ls> globbing_expression The first form lists all distribution files in and below an author's CPAN directory as stored in the CHECKSUMS files distributed on CPAN. The listing recurses into subdirectories. The second form limits or expands the output with shell globbing as in the following examples: ls JV/make* ls GSAR/*make* ls */*make* The last example is very slow and outputs extra progress indicators that break the alignment of the result. Note that globbing only lists directories explicitly asked for, for example FOO/* will not list FOO/bar/Acme-Sthg-n.nn.tar.gz. This may be regarded as a bug that may be changed in some future version. =item C<failed> The C<failed> command reports all distributions that failed on one of C<make>, C<test> or C<install> for some reason in the currently running shell session. =item Persistence between sessions If the C<YAML> or the C<YAML::Syck> module is installed a record of the internal state of all modules is written to disk after each step. The files contain a signature of the currently running perl version for later perusal. If the configurations variable C<build_dir_reuse> is set to a true value, then CPAN.pm reads the collected YAML files. If the stored signature matches the currently running perl, the stored state is loaded into memory such that persistence between sessions is effectively established. =item The C<force> and the C<fforce> pragma To speed things up in complex installation scenarios, CPAN.pm keeps track of what it has already done and refuses to do some things a second time. A C<get>, a C<make>, and an C<install> are not repeated. A C<test> is repeated only if the previous test was unsuccessful. The diagnostic message when CPAN.pm refuses to do something a second time is one of I<Has already been >C<unwrapped|made|tested successfully> or something similar. Another situation where CPAN refuses to act is an C<install> if the corresponding C<test> was not successful. In all these cases, the user can override this stubborn behaviour by prepending the command with the word force, for example: cpan> force get Foo cpan> force make AUTHOR/Bar-3.14.tar.gz cpan> force test Baz cpan> force install Acme::Meta Each I<forced> command is executed with the corresponding part of its memory erased. The C<fforce> pragma is a variant that emulates a C<force get> which erases the entire memory followed by the action specified, effectively restarting the whole get/make/test/install procedure from scratch. =item Lockfile Interactive sessions maintain a lockfile, by default C<~/.cpan/.lock>. Batch jobs can run without a lockfile and not disturb each other. The shell offers to run in I<downgraded mode> when another process is holding the lockfile. This is an experimental feature that is not yet tested very well. This second shell then does not write the history file, does not use the metadata file, and has a different prompt. =item Signals CPAN.pm installs signal handlers for SIGINT and SIGTERM. While you are in the cpan-shell, it is intended that you can press C<^C> anytime and return to the cpan-shell prompt. A SIGTERM will cause the cpan-shell to clean up and leave the shell loop. You can emulate the effect of a SIGTERM by sending two consecutive SIGINTs, which usually means by pressing C<^C> twice. CPAN.pm ignores SIGPIPE. If the user sets C<inactivity_timeout>, a SIGALRM is used during the run of the C<perl Makefile.PL> or C<perl Build.PL> subprocess. A SIGALRM is also used during module version parsing, and is controlled by C<version_timeout>. =back =head2 CPAN::Shell The commands available in the shell interface are methods in the package CPAN::Shell. If you enter the shell command, your input is split by the Text::ParseWords::shellwords() routine, which acts like most shells do. The first word is interpreted as the method to be invoked, and the rest of the words are treated as the method's arguments. Continuation lines are supported by ending a line with a literal backslash. =head2 autobundle C<autobundle> writes a bundle file into the C<$CPAN::Config-E<gt>{cpan_home}/Bundle> directory. The file contains a list of all modules that are both available from CPAN and currently installed within @INC. Duplicates of each distribution are suppressed. The name of the bundle file is based on the current date and a counter, e.g. F<Bundle/Snapshot_2012_05_21_00.pm>. This is installed again by running C<cpan Bundle::Snapshot_2012_05_21_00>, or installing C<Bundle::Snapshot_2012_05_21_00> from the CPAN shell. Return value: path to the written file. =head2 hosts Note: this feature is still in alpha state and may change in future versions of CPAN.pm This commands provides a statistical overview over recent download activities. The data for this is collected in the YAML file C<FTPstats.yml> in your C<cpan_home> directory. If no YAML module is configured or YAML not installed, or if C<ftpstats_size> is set to a value C<< <=0 >>, no stats are provided. =head2 install_tested Install all distributions that have been tested successfully but have not yet been installed. See also C<is_tested>. =head2 is_tested List all build directories of distributions that have been tested successfully but have not yet been installed. See also C<install_tested>. =head2 mkmyconfig mkmyconfig() writes your own CPAN::MyConfig file into your C<~/.cpan/> directory so that you can save your own preferences instead of the system-wide ones. =head2 r [Module|/Regexp/]... scans current perl installation for modules that have a newer version available on CPAN and provides a list of them. If called without argument, all potential upgrades are listed; if called with arguments the list is filtered to the modules and regexps given as arguments. The listing looks something like this: Package namespace installed latest in CPAN file CPAN 1.94_64 1.9600 ANDK/CPAN-1.9600.tar.gz CPAN::Reporter 1.1801 1.1902 DAGOLDEN/CPAN-Reporter-1.1902.tar.gz YAML 0.70 0.73 INGY/YAML-0.73.tar.gz YAML::Syck 1.14 1.17 AVAR/YAML-Syck-1.17.tar.gz YAML::Tiny 1.44 1.50 ADAMK/YAML-Tiny-1.50.tar.gz CGI 3.43 3.55 MARKSTOS/CGI.pm-3.55.tar.gz Module::Build::YAML 1.40 1.41 DAGOLDEN/Module-Build-0.3800.tar.gz TAP::Parser::Result::YAML 3.22 3.23 ANDYA/Test-Harness-3.23.tar.gz YAML::XS 0.34 0.35 INGY/YAML-LibYAML-0.35.tar.gz It suppresses duplicates in the column C<in CPAN file> such that distributions with many upgradeable modules are listed only once. Note that the list is not sorted. =head2 recent ***EXPERIMENTAL COMMAND*** The C<recent> command downloads a list of recent uploads to CPAN and displays them I<slowly>. While the command is running, a $SIG{INT} exits the loop after displaying the current item. B<Note>: This command requires XML::LibXML installed. B<Note>: This whole command currently is just a hack and will probably change in future versions of CPAN.pm, but the general approach will likely remain. B<Note>: See also L<smoke> =head2 recompile recompile() is a special command that takes no argument and runs the make/test/install cycle with brute force over all installed dynamically loadable extensions (a.k.a. XS modules) with 'force' in effect. The primary purpose of this command is to finish a network installation. Imagine you have a common source tree for two different architectures. You decide to do a completely independent fresh installation. You start on one architecture with the help of a Bundle file produced earlier. CPAN installs the whole Bundle for you, but when you try to repeat the job on the second architecture, CPAN responds with a C<"Foo up to date"> message for all modules. So you invoke CPAN's recompile on the second architecture and you're done. Another popular use for C<recompile> is to act as a rescue in case your perl breaks binary compatibility. If one of the modules that CPAN uses is in turn depending on binary compatibility (so you cannot run CPAN commands), then you should try the CPAN::Nox module for recovery. =head2 report Bundle|Distribution|Module The C<report> command temporarily turns on the C<test_report> config variable, then runs the C<force test> command with the given arguments. The C<force> pragma reruns the tests and repeats every step that might have failed before. =head2 smoke ***EXPERIMENTAL COMMAND*** B<*** WARNING: this command downloads and executes software from CPAN to your computer of completely unknown status. You should never do this with your normal account and better have a dedicated well separated and secured machine to do this. ***> The C<smoke> command takes the list of recent uploads to CPAN as provided by the C<recent> command and tests them all. While the command is running $SIG{INT} is defined to mean that the current item shall be skipped. B<Note>: This whole command currently is just a hack and will probably change in future versions of CPAN.pm, but the general approach will likely remain. B<Note>: See also L<recent> =head2 upgrade [Module|/Regexp/]... The C<upgrade> command first runs an C<r> command with the given arguments and then installs the newest versions of all modules that were listed by that. =head2 The four C<CPAN::*> Classes: Author, Bundle, Module, Distribution Although it may be considered internal, the class hierarchy does matter for both users and programmer. CPAN.pm deals with the four classes mentioned above, and those classes all share a set of methods. Classical single polymorphism is in effect. A metaclass object registers all objects of all kinds and indexes them with a string. The strings referencing objects have a separated namespace (well, not completely separated): Namespace Class words containing a "/" (slash) Distribution words starting with Bundle:: Bundle everything else Module or Author Modules know their associated Distribution objects. They always refer to the most recent official release. Developers may mark their releases as unstable development versions (by inserting an underscore into the module version number which will also be reflected in the distribution name when you run 'make dist'), so the really hottest and newest distribution is not always the default. If a module Foo circulates on CPAN in both version 1.23 and 1.23_90, CPAN.pm offers a convenient way to install version 1.23 by saying install Foo This would install the complete distribution file (say BAR/Foo-1.23.tar.gz) with all accompanying material. But if you would like to install version 1.23_90, you need to know where the distribution file resides on CPAN relative to the authors/id/ directory. If the author is BAR, this might be BAR/Foo-1.23_90.tar.gz; so you would have to say install BAR/Foo-1.23_90.tar.gz The first example will be driven by an object of the class CPAN::Module, the second by an object of class CPAN::Distribution. =head2 Integrating local directories Note: this feature is still in alpha state and may change in future versions of CPAN.pm Distribution objects are normally distributions from the CPAN, but there is a slightly degenerate case for Distribution objects, too, of projects held on the local disk. These distribution objects have the same name as the local directory and end with a dot. A dot by itself is also allowed for the current directory at the time CPAN.pm was used. All actions such as C<make>, C<test>, and C<install> are applied directly to that directory. This gives the command C<cpan .> an interesting touch: while the normal mantra of installing a CPAN module without CPAN.pm is one of perl Makefile.PL perl Build.PL ( go and get prerequisites ) make ./Build make test ./Build test make install ./Build install the command C<cpan .> does all of this at once. It figures out which of the two mantras is appropriate, fetches and installs all prerequisites, takes care of them recursively, and finally finishes the installation of the module in the current directory, be it a CPAN module or not. The typical usage case is for private modules or working copies of projects from remote repositories on the local disk. =head2 Redirection The usual shell redirection symbols C< | > and C<< > >> are recognized by the cpan shell B<only when surrounded by whitespace>. So piping to pager or redirecting output into a file works somewhat as in a normal shell, with the stipulation that you must type extra spaces. =head2 Plugin support ***EXPERIMENTAL*** Plugins are objects that implement any of currently eight methods: pre_get post_get pre_make post_make pre_test post_test pre_install post_install The C<plugin_list> configuration parameter holds a list of strings of the form Modulename=arg0,arg1,arg2,arg3,... eg: CPAN::Plugin::Flurb=dir,/opt/pkgs/flurb/raw,verbose,1 At run time, each listed plugin is instantiated as a singleton object by running the equivalent of this pseudo code: my $plugin = <string representation from config>; <generate Modulename and arguments from $plugin>; my $p = $instance{$plugin} ||= Modulename->new($arg0,$arg1,...); The generated singletons are kept around from instantiation until the end of the shell session. <plugin_list> can be reconfigured at any time at run time. While the cpan shell is running, it checks all activated plugins at each of the 8 reference points listed above and runs the respective method if it is implemented for that object. The method is called with the active CPAN::Distribution object passed in as an argument. =head1 CONFIGURATION When the CPAN module is used for the first time, a configuration dialogue tries to determine a couple of site specific options. The result of the dialog is stored in a hash reference C< $CPAN::Config > in a file CPAN/Config.pm. Default values defined in the CPAN/Config.pm file can be overridden in a user specific file: CPAN/MyConfig.pm. Such a file is best placed in C<$HOME/.cpan/CPAN/MyConfig.pm>, because C<$HOME/.cpan> is added to the search path of the CPAN module before the use() or require() statements. The mkmyconfig command writes this file for you. If you want to keep your own CPAN/MyConfig.pm somewhere else, you should load it before loading CPAN.pm, e.g.: perl -I/tmp/somewhere -MCPAN::MyConfig -MCPAN -eshell --or-- perl -I/tmp/somewhere -MCPAN::MyConfig -S cpan Once you are in the shell you can change your configuration as follows. The C<o conf> command has various bells and whistles: =over =item completion support If you have a ReadLine module installed, you can hit TAB at any point of the commandline and C<o conf> will offer you completion for the built-in subcommands and/or config variable names. =item displaying some help: o conf help Displays a short help =item displaying current values: o conf [KEY] Displays the current value(s) for this config variable. Without KEY, displays all subcommands and config variables. Example: o conf shell If KEY starts and ends with a slash, the string in between is treated as a regular expression and only keys matching this regexp are displayed Example: o conf /color/ =item changing of scalar values: o conf KEY VALUE Sets the config variable KEY to VALUE. The empty string can be specified as usual in shells, with C<''> or C<""> Example: o conf wget /usr/bin/wget =item changing of list values: o conf KEY SHIFT|UNSHIFT|PUSH|POP|SPLICE|LIST If a config variable name ends with C<list>, it is a list. C<o conf KEY shift> removes the first element of the list, C<o conf KEY pop> removes the last element of the list. C<o conf KEYS unshift LIST> prepends a list of values to the list, C<o conf KEYS push LIST> appends a list of valued to the list. Likewise, C<o conf KEY splice LIST> passes the LIST to the corresponding splice command. Finally, any other list of arguments is taken as a new list value for the KEY variable discarding the previous value. Examples: o conf urllist unshift http://cpan.dev.local/CPAN o conf urllist splice 3 1 o conf urllist http://cpan1.local http://cpan2.local ftp://ftp.perl.org =item reverting to saved: o conf defaults Reverts all config variables to the state in the saved config file. =item saving the config: o conf commit Saves all config variables to the current config file (CPAN/Config.pm or CPAN/MyConfig.pm that was loaded at start). =back The configuration dialog can be started any time later again by issuing the command C< o conf init > in the CPAN shell. A subset of the configuration dialog can be run by issuing C<o conf init WORD> where WORD is any valid config variable or a regular expression. =head2 Config Variables The following keys in the hash reference $CPAN::Config are currently defined: allow_installing_module_downgrades allow or disallow installing module downgrades allow_installing_outdated_dists allow or disallow installing modules that are indexed in the cpan index pointing to a distro with a higher distro-version number applypatch path to external prg auto_commit commit all changes to config variables to disk build_cache size of cache for directories to build modules build_dir locally accessible directory to build modules build_dir_reuse boolean if distros in build_dir are persistent build_requires_install_policy to install or not to install when a module is only needed for building. yes|no|ask/yes|ask/no bzip2 path to external prg cache_metadata use serializer to cache metadata check_sigs if signatures should be verified cleanup_after_install remove build directory immediately after a successful install and remember that for the duration of the session colorize_debug Term::ANSIColor attributes for debugging output colorize_output boolean if Term::ANSIColor should colorize output colorize_print Term::ANSIColor attributes for normal output colorize_warn Term::ANSIColor attributes for warnings commandnumber_in_prompt boolean if you want to see current command number commands_quote preferred character to use for quoting external commands when running them. Defaults to double quote on Windows, single tick everywhere else; can be set to space to disable quoting connect_to_internet_ok whether to ask if opening a connection is ok before urllist is specified cpan_home local directory reserved for this package curl path to external prg dontload_hash DEPRECATED dontload_list arrayref: modules in the list will not be loaded by the CPAN::has_inst() routine ftp path to external prg ftp_passive if set, the environment variable FTP_PASSIVE is set for downloads ftp_proxy proxy host for ftp requests ftpstats_period max number of days to keep download statistics ftpstats_size max number of items to keep in the download statistics getcwd see below gpg path to external prg gzip location of external program gzip halt_on_failure stop processing after the first failure of queued items or dependencies histfile file to maintain history between sessions histsize maximum number of lines to keep in histfile http_proxy proxy host for http requests inactivity_timeout breaks interactive Makefile.PLs or Build.PLs after this many seconds inactivity. Set to 0 to disable timeouts. index_expire refetch index files after this many days inhibit_startup_message if true, suppress the startup message keep_source_where directory in which to keep the source (if we do) load_module_verbosity report loading of optional modules used by CPAN.pm lynx path to external prg make location of external make program make_arg arguments that should always be passed to 'make' make_install_make_command the make command for running 'make install', for example 'sudo make' make_install_arg same as make_arg for 'make install' makepl_arg arguments passed to 'perl Makefile.PL' mbuild_arg arguments passed to './Build' mbuild_install_arg arguments passed to './Build install' mbuild_install_build_command command to use instead of './Build' when we are in the install stage, for example 'sudo ./Build' mbuildpl_arg arguments passed to 'perl Build.PL' ncftp path to external prg ncftpget path to external prg no_proxy don't proxy to these hosts/domains (comma separated list) pager location of external program more (or any pager) password your password if you CPAN server wants one patch path to external prg patches_dir local directory containing patch files perl5lib_verbosity verbosity level for PERL5LIB additions plugin_list list of active hooks (see Plugin support above and the CPAN::Plugin module) prefer_external_tar per default all untar operations are done with Archive::Tar; by setting this variable to true the external tar command is used if available prefer_installer legal values are MB and EUMM: if a module comes with both a Makefile.PL and a Build.PL, use the former (EUMM) or the latter (MB); if the module comes with only one of the two, that one will be used no matter the setting prerequisites_policy what to do if you are missing module prerequisites ('follow' automatically, 'ask' me, or 'ignore') For 'follow', also sets PERL_AUTOINSTALL and PERL_EXTUTILS_AUTOINSTALL for "--defaultdeps" if not already set prefs_dir local directory to store per-distro build options proxy_user username for accessing an authenticating proxy proxy_pass password for accessing an authenticating proxy pushy_https use https to cpan.org when possible, otherwise use http to cpan.org and issue a warning randomize_urllist add some randomness to the sequence of the urllist recommends_policy whether recommended prerequisites should be included scan_cache controls scanning of cache ('atstart', 'atexit' or 'never') shell your favorite shell show_unparsable_versions boolean if r command tells which modules are versionless show_upload_date boolean if commands should try to determine upload date show_zero_versions boolean if r command tells for which modules $version==0 suggests_policy whether suggested prerequisites should be included tar location of external program tar tar_verbosity verbosity level for the tar command term_is_latin deprecated: if true Unicode is translated to ISO-8859-1 (and nonsense for characters outside latin range) term_ornaments boolean to turn ReadLine ornamenting on/off test_report email test reports (if CPAN::Reporter is installed) trust_test_report_history skip testing when previously tested ok (according to CPAN::Reporter history) unzip location of external program unzip urllist arrayref to nearby CPAN sites (or equivalent locations) urllist_ping_external use external ping command when autoselecting mirrors urllist_ping_verbose increase verbosity when autoselecting mirrors use_prompt_default set PERL_MM_USE_DEFAULT for configure/make/test/install use_sqlite use CPAN::SQLite for metadata storage (fast and lean) username your username if you CPAN server wants one version_timeout stops version parsing after this many seconds. Default is 15 secs. Set to 0 to disable. wait_list arrayref to a wait server to try (See CPAN::WAIT) wget path to external prg yaml_load_code enable YAML code deserialisation via CPAN::DeferredCode yaml_module which module to use to read/write YAML files You can set and query each of these options interactively in the cpan shell with the C<o conf> or the C<o conf init> command as specified below. =over 2 =item C<o conf E<lt>scalar optionE<gt>> prints the current value of the I<scalar option> =item C<o conf E<lt>scalar optionE<gt> E<lt>valueE<gt>> Sets the value of the I<scalar option> to I<value> =item C<o conf E<lt>list optionE<gt>> prints the current value of the I<list option> in MakeMaker's neatvalue format. =item C<o conf E<lt>list optionE<gt> [shift|pop]> shifts or pops the array in the I<list option> variable =item C<o conf E<lt>list optionE<gt> [unshift|push|splice] E<lt>listE<gt>> works like the corresponding perl commands. =item interactive editing: o conf init [MATCH|LIST] Runs an interactive configuration dialog for matching variables. Without argument runs the dialog over all supported config variables. To specify a MATCH the argument must be enclosed by slashes. Examples: o conf init ftp_passive ftp_proxy o conf init /color/ Note: this method of setting config variables often provides more explanation about the functioning of a variable than the manpage. =back =head2 CPAN::anycwd($path): Note on config variable getcwd CPAN.pm changes the current working directory often and needs to determine its own current working directory. By default it uses Cwd::cwd, but if for some reason this doesn't work on your system, configure alternatives according to the following table: =over 4 =item cwd Calls Cwd::cwd =item getcwd Calls Cwd::getcwd =item fastcwd Calls Cwd::fastcwd =item getdcwd Calls Cwd::getdcwd =item backtickcwd Calls the external command cwd. =back =head2 Note on the format of the urllist parameter urllist parameters are URLs according to RFC 1738. We do a little guessing if your URL is not compliant, but if you have problems with C<file> URLs, please try the correct format. Either: file://localhost/whatever/ftp/pub/CPAN/ or file:///home/ftp/pub/CPAN/ =head2 The urllist parameter has CD-ROM support The C<urllist> parameter of the configuration table contains a list of URLs used for downloading. If the list contains any C<file> URLs, CPAN always tries there first. This feature is disabled for index files. So the recommendation for the owner of a CD-ROM with CPAN contents is: include your local, possibly outdated CD-ROM as a C<file> URL at the end of urllist, e.g. o conf urllist push file://localhost/CDROM/CPAN CPAN.pm will then fetch the index files from one of the CPAN sites that come at the beginning of urllist. It will later check for each module to see whether there is a local copy of the most recent version. Another peculiarity of urllist is that the site that we could successfully fetch the last file from automatically gets a preference token and is tried as the first site for the next request. So if you add a new site at runtime it may happen that the previously preferred site will be tried another time. This means that if you want to disallow a site for the next transfer, it must be explicitly removed from urllist. =head2 Maintaining the urllist parameter If you have YAML.pm (or some other YAML module configured in C<yaml_module>) installed, CPAN.pm collects a few statistical data about recent downloads. You can view the statistics with the C<hosts> command or inspect them directly by looking into the C<FTPstats.yml> file in your C<cpan_home> directory. To get some interesting statistics, it is recommended that C<randomize_urllist> be set; this introduces some amount of randomness into the URL selection. =head2 The C<requires> and C<build_requires> dependency declarations Since CPAN.pm version 1.88_51 modules declared as C<build_requires> by a distribution are treated differently depending on the config variable C<build_requires_install_policy>. By setting C<build_requires_install_policy> to C<no>, such a module is not installed. It is only built and tested, and then kept in the list of tested but uninstalled modules. As such, it is available during the build of the dependent module by integrating the path to the C<blib/arch> and C<blib/lib> directories in the environment variable PERL5LIB. If C<build_requires_install_policy> is set to C<yes>, then both modules declared as C<requires> and those declared as C<build_requires> are treated alike. By setting to C<ask/yes> or C<ask/no>, CPAN.pm asks the user and sets the default accordingly. =head2 Configuration of the allow_installing_* parameters The C<allow_installing_*> parameters are evaluated during the C<make> phase. If set to C<yes>, they allow the testing and the installation of the current distro and otherwise have no effect. If set to C<no>, they may abort the build (preventing testing and installing), depending on the contents of the C<blib/> directory. The C<blib/> directory is the directory that holds all the files that would usually be installed in the C<install> phase. C<allow_installing_outdated_dists> compares the C<blib/> directory with the CPAN index. If it finds something there that belongs, according to the index, to a different dist, it aborts the current build. C<allow_installing_module_downgrades> compares the C<blib/> directory with already installed modules, actually their version numbers, as determined by ExtUtils::MakeMaker or equivalent. If a to-be-installed module would downgrade an already installed module, the current build is aborted. An interesting twist occurs when a distroprefs document demands the installation of an outdated dist via goto while C<allow_installing_outdated_dists> forbids it. Without additional provisions, this would let the C<allow_installing_outdated_dists> win and the distroprefs lose. So the proper arrangement in such a case is to write a second distroprefs document for the distro that C<goto> points to and overrule the C<cpanconfig> there. E.g.: --- match: distribution: "^MAUKE/Keyword-Simple-0.04.tar.gz" goto: "MAUKE/Keyword-Simple-0.03.tar.gz" --- match: distribution: "^MAUKE/Keyword-Simple-0.03.tar.gz" cpanconfig: allow_installing_outdated_dists: yes =head2 Configuration for individual distributions (I<Distroprefs>) (B<Note:> This feature has been introduced in CPAN.pm 1.8854) Distributions on CPAN usually behave according to what we call the CPAN mantra. Or since the advent of Module::Build we should talk about two mantras: perl Makefile.PL perl Build.PL make ./Build make test ./Build test make install ./Build install But some modules cannot be built with this mantra. They try to get some extra data from the user via the environment, extra arguments, or interactively--thus disturbing the installation of large bundles like Phalanx100 or modules with many dependencies like Plagger. The distroprefs system of C<CPAN.pm> addresses this problem by allowing the user to specify extra informations and recipes in YAML files to either =over =item pass additional arguments to one of the four commands, =item set environment variables =item instantiate an Expect object that reads from the console, waits for some regular expressions and enters some answers =item temporarily override assorted C<CPAN.pm> configuration variables =item specify dependencies the original maintainer forgot =item disable the installation of an object altogether =back See the YAML and Data::Dumper files that come with the C<CPAN.pm> distribution in the C<distroprefs/> directory for examples. =head2 Filenames The YAML files themselves must have the C<.yml> extension; all other files are ignored (for two exceptions see I<Fallback Data::Dumper and Storable> below). The containing directory can be specified in C<CPAN.pm> in the C<prefs_dir> config variable. Try C<o conf init prefs_dir> in the CPAN shell to set and activate the distroprefs system. Every YAML file may contain arbitrary documents according to the YAML specification, and every document is treated as an entity that can specify the treatment of a single distribution. Filenames can be picked arbitrarily; C<CPAN.pm> always reads all files (in alphabetical order) and takes the key C<match> (see below in I<Language Specs>) as a hashref containing match criteria that determine if the current distribution matches the YAML document or not. =head2 Fallback Data::Dumper and Storable If neither your configured C<yaml_module> nor YAML.pm is installed, CPAN.pm falls back to using Data::Dumper and Storable and looks for files with the extensions C<.dd> or C<.st> in the C<prefs_dir> directory. These files are expected to contain one or more hashrefs. For Data::Dumper generated files, this is expected to be done with by defining C<$VAR1>, C<$VAR2>, etc. The YAML shell would produce these with the command ysh < somefile.yml > somefile.dd For Storable files the rule is that they must be constructed such that C<Storable::retrieve(file)> returns an array reference and the array elements represent one distropref object each. The conversion from YAML would look like so: perl -MYAML=LoadFile -MStorable=nstore -e ' @y=LoadFile(shift); nstore(\@y, shift)' somefile.yml somefile.st In bootstrapping situations it is usually sufficient to translate only a few YAML files to Data::Dumper for crucial modules like C<YAML::Syck>, C<YAML.pm> and C<Expect.pm>. If you prefer Storable over Data::Dumper, remember to pull out a Storable version that writes an older format than all the other Storable versions that will need to read them. =head2 Blueprint The following example contains all supported keywords and structures with the exception of C<eexpect> which can be used instead of C<expect>. --- comment: "Demo" match: module: "Dancing::Queen" distribution: "^CHACHACHA/Dancing-" not_distribution: "\.zip$" perl: "/usr/local/cariba-perl/bin/perl" perlconfig: archname: "freebsd" not_cc: "gcc" env: DANCING_FLOOR: "Shubiduh" disabled: 1 cpanconfig: make: gmake pl: args: - "--somearg=specialcase" env: {} expect: - "Which is your favorite fruit" - "apple\n" make: args: - all - extra-all env: {} expect: [] commandline: "echo SKIPPING make" test: args: [] env: {} expect: [] install: args: [] env: WANT_TO_INSTALL: YES expect: - "Do you really want to install" - "y\n" patches: - "ABCDE/Fedcba-3.14-ABCDE-01.patch" depends: configure_requires: LWP: 5.8 build_requires: Test::Exception: 0.25 requires: Spiffy: 0.30 =head2 Language Specs Every YAML document represents a single hash reference. The valid keys in this hash are as follows: =over =item comment [scalar] A comment =item cpanconfig [hash] Temporarily override assorted C<CPAN.pm> configuration variables. Supported are: C<build_requires_install_policy>, C<check_sigs>, C<make>, C<make_install_make_command>, C<prefer_installer>, C<test_report>. Please report as a bug when you need another one supported. =item depends [hash] *** EXPERIMENTAL FEATURE *** All three types, namely C<configure_requires>, C<build_requires>, and C<requires> are supported in the way specified in the META.yml specification. The current implementation I<merges> the specified dependencies with those declared by the package maintainer. In a future implementation this may be changed to override the original declaration. =item disabled [boolean] Specifies that this distribution shall not be processed at all. =item features [array] *** EXPERIMENTAL FEATURE *** Experimental implementation to deal with optional_features from META.yml. Still needs coordination with installer software and currently works only for META.yml declaring C<dynamic_config=0>. Use with caution. =item goto [string] The canonical name of a delegate distribution to install instead. Useful when a new version, although it tests OK itself, breaks something else or a developer release or a fork is already uploaded that is better than the last released version. =item install [hash] Processing instructions for the C<make install> or C<./Build install> phase of the CPAN mantra. See below under I<Processing Instructions>. =item make [hash] Processing instructions for the C<make> or C<./Build> phase of the CPAN mantra. See below under I<Processing Instructions>. =item match [hash] A hashref with one or more of the keys C<distribution>, C<module>, C<perl>, C<perlconfig>, and C<env> that specify whether a document is targeted at a specific CPAN distribution or installation. Keys prefixed with C<not_> negates the corresponding match. The corresponding values are interpreted as regular expressions. The C<distribution> related one will be matched against the canonical distribution name, e.g. "AUTHOR/Foo-Bar-3.14.tar.gz". The C<module> related one will be matched against I<all> modules contained in the distribution until one module matches. The C<perl> related one will be matched against C<$^X> (but with the absolute path). The value associated with C<perlconfig> is itself a hashref that is matched against corresponding values in the C<%Config::Config> hash living in the C<Config.pm> module. Keys prefixed with C<not_> negates the corresponding match. The value associated with C<env> is itself a hashref that is matched against corresponding values in the C<%ENV> hash. Keys prefixed with C<not_> negates the corresponding match. If more than one restriction of C<module>, C<distribution>, etc. is specified, the results of the separately computed match values must all match. If so, the hashref represented by the YAML document is returned as the preference structure for the current distribution. =item patches [array] An array of patches on CPAN or on the local disk to be applied in order via an external patch program. If the value for the C<-p> parameter is C<0> or C<1> is determined by reading the patch beforehand. The path to each patch is either an absolute path on the local filesystem or relative to a patch directory specified in the C<patches_dir> configuration variable or in the format of a canonical distro name. For examples please consult the distroprefs/ directory in the CPAN.pm distribution (these examples are not installed by default). Note: if the C<applypatch> program is installed and C<CPAN::Config> knows about it B<and> a patch is written by the C<makepatch> program, then C<CPAN.pm> lets C<applypatch> apply the patch. Both C<makepatch> and C<applypatch> are available from CPAN in the C<JV/makepatch-*> distribution. =item pl [hash] Processing instructions for the C<perl Makefile.PL> or C<perl Build.PL> phase of the CPAN mantra. See below under I<Processing Instructions>. =item test [hash] Processing instructions for the C<make test> or C<./Build test> phase of the CPAN mantra. See below under I<Processing Instructions>. =back =head2 Processing Instructions =over =item args [array] Arguments to be added to the command line =item commandline A full commandline to run via C<system()>. During execution, the environment variable PERL is set to $^X (but with an absolute path). If C<commandline> is specified, C<args> is not used. =item eexpect [hash] Extended C<expect>. This is a hash reference with four allowed keys, C<mode>, C<timeout>, C<reuse>, and C<talk>. You must install the C<Expect> module to use C<eexpect>. CPAN.pm does not install it for you. C<mode> may have the values C<deterministic> for the case where all questions come in the order written down and C<anyorder> for the case where the questions may come in any order. The default mode is C<deterministic>. C<timeout> denotes a timeout in seconds. Floating-point timeouts are OK. With C<mode=deterministic>, the timeout denotes the timeout per question; with C<mode=anyorder> it denotes the timeout per byte received from the stream or questions. C<talk> is a reference to an array that contains alternating questions and answers. Questions are regular expressions and answers are literal strings. The Expect module watches the stream from the execution of the external program (C<perl Makefile.PL>, C<perl Build.PL>, C<make>, etc.). For C<mode=deterministic>, the CPAN.pm injects the corresponding answer as soon as the stream matches the regular expression. For C<mode=anyorder> CPAN.pm answers a question as soon as the timeout is reached for the next byte in the input stream. In this mode you can use the C<reuse> parameter to decide what will happen with a question-answer pair after it has been used. In the default case (reuse=0) it is removed from the array, avoiding being used again accidentally. If you want to answer the question C<Do you really want to do that> several times, then it must be included in the array at least as often as you want this answer to be given. Setting the parameter C<reuse> to 1 makes this repetition unnecessary. =item env [hash] Environment variables to be set during the command =item expect [array] You must install the C<Expect> module to use C<expect>. CPAN.pm does not install it for you. C<< expect: <array> >> is a short notation for this C<eexpect>: eexpect: mode: deterministic timeout: 15 talk: <array> =back =head2 Schema verification with C<Kwalify> If you have the C<Kwalify> module installed (which is part of the Bundle::CPANxxl), then all your distroprefs files are checked for syntactic correctness. =head2 Example Distroprefs Files C<CPAN.pm> comes with a collection of example YAML files. Note that these are really just examples and should not be used without care because they cannot fit everybody's purpose. After all, the authors of the packages that ask questions had a need to ask, so you should watch their questions and adjust the examples to your environment and your needs. You have been warned:-) =head1 PROGRAMMER'S INTERFACE If you do not enter the shell, shell commands are available both as methods (C<CPAN::Shell-E<gt>install(...)>) and as functions in the calling package (C<install(...)>). Before calling low-level commands, it makes sense to initialize components of CPAN you need, e.g.: CPAN::HandleConfig->load; CPAN::Shell::setup_output; CPAN::Index->reload; High-level commands do such initializations automatically. There's currently only one class that has a stable interface - CPAN::Shell. All commands that are available in the CPAN shell are methods of the class CPAN::Shell. The arguments on the commandline are passed as arguments to the method. So if you take for example the shell command notest install A B C the actually executed command is CPAN::Shell->notest("install","A","B","C"); Each of the commands that produce listings of modules (C<r>, C<autobundle>, C<u>) also return a list of the IDs of all modules within the list. =over 2 =item expand($type,@things) The IDs of all objects available within a program are strings that can be expanded to the corresponding real objects with the C<CPAN::Shell-E<gt>expand("Module",@things)> method. Expand returns a list of CPAN::Module objects according to the C<@things> arguments given. In scalar context, it returns only the first element of the list. =item expandany(@things) Like expand, but returns objects of the appropriate type, i.e. CPAN::Bundle objects for bundles, CPAN::Module objects for modules, and CPAN::Distribution objects for distributions. Note: it does not expand to CPAN::Author objects. =item Programming Examples This enables the programmer to do operations that combine functionalities that are available in the shell. # install everything that is outdated on my disk: perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)' # install my favorite programs if necessary: for $mod (qw(Net::FTP Digest::SHA Data::Dumper)) { CPAN::Shell->install($mod); } # list all modules on my disk that have no VERSION number for $mod (CPAN::Shell->expand("Module","/./")) { next unless $mod->inst_file; # MakeMaker convention for undefined $VERSION: next unless $mod->inst_version eq "undef"; print "No VERSION in ", $mod->id, "\n"; } # find out which distribution on CPAN contains a module: print CPAN::Shell->expand("Module","Apache::Constants")->cpan_file Or if you want to schedule a I<cron> job to watch CPAN, you could list all modules that need updating. First a quick and dirty way: perl -e 'use CPAN; CPAN::Shell->r;' If you don't want any output should all modules be up to date, parse the output of above command for the regular expression C</modules are up to date/> and decide to mail the output only if it doesn't match. If you prefer to do it more in a programmerish style in one single process, something like this may better suit you: # list all modules on my disk that have newer versions on CPAN for $mod (CPAN::Shell->expand("Module","/./")) { next unless $mod->inst_file; next if $mod->uptodate; printf "Module %s is installed as %s, could be updated to %s from CPAN\n", $mod->id, $mod->inst_version, $mod->cpan_version; } If that gives too much output every day, you may want to watch only for three modules. You can write for $mod (CPAN::Shell->expand("Module","/Apache|LWP|CGI/")) { as the first line instead. Or you can combine some of the above tricks: # watch only for a new mod_perl module $mod = CPAN::Shell->expand("Module","mod_perl"); exit if $mod->uptodate; # new mod_perl arrived, let me know all update recommendations CPAN::Shell->r; =back =head2 Methods in the other Classes =over 4 =item CPAN::Author::as_glimpse() Returns a one-line description of the author =item CPAN::Author::as_string() Returns a multi-line description of the author =item CPAN::Author::email() Returns the author's email address =item CPAN::Author::fullname() Returns the author's name =item CPAN::Author::name() An alias for fullname =item CPAN::Bundle::as_glimpse() Returns a one-line description of the bundle =item CPAN::Bundle::as_string() Returns a multi-line description of the bundle =item CPAN::Bundle::clean() Recursively runs the C<clean> method on all items contained in the bundle. =item CPAN::Bundle::contains() Returns a list of objects' IDs contained in a bundle. The associated objects may be bundles, modules or distributions. =item CPAN::Bundle::force($method,@args) Forces CPAN to perform a task that it normally would have refused to do. Force takes as arguments a method name to be called and any number of additional arguments that should be passed to the called method. The internals of the object get the needed changes so that CPAN.pm does not refuse to take the action. The C<force> is passed recursively to all contained objects. See also the section above on the C<force> and the C<fforce> pragma. =item CPAN::Bundle::get() Recursively runs the C<get> method on all items contained in the bundle =item CPAN::Bundle::inst_file() Returns the highest installed version of the bundle in either @INC or C<< $CPAN::Config->{cpan_home} >>. Note that this is different from CPAN::Module::inst_file. =item CPAN::Bundle::inst_version() Like CPAN::Bundle::inst_file, but returns the $VERSION =item CPAN::Bundle::uptodate() Returns 1 if the bundle itself and all its members are up-to-date. =item CPAN::Bundle::install() Recursively runs the C<install> method on all items contained in the bundle =item CPAN::Bundle::make() Recursively runs the C<make> method on all items contained in the bundle =item CPAN::Bundle::readme() Recursively runs the C<readme> method on all items contained in the bundle =item CPAN::Bundle::test() Recursively runs the C<test> method on all items contained in the bundle =item CPAN::Distribution::as_glimpse() Returns a one-line description of the distribution =item CPAN::Distribution::as_string() Returns a multi-line description of the distribution =item CPAN::Distribution::author Returns the CPAN::Author object of the maintainer who uploaded this distribution =item CPAN::Distribution::pretty_id() Returns a string of the form "AUTHORID/TARBALL", where AUTHORID is the author's PAUSE ID and TARBALL is the distribution filename. =item CPAN::Distribution::base_id() Returns the distribution filename without any archive suffix. E.g "Foo-Bar-0.01" =item CPAN::Distribution::clean() Changes to the directory where the distribution has been unpacked and runs C<make clean> there. =item CPAN::Distribution::containsmods() Returns a list of IDs of modules contained in a distribution file. Works only for distributions listed in the 02packages.details.txt.gz file. This typically means that just most recent version of a distribution is covered. =item CPAN::Distribution::cvs_import() Changes to the directory where the distribution has been unpacked and runs something like cvs -d $cvs_root import -m $cvs_log $cvs_dir $userid v$version there. =item CPAN::Distribution::dir() Returns the directory into which this distribution has been unpacked. =item CPAN::Distribution::force($method,@args) Forces CPAN to perform a task that it normally would have refused to do. Force takes as arguments a method name to be called and any number of additional arguments that should be passed to the called method. The internals of the object get the needed changes so that CPAN.pm does not refuse to take the action. See also the section above on the C<force> and the C<fforce> pragma. =item CPAN::Distribution::get() Downloads the distribution from CPAN and unpacks it. Does nothing if the distribution has already been downloaded and unpacked within the current session. =item CPAN::Distribution::install() Changes to the directory where the distribution has been unpacked and runs the external command C<make install> there. If C<make> has not yet been run, it will be run first. A C<make test> is issued in any case and if this fails, the install is cancelled. The cancellation can be avoided by letting C<force> run the C<install> for you. This install method only has the power to install the distribution if there are no dependencies in the way. To install an object along with all its dependencies, use CPAN::Shell->install. Note that install() gives no meaningful return value. See uptodate(). =item CPAN::Distribution::isa_perl() Returns 1 if this distribution file seems to be a perl distribution. Normally this is derived from the file name only, but the index from CPAN can contain a hint to achieve a return value of true for other filenames too. =item CPAN::Distribution::look() Changes to the directory where the distribution has been unpacked and opens a subshell there. Exiting the subshell returns. =item CPAN::Distribution::make() First runs the C<get> method to make sure the distribution is downloaded and unpacked. Changes to the directory where the distribution has been unpacked and runs the external commands C<perl Makefile.PL> or C<perl Build.PL> and C<make> there. =item CPAN::Distribution::perldoc() Downloads the pod documentation of the file associated with a distribution (in HTML format) and runs it through the external command I<lynx> specified in C<< $CPAN::Config->{lynx} >>. If I<lynx> isn't available, it converts it to plain text with the external command I<html2text> and runs it through the pager specified in C<< $CPAN::Config->{pager} >>. =item CPAN::Distribution::prefs() Returns the hash reference from the first matching YAML file that the user has deposited in the C<prefs_dir/> directory. The first succeeding match wins. The files in the C<prefs_dir/> are processed alphabetically, and the canonical distro name (e.g. AUTHOR/Foo-Bar-3.14.tar.gz) is matched against the regular expressions stored in the $root->{match}{distribution} attribute value. Additionally all module names contained in a distribution are matched against the regular expressions in the $root->{match}{module} attribute value. The two match values are ANDed together. Each of the two attributes are optional. =item CPAN::Distribution::prereq_pm() Returns the hash reference that has been announced by a distribution as the C<requires> and C<build_requires> elements. These can be declared either by the C<META.yml> (if authoritative) or can be deposited after the run of C<Build.PL> in the file C<./_build/prereqs> or after the run of C<Makfile.PL> written as the C<PREREQ_PM> hash in a comment in the produced C<Makefile>. I<Note>: this method only works after an attempt has been made to C<make> the distribution. Returns undef otherwise. =item CPAN::Distribution::readme() Downloads the README file associated with a distribution and runs it through the pager specified in C<< $CPAN::Config->{pager} >>. =item CPAN::Distribution::reports() Downloads report data for this distribution from www.cpantesters.org and displays a subset of them. =item CPAN::Distribution::read_yaml() Returns the content of the META.yml of this distro as a hashref. Note: works only after an attempt has been made to C<make> the distribution. Returns undef otherwise. Also returns undef if the content of META.yml is not authoritative. (The rules about what exactly makes the content authoritative are still in flux.) =item CPAN::Distribution::test() Changes to the directory where the distribution has been unpacked and runs C<make test> there. =item CPAN::Distribution::uptodate() Returns 1 if all the modules contained in the distribution are up-to-date. Relies on containsmods. =item CPAN::Index::force_reload() Forces a reload of all indices. =item CPAN::Index::reload() Reloads all indices if they have not been read for more than C<< $CPAN::Config->{index_expire} >> days. =item CPAN::InfoObj::dump() CPAN::Author, CPAN::Bundle, CPAN::Module, and CPAN::Distribution inherit this method. It prints the data structure associated with an object. Useful for debugging. Note: the data structure is considered internal and thus subject to change without notice. =item CPAN::Module::as_glimpse() Returns a one-line description of the module in four columns: The first column contains the word C<Module>, the second column consists of one character: an equals sign if this module is already installed and up-to-date, a less-than sign if this module is installed but can be upgraded, and a space if the module is not installed. The third column is the name of the module and the fourth column gives maintainer or distribution information. =item CPAN::Module::as_string() Returns a multi-line description of the module =item CPAN::Module::clean() Runs a clean on the distribution associated with this module. =item CPAN::Module::cpan_file() Returns the filename on CPAN that is associated with the module. =item CPAN::Module::cpan_version() Returns the latest version of this module available on CPAN. =item CPAN::Module::cvs_import() Runs a cvs_import on the distribution associated with this module. =item CPAN::Module::description() Returns a 44 character description of this module. Only available for modules listed in The Module List (CPAN/modules/00modlist.long.html or 00modlist.long.txt.gz) =item CPAN::Module::distribution() Returns the CPAN::Distribution object that contains the current version of this module. =item CPAN::Module::dslip_status() Returns a hash reference. The keys of the hash are the letters C<D>, C<S>, C<L>, C<I>, and <P>, for development status, support level, language, interface and public licence respectively. The data for the DSLIP status are collected by pause.perl.org when authors register their namespaces. The values of the 5 hash elements are one-character words whose meaning is described in the table below. There are also 5 hash elements C<DV>, C<SV>, C<LV>, C<IV>, and <PV> that carry a more verbose value of the 5 status variables. Where the 'DSLIP' characters have the following meanings: D - Development Stage (Note: *NO IMPLIED TIMESCALES*): i - Idea, listed to gain consensus or as a placeholder c - under construction but pre-alpha (not yet released) a/b - Alpha/Beta testing R - Released M - Mature (no rigorous definition) S - Standard, supplied with Perl 5 S - Support Level: m - Mailing-list d - Developer u - Usenet newsgroup comp.lang.perl.modules n - None known, try comp.lang.perl.modules a - abandoned; volunteers welcome to take over maintenance L - Language Used: p - Perl-only, no compiler needed, should be platform independent c - C and perl, a C compiler will be needed h - Hybrid, written in perl with optional C code, no compiler needed + - C++ and perl, a C++ compiler will be needed o - perl and another language other than C or C++ I - Interface Style f - plain Functions, no references used h - hybrid, object and function interfaces available n - no interface at all (huh?) r - some use of unblessed References or ties O - Object oriented using blessed references and/or inheritance P - Public License p - Standard-Perl: user may choose between GPL and Artistic g - GPL: GNU General Public License l - LGPL: "GNU Lesser General Public License" (previously known as "GNU Library General Public License") b - BSD: The BSD License a - Artistic license alone 2 - Artistic license 2.0 or later o - open source: approved by www.opensource.org d - allows distribution without restrictions r - restricted distribution n - no license at all =item CPAN::Module::force($method,@args) Forces CPAN to perform a task it would normally refuse to do. Force takes as arguments a method name to be invoked and any number of additional arguments to pass that method. The internals of the object get the needed changes so that CPAN.pm does not refuse to take the action. See also the section above on the C<force> and the C<fforce> pragma. =item CPAN::Module::get() Runs a get on the distribution associated with this module. =item CPAN::Module::inst_file() Returns the filename of the module found in @INC. The first file found is reported, just as perl itself stops searching @INC once it finds a module. =item CPAN::Module::available_file() Returns the filename of the module found in PERL5LIB or @INC. The first file found is reported. The advantage of this method over C<inst_file> is that modules that have been tested but not yet installed are included because PERL5LIB keeps track of tested modules. =item CPAN::Module::inst_version() Returns the version number of the installed module in readable format. =item CPAN::Module::available_version() Returns the version number of the available module in readable format. =item CPAN::Module::install() Runs an C<install> on the distribution associated with this module. =item CPAN::Module::look() Changes to the directory where the distribution associated with this module has been unpacked and opens a subshell there. Exiting the subshell returns. =item CPAN::Module::make() Runs a C<make> on the distribution associated with this module. =item CPAN::Module::manpage_headline() If module is installed, peeks into the module's manpage, reads the headline, and returns it. Moreover, if the module has been downloaded within this session, does the equivalent on the downloaded module even if it hasn't been installed yet. =item CPAN::Module::perldoc() Runs a C<perldoc> on this module. =item CPAN::Module::readme() Runs a C<readme> on the distribution associated with this module. =item CPAN::Module::reports() Calls the reports() method on the associated distribution object. =item CPAN::Module::test() Runs a C<test> on the distribution associated with this module. =item CPAN::Module::uptodate() Returns 1 if the module is installed and up-to-date. =item CPAN::Module::userid() Returns the author's ID of the module. =back =head2 Cache Manager Currently the cache manager only keeps track of the build directory ($CPAN::Config->{build_dir}). It is a simple FIFO mechanism that deletes complete directories below C<build_dir> as soon as the size of all directories there gets bigger than $CPAN::Config->{build_cache} (in MB). The contents of this cache may be used for later re-installations that you intend to do manually, but will never be trusted by CPAN itself. This is due to the fact that the user might use these directories for building modules on different architectures. There is another directory ($CPAN::Config->{keep_source_where}) where the original distribution files are kept. This directory is not covered by the cache manager and must be controlled by the user. If you choose to have the same directory as build_dir and as keep_source_where directory, then your sources will be deleted with the same fifo mechanism. =head2 Bundles A bundle is just a perl module in the namespace Bundle:: that does not define any functions or methods. It usually only contains documentation. It starts like a perl module with a package declaration and a $VERSION variable. After that the pod section looks like any other pod with the only difference being that I<one special pod section> exists starting with (verbatim): =head1 CONTENTS In this pod section each line obeys the format Module_Name [Version_String] [- optional text] The only required part is the first field, the name of a module (e.g. Foo::Bar, i.e. I<not> the name of the distribution file). The rest of the line is optional. The comment part is delimited by a dash just as in the man page header. The distribution of a bundle should follow the same convention as other distributions. Bundles are treated specially in the CPAN package. If you say 'install Bundle::Tkkit' (assuming such a bundle exists), CPAN will install all the modules in the CONTENTS section of the pod. You can install your own Bundles locally by placing a conformant Bundle file somewhere into your @INC path. The autobundle() command which is available in the shell interface does that for you by including all currently installed modules in a snapshot bundle file. =head1 PREREQUISITES The CPAN program is trying to depend on as little as possible so the user can use it in hostile environment. It works better the more goodies the environment provides. For example if you try in the CPAN shell install Bundle::CPAN or install Bundle::CPANxxl you will find the shell more convenient than the bare shell before. If you have a local mirror of CPAN and can access all files with "file:" URLs, then you only need a perl later than perl5.003 to run this module. Otherwise Net::FTP is strongly recommended. LWP may be required for non-UNIX systems, or if your nearest CPAN site is associated with a URL that is not C<ftp:>. If you have neither Net::FTP nor LWP, there is a fallback mechanism implemented for an external ftp command or for an external lynx command. =head1 UTILITIES =head2 Finding packages and VERSION This module presumes that all packages on CPAN =over 2 =item * declare their $VERSION variable in an easy to parse manner. This prerequisite can hardly be relaxed because it consumes far too much memory to load all packages into the running program just to determine the $VERSION variable. Currently all programs that are dealing with version use something like this perl -MExtUtils::MakeMaker -le \ 'print MM->parse_version(shift)' filename If you are author of a package and wonder if your $VERSION can be parsed, please try the above method. =item * come as compressed or gzipped tarfiles or as zip files and contain a C<Makefile.PL> or C<Build.PL> (well, we try to handle a bit more, but with little enthusiasm). =back =head2 Debugging Debugging this module is more than a bit complex due to interference from the software producing the indices on CPAN, the mirroring process on CPAN, packaging, configuration, synchronicity, and even (gasp!) due to bugs within the CPAN.pm module itself. For debugging the code of CPAN.pm itself in interactive mode, some debugging aid can be turned on for most packages within CPAN.pm with one of =over 2 =item o debug package... sets debug mode for packages. =item o debug -package... unsets debug mode for packages. =item o debug all turns debugging on for all packages. =item o debug number =back which sets the debugging packages directly. Note that C<o debug 0> turns debugging off. What seems a successful strategy is the combination of C<reload cpan> and the debugging switches. Add a new debug statement while running in the shell and then issue a C<reload cpan> and see the new debugging messages immediately without losing the current context. C<o debug> without an argument lists the valid package names and the current set of packages in debugging mode. C<o debug> has built-in completion support. For debugging of CPAN data there is the C<dump> command which takes the same arguments as make/test/install and outputs each object's Data::Dumper dump. If an argument looks like a perl variable and contains one of C<$>, C<@> or C<%>, it is eval()ed and fed to Data::Dumper directly. =head2 Floppy, Zip, Offline Mode CPAN.pm works nicely without network access, too. If you maintain machines that are not networked at all, you should consider working with C<file:> URLs. You'll have to collect your modules somewhere first. So you might use CPAN.pm to put together all you need on a networked machine. Then copy the $CPAN::Config->{keep_source_where} (but not $CPAN::Config->{build_dir}) directory on a floppy. This floppy is kind of a personal CPAN. CPAN.pm on the non-networked machines works nicely with this floppy. See also below the paragraph about CD-ROM support. =head2 Basic Utilities for Programmers =over 2 =item has_inst($module) Returns true if the module is installed. Used to load all modules into the running CPAN.pm that are considered optional. The config variable C<dontload_list> intercepts the C<has_inst()> call such that an optional module is not loaded despite being available. For example, the following command will prevent C<YAML.pm> from being loaded: cpan> o conf dontload_list push YAML See the source for details. =item use_inst($module) Similary to L<has_inst()> tries to load optional library but also dies if library is not available =item has_usable($module) Returns true if the module is installed and in a usable state. Only useful for a handful of modules that are used internally. See the source for details. =item instance($module) The constructor for all the singletons used to represent modules, distributions, authors, and bundles. If the object already exists, this method returns the object; otherwise, it calls the constructor. =item frontend() =item frontend($new_frontend) Getter/setter for frontend object. Method just allows to subclass CPAN.pm. =back =head1 SECURITY There's no strong security layer in CPAN.pm. CPAN.pm helps you to install foreign, unmasked, unsigned code on your machine. We compare to a checksum that comes from the net just as the distribution file itself. But we try to make it easy to add security on demand: =head2 Cryptographically signed modules Since release 1.77, CPAN.pm has been able to verify cryptographically signed module distributions using Module::Signature. The CPAN modules can be signed by their authors, thus giving more security. The simple unsigned MD5 checksums that were used before by CPAN protect mainly against accidental file corruption. You will need to have Module::Signature installed, which in turn requires that you have at least one of Crypt::OpenPGP module or the command-line F<gpg> tool installed. You will also need to be able to connect over the Internet to the public key servers, like pgp.mit.edu, and their port 11731 (the HKP protocol). The configuration parameter check_sigs is there to turn signature checking on or off. =head1 EXPORT Most functions in package CPAN are exported by default. The reason for this is that the primary use is intended for the cpan shell or for one-liners. =head1 ENVIRONMENT When the CPAN shell enters a subshell via the look command, it sets the environment CPAN_SHELL_LEVEL to 1, or increments that variable if it is already set. When CPAN runs, it sets the environment variable PERL5_CPAN_IS_RUNNING to the ID of the running process. It also sets PERL5_CPANPLUS_IS_RUNNING to prevent runaway processes which could happen with older versions of Module::Install. When running C<perl Makefile.PL>, the environment variable C<PERL5_CPAN_IS_EXECUTING> is set to the full path of the C<Makefile.PL> that is being executed. This prevents runaway processes with newer versions of Module::Install. When the config variable ftp_passive is set, all downloads will be run with the environment variable FTP_PASSIVE set to this value. This is in general a good idea as it influences both Net::FTP and LWP based connections. The same effect can be achieved by starting the cpan shell with this environment variable set. For Net::FTP alone, one can also always set passive mode by running libnetcfg. =head1 POPULATE AN INSTALLATION WITH LOTS OF MODULES Populating a freshly installed perl with one's favorite modules is pretty easy if you maintain a private bundle definition file. To get a useful blueprint of a bundle definition file, the command autobundle can be used on the CPAN shell command line. This command writes a bundle definition file for all modules installed for the current perl interpreter. It's recommended to run this command once only, and from then on maintain the file manually under a private name, say Bundle/my_bundle.pm. With a clever bundle file you can then simply say cpan> install Bundle::my_bundle then answer a few questions and go out for coffee (possibly even in a different city). Maintaining a bundle definition file means keeping track of two things: dependencies and interactivity. CPAN.pm sometimes fails on calculating dependencies because not all modules define all MakeMaker attributes correctly, so a bundle definition file should specify prerequisites as early as possible. On the other hand, it's annoying that so many distributions need some interactive configuring. So what you can try to accomplish in your private bundle file is to have the packages that need to be configured early in the file and the gentle ones later, so you can go out for coffee after a few minutes and leave CPAN.pm to churn away unattended. =head1 WORKING WITH CPAN.pm BEHIND FIREWALLS Thanks to Graham Barr for contributing the following paragraphs about the interaction between perl, and various firewall configurations. For further information on firewalls, it is recommended to consult the documentation that comes with the I<ncftp> program. If you are unable to go through the firewall with a simple Perl setup, it is likely that you can configure I<ncftp> so that it works through your firewall. =head2 Three basic types of firewalls Firewalls can be categorized into three basic types. =over 4 =item http firewall This is when the firewall machine runs a web server, and to access the outside world, you must do so via that web server. If you set environment variables like http_proxy or ftp_proxy to values beginning with http://, or in your web browser you've proxy information set, then you know you are running behind an http firewall. To access servers outside these types of firewalls with perl (even for ftp), you need LWP or HTTP::Tiny. =item ftp firewall This where the firewall machine runs an ftp server. This kind of firewall will only let you access ftp servers outside the firewall. This is usually done by connecting to the firewall with ftp, then entering a username like "user@outside.host.com". To access servers outside these type of firewalls with perl, you need Net::FTP. =item One-way visibility One-way visibility means these firewalls try to make themselves invisible to users inside the firewall. An FTP data connection is normally created by sending your IP address to the remote server and then listening for the return connection. But the remote server will not be able to connect to you because of the firewall. For these types of firewall, FTP connections need to be done in a passive mode. There are two that I can think off. =over 4 =item SOCKS If you are using a SOCKS firewall, you will need to compile perl and link it with the SOCKS library. This is what is normally called a 'socksified' perl. With this executable you will be able to connect to servers outside the firewall as if it were not there. =item IP Masquerade This is when the firewall implemented in the kernel (via NAT, or networking address translation), it allows you to hide a complete network behind one IP address. With this firewall no special compiling is needed as you can access hosts directly. For accessing ftp servers behind such firewalls you usually need to set the environment variable C<FTP_PASSIVE> or the config variable ftp_passive to a true value. =back =back =head2 Configuring lynx or ncftp for going through a firewall If you can go through your firewall with e.g. lynx, presumably with a command such as /usr/local/bin/lynx -pscott:tiger then you would configure CPAN.pm with the command o conf lynx "/usr/local/bin/lynx -pscott:tiger" That's all. Similarly for ncftp or ftp, you would configure something like o conf ncftp "/usr/bin/ncftp -f /home/scott/ncftplogin.cfg" Your mileage may vary... =head1 FAQ =over 4 =item 1) I installed a new version of module X but CPAN keeps saying, I have the old version installed Probably you B<do> have the old version installed. This can happen if a module installs itself into a different directory in the @INC path than it was previously installed. This is not really a CPAN.pm problem, you would have the same problem when installing the module manually. The easiest way to prevent this behaviour is to add the argument C<UNINST=1> to the C<make install> call, and that is why many people add this argument permanently by configuring o conf make_install_arg UNINST=1 =item 2) So why is UNINST=1 not the default? Because there are people who have their precise expectations about who may install where in the @INC path and who uses which @INC array. In fine tuned environments C<UNINST=1> can cause damage. =item 3) I want to clean up my mess, and install a new perl along with all modules I have. How do I go about it? Run the autobundle command for your old perl and optionally rename the resulting bundle file (e.g. Bundle/mybundle.pm), install the new perl with the Configure option prefix, e.g. ./Configure -Dprefix=/usr/local/perl-5.6.78.9 Install the bundle file you produced in the first step with something like cpan> install Bundle::mybundle and you're done. =item 4) When I install bundles or multiple modules with one command there is too much output to keep track of. You may want to configure something like o conf make_arg "| tee -ai /root/.cpan/logs/make.out" o conf make_install_arg "| tee -ai /root/.cpan/logs/make_install.out" so that STDOUT is captured in a file for later inspection. =item 5) I am not root, how can I install a module in a personal directory? As of CPAN 1.9463, if you do not have permission to write the default perl library directories, CPAN's configuration process will ask you whether you want to bootstrap <local::lib>, which makes keeping a personal perl library directory easy. Another thing you should bear in mind is that the UNINST parameter can be dangerous when you are installing into a private area because you might accidentally remove modules that other people depend on that are not using the private area. =item 6) How to get a package, unwrap it, and make a change before building it? Have a look at the C<look> (!) command. =item 7) I installed a Bundle and had a couple of fails. When I retried, everything resolved nicely. Can this be fixed to work on first try? The reason for this is that CPAN does not know the dependencies of all modules when it starts out. To decide about the additional items to install, it just uses data found in the META.yml file or the generated Makefile. An undetected missing piece breaks the process. But it may well be that your Bundle installs some prerequisite later than some depending item and thus your second try is able to resolve everything. Please note, CPAN.pm does not know the dependency tree in advance and cannot sort the queue of things to install in a topologically correct order. It resolves perfectly well B<if> all modules declare the prerequisites correctly with the PREREQ_PM attribute to MakeMaker or the C<requires> stanza of Module::Build. For bundles which fail and you need to install often, it is recommended to sort the Bundle definition file manually. =item 8) In our intranet, we have many modules for internal use. How can I integrate these modules with CPAN.pm but without uploading the modules to CPAN? Have a look at the CPAN::Site module. =item 9) When I run CPAN's shell, I get an error message about things in my C</etc/inputrc> (or C<~/.inputrc>) file. These are readline issues and can only be fixed by studying readline configuration on your architecture and adjusting the referenced file accordingly. Please make a backup of the C</etc/inputrc> or C<~/.inputrc> and edit them. Quite often harmless changes like uppercasing or lowercasing some arguments solves the problem. =item 10) Some authors have strange characters in their names. Internally CPAN.pm uses the UTF-8 charset. If your terminal is expecting ISO-8859-1 charset, a converter can be activated by setting term_is_latin to a true value in your config file. One way of doing so would be cpan> o conf term_is_latin 1 If other charset support is needed, please file a bug report against CPAN.pm at rt.cpan.org and describe your needs. Maybe we can extend the support or maybe UTF-8 terminals become widely available. Note: this config variable is deprecated and will be removed in a future version of CPAN.pm. It will be replaced with the conventions around the family of $LANG and $LC_* environment variables. =item 11) When an install fails for some reason and then I correct the error condition and retry, CPAN.pm refuses to install the module, saying C<Already tried without success>. You could use the force pragma like so force install Foo::Bar Or, to avoid a force install (which would install even if the tests fail), you can force only the test and then install: force test Foo::Bar install Foo::Bar Or you can use look Foo::Bar and then C<make install> directly in the subshell. =item 12) How do I install a "DEVELOPER RELEASE" of a module? By default, CPAN will install the latest non-developer release of a module. If you want to install a dev release, you have to specify the partial path starting with the author id to the tarball you wish to install, like so: cpan> install KWILLIAMS/Module-Build-0.27_07.tar.gz Note that you can use the C<ls> command to get this path listed. =item 13) How do I install a module and all its dependencies from the commandline, without being prompted for anything, despite my CPAN configuration (or lack thereof)? CPAN uses ExtUtils::MakeMaker's prompt() function to ask its questions, so if you set the PERL_MM_USE_DEFAULT environment variable, you shouldn't be asked any questions at all (assuming the modules you are installing are nice about obeying that variable as well): % PERL_MM_USE_DEFAULT=1 perl -MCPAN -e 'install My::Module' =item 14) How do I create a Module::Build based Build.PL derived from an ExtUtils::MakeMaker focused Makefile.PL? http://search.cpan.org/dist/Module-Build-Convert/ =item 15) I'm frequently irritated with the CPAN shell's inability to help me select a good mirror. CPAN can now help you select a "good" mirror, based on which ones have the lowest 'ping' round-trip times. From the shell, use the command 'o conf init urllist' and allow CPAN to automatically select mirrors for you. Beyond that help, the urllist config parameter is yours. You can add and remove sites at will. You should find out which sites have the best up-to-dateness, bandwidth, reliability, etc. and are topologically close to you. Some people prefer fast downloads, others up-to-dateness, others reliability. You decide which to try in which order. Henk P. Penning maintains a site that collects data about CPAN sites: http://mirrors.cpan.org/ Also, feel free to play with experimental features. Run o conf init randomize_urllist ftpstats_period ftpstats_size and choose your favorite parameters. After a few downloads running the C<hosts> command will probably assist you in choosing the best mirror sites. =item 16) Why do I get asked the same questions every time I start the shell? You can make your configuration changes permanent by calling the command C<o conf commit>. Alternatively set the C<auto_commit> variable to true by running C<o conf init auto_commit> and answering the following question with yes. =item 17) Older versions of CPAN.pm had the original root directory of all tarballs in the build directory. Now there are always random characters appended to these directory names. Why was this done? The random characters are provided by File::Temp and ensure that each module's individual build directory is unique. This makes running CPAN.pm in concurrent processes simultaneously safe. =item 18) Speaking of the build directory. Do I have to clean it up myself? You have the choice to set the config variable C<scan_cache> to C<never>. Then you must clean it up yourself. The other possible values, C<atstart> and C<atexit> clean up the build directory when you start (or more precisely, after the first extraction into the build directory) or exit the CPAN shell, respectively. If you never start up the CPAN shell, you probably also have to clean up the build directory yourself. =item 19) How can I switch to sudo instead of local::lib? The following 5 environment veriables need to be reset to the previous values: PATH, PERL5LIB, PERL_LOCAL_LIB_ROOT, PERL_MB_OPT, PERL_MM_OPT; and these two CPAN.pm config variables must be reconfigured: make_install_make_command and mbuild_install_build_command. The five env variables have probably been overwritten in your $HOME/.bashrc or some equivalent. You either find them there and delete their traces and logout/login or you override them temporarily, depending on your exact desire. The two cpanpm config variables can be set with: o conf init /install_.*_command/ probably followed by o conf commit =item 20) How do recommends_policy and suggests_policy work, exactly? The terms C<recommends> and C<suggests> have been standardized in https://metacpan.org/pod/CPAN::Meta::Spec In CPAN.pm, if you set C<recommands_policy> to a true value, that means: if you then install a distribution C<Foo> that I<recommends> a module C<Bar>, both C<Foo> and C<Bar> will be tested and potentially installed. Similarly, if you set C<suggests_policy> to a true value, it means: if you install a distribution C<Foo> that I<suggests> a module C<Bar>, both C<Foo> and C<Bar> will be tested and potentially installed. In either case, when C<Foo> passes its tests and C<Bar> does not pass its tests, C<Foo> will be installed nontheless. But if C<Foo> does not pass its tests, neither will be installed. This also works recursively for all recommends and suggests of the module C<Bar>. This has also been illustrated by a cpan tester, who wrote: I just tested Starlink-AST-3.03 which recommends Tk::Zinc; Tk-Zinc-3.306 fails with http://www.cpantesters.org/cpan/report/a2de7c38-810d-11ee-9ad4-e2167316189a ; nonetheless Starlink-AST-3.03 succeeds with http://www.cpantesters.org/cpan/report/9352e754-810d-11ee-90e9-46117316189a =back =head1 COMPATIBILITY =head2 OLD PERL VERSIONS CPAN.pm is regularly tested to run under 5.005 and assorted newer versions. It is getting more and more difficult to get the minimal prerequisites working on older perls. It is close to impossible to get the whole Bundle::CPAN working there. If you're in the position to have only these old versions, be advised that CPAN is designed to work fine without the Bundle::CPAN installed. To get things going, note that GBARR/Scalar-List-Utils-1.18.tar.gz is compatible with ancient perls and that File::Temp is listed as a prerequisite but CPAN has reasonable workarounds if it is missing. =head2 CPANPLUS This module and its competitor, the CPANPLUS module, are both much cooler than the other. CPAN.pm is older. CPANPLUS was designed to be more modular, but it was never intended to be compatible with CPAN.pm. =head2 CPANMINUS In the year 2010 App::cpanminus was launched as a new approach to a cpan shell with a considerably smaller footprint. Very cool stuff. =head1 SECURITY ADVICE This software enables you to upgrade software on your computer and so is inherently dangerous because the newly installed software may contain bugs and may alter the way your computer works or even make it unusable. Please consider backing up your data before every upgrade. =head1 BUGS Please report bugs via L<http://rt.cpan.org/> Before submitting a bug, please make sure that the traditional method of building a Perl module package from a shell by following the installation instructions of that package still works in your environment. =head1 AUTHOR Andreas Koenig C<< <andk@cpan.org> >> =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L<http://www.perl.com/perl/misc/Artistic.html> =head1 TRANSLATIONS Kawai,Takanori provides a Japanese translation of a very old version of this manpage at L<http://homepage3.nifty.com/hippo2000/perltips/CPAN.htm> =head1 SEE ALSO Many people enter the CPAN shell by running the L<cpan> utility program which is installed in the same directory as perl itself. So if you have this directory in your PATH variable (or some equivalent in your operating system) then typing C<cpan> in a console window will work for you as well. Above that the utility provides several commandline shortcuts. melezhik (Alexey) sent me a link where he published a chef recipe to work with CPAN.pm: http://community.opscode.com/cookbooks/cpan. =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Scope/Guard.pm��������������������������������������������������������������������������������������0000444�����������������00000007322�15111204742�0007214 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Scope::Guard; use strict; use warnings; use Carp qw(confess); use Exporter (); our @ISA = qw(Exporter); our @EXPORT_OK = qw(guard scope_guard); our $VERSION = '0.21'; sub new { confess "Can't create a Scope::Guard in void context" unless (defined wantarray); my $class = shift; my $handler = shift() || die 'Scope::Guard::new: no handler supplied'; my $ref = ref $handler || ''; die "Scope::Guard::new: invalid handler - expected CODE ref, got: '$ref'" unless ref($handler) eq 'CODE'; bless [ 0, $handler ], ref $class || $class; } sub dismiss { my $self = shift; my $dismiss = @_ ? shift : 1; $self->[0] = $dismiss; } sub guard(&) { __PACKAGE__->new(shift) } sub scope_guard($) { __PACKAGE__->new(shift) } sub DESTROY { my $self = shift; my ($dismiss, $handler) = @$self; $handler->() unless ($dismiss); } 1; __END__ =pod =head1 NAME Scope::Guard - lexically-scoped resource management =head1 SYNOPSIS my $guard = guard { ... }; # or my $guard = scope_guard \&handler; # or my $guard = Scope::Guard->new(sub { ... }); $guard->dismiss(); # disable the handler =head1 DESCRIPTION This module provides a convenient way to perform cleanup or other forms of resource management at the end of a scope. It is particularly useful when dealing with exceptions: the C<Scope::Guard> constructor takes a reference to a subroutine that is guaranteed to be called even if the thread of execution is aborted prematurely. This effectively allows lexically-scoped "promises" to be made that are automatically honoured by perl's garbage collector. For more information, see: L<http://www.drdobbs.com/cpp/184403758> =head1 METHODS =head2 new my $guard = Scope::Guard->new(sub { ... }); # or my $guard = Scope::Guard->new(\&handler); The C<new> method creates a new C<Scope::Guard> object which calls the supplied handler when its C<DESTROY> method is called, typically at the end of the scope. =head2 dismiss $guard->dismiss(); # or $guard->dismiss(1); C<dismiss> detaches the handler from the C<Scope::Guard> object. This revokes the "promise" to call the handler when the object is destroyed. The handler can be re-enabled by calling: $guard->dismiss(0); =head1 EXPORTS =head2 guard C<guard> takes a block and returns a new C<Scope::Guard> object. It can be used as a shorthand for: Scope::Guard->new(...) e.g. my $guard = guard { ... }; Note: calling C<guard> anonymously, i.e. in void context, will raise an exception. This is because anonymous guards are destroyed B<immediately> (rather than at the end of the scope), which is unlikely to be the desired behaviour. =head2 scope_guard C<scope_guard> is the same as C<guard>, but it takes a code ref rather than a block. e.g. my $guard = scope_guard \&handler; or: my $guard = scope_guard sub { ... }; or: my $guard = scope_guard $handler; As with C<guard>, calling C<scope_guard> in void context will raise an exception. =head1 VERSION 0.21 =head1 SEE ALSO =over =item * L<B::Hooks::EndOfScope|B::Hooks::EndOfScope> =item * L<End|End> =item * L<Guard|Guard> =item * L<Hook::Scope|Hook::Scope> =item * L<Object::Destroyer|Object::Destroyer> =item * L<Perl::AtEndOfScope|Perl::AtEndOfScope> =item * L<ReleaseAction|ReleaseAction> =item * L<Scope::local_OnExit|Scope::local_OnExit> =item * L<Scope::OnExit|Scope::OnExit> =item * L<Sub::ScopeFinalizer|Sub::ScopeFinalizer> =item * L<Value::Canary|Value::Canary> =back =head1 AUTHOR chocolateboy <chocolate@cpan.org> =head1 COPYRIGHT Copyright (c) 2005-2015, chocolateboy. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Types/Serialiser/Error.pm���������������������������������������������������������������������������0000444�����������������00000000703�15111204742�0011374 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=head1 NAME Types::Serialiser::Error - dummy module for Types::Serialiser =head1 SYNOPSIS # do not "use" yourself =head1 DESCRIPTION This module exists only to provide overload resolution for Storable and similar modules that assume that class name equals module name. See L<Types::Serialiser> for more info about this class. =cut use Types::Serialiser (); =head1 AUTHOR Marc Lehmann <schmorp@schmorp.de> http://home.schmorp.de/ =cut 1 �������������������������������������������������������������Types/Serialiser.pm���������������������������������������������������������������������������������0000444�����������������00000021674�15111204742�0010315 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=head1 NAME Types::Serialiser - simple data types for common serialisation formats =encoding utf-8 =head1 SYNOPSIS =head1 DESCRIPTION This module provides some extra datatypes that are used by common serialisation formats such as JSON or CBOR. The idea is to have a repository of simple/small constants and containers that can be shared by different implementations so they become interoperable between each other. =cut package Types::Serialiser; use common::sense; # required to suppress annoying warnings our $VERSION = '1.01'; =head1 SIMPLE SCALAR CONSTANTS Simple scalar constants are values that are overloaded to act like simple Perl values, but have (class) type to differentiate them from normal Perl scalars. This is necessary because these have different representations in the serialisation formats. In the following, functions with zero or one arguments have a prototype of C<()> and C<($)>, respectively, so act as constants and unary operators. =head2 BOOLEANS (Types::Serialiser::Boolean class) This type has only two instances, true and false. A natural representation for these in Perl is C<1> and C<0>, but serialisation formats need to be able to differentiate between them and mere numbers. =over 4 =item $Types::Serialiser::true, Types::Serialiser::true This value represents the "true" value. In most contexts is acts like the number C<1>. It is up to you whether you use the variable form (C<$Types::Serialiser::true>) or the constant form (C<Types::Serialiser::true>). The constant is represented as a reference to a scalar containing C<1> - implementations are allowed to directly test for this. =item $Types::Serialiser::false, Types::Serialiser::false This value represents the "false" value. In most contexts is acts like the number C<0>. It is up to you whether you use the variable form (C<$Types::Serialiser::false>) or the constant form (C<Types::Serialiser::false>). The constant is represented as a reference to a scalar containing C<0> - implementations are allowed to directly test for this. =item Types::Serialiser::as_bool $value Converts a Perl scalar into a boolean, which is useful syntactic sugar. Strictly equivalent to: $value ? $Types::Serialiser::true : $Types::Serialiser::false =item $is_bool = Types::Serialiser::is_bool $value Returns true iff the C<$value> is either C<$Types::Serialiser::true> or C<$Types::Serialiser::false>. For example, you could differentiate between a perl true value and a C<Types::Serialiser::true> by using this: $value && Types::Serialiser::is_bool $value =item $is_true = Types::Serialiser::is_true $value Returns true iff C<$value> is C<$Types::Serialiser::true>. =item $is_false = Types::Serialiser::is_false $value Returns false iff C<$value> is C<$Types::Serialiser::false>. =back =head2 ERROR (Types::Serialiser::Error class) This class has only a single instance, C<error>. It is used to signal an encoding or decoding error. In CBOR for example, and object that couldn't be encoded will be represented by a CBOR undefined value, which is represented by the error value in Perl. =over 4 =item $Types::Serialiser::error, Types::Serialiser::error This value represents the "error" value. Accessing values of this type will throw an exception. The constant is represented as a reference to a scalar containing C<undef> - implementations are allowed to directly test for this. =item $is_error = Types::Serialiser::is_error $value Returns false iff C<$value> is C<$Types::Serialiser::error>. =back =cut BEGIN { # for historical reasons, and to avoid extra dependencies in JSON::PP, # we alias *Types::Serialiser::Boolean with JSON::PP::Boolean. package JSON::PP::Boolean; *Types::Serialiser::Boolean:: = *JSON::PP::Boolean::; } { # this must done before blessing to work around bugs # in perl < 5.18 (it seems to be fixed in 5.18). package Types::Serialiser::BooleanBase; use overload "0+" => sub { ${$_[0]} }, "++" => sub { $_[0] = ${$_[0]} + 1 }, "--" => sub { $_[0] = ${$_[0]} - 1 }, fallback => 1; @Types::Serialiser::Boolean::ISA = Types::Serialiser::BooleanBase::; } our $true = do { bless \(my $dummy = 1), Types::Serialiser::Boolean:: }; our $false = do { bless \(my $dummy = 0), Types::Serialiser::Boolean:: }; our $error = do { bless \(my $dummy ), Types::Serialiser::Error:: }; sub true () { $true } sub false () { $false } sub error () { $error } sub as_bool($) { $_[0] ? $true : $false } sub is_bool ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } sub is_true ($) { $_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } sub is_false ($) { !$_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } sub is_error ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Error:: } package Types::Serialiser::Error; sub error { require Carp; Carp::croak ("caught attempt to use the Types::Serialiser::error value"); }; use overload "0+" => \&error, "++" => \&error, "--" => \&error, fallback => 1; =head1 NOTES FOR XS USERS The recommended way to detect whether a scalar is one of these objects is to check whether the stash is the C<Types::Serialiser::Boolean> or C<Types::Serialiser::Error> stash, and then follow the scalar reference to see if it's C<1> (true), C<0> (false) or C<undef> (error). While it is possible to use an isa test, directly comparing stash pointers is faster and guaranteed to work. For historical reasons, the C<Types::Serialiser::Boolean> stash is just an alias for C<JSON::PP::Boolean>. When printed, the classname with usually be C<JSON::PP::Boolean>, but isa tests and stash pointer comparison will normally work correctly (i.e. Types::Serialiser::true ISA JSON::PP::Boolean, but also ISA Types::Serialiser::Boolean). =head1 A GENERIC OBJECT SERIALIATION PROTOCOL This section explains the object serialisation protocol used by L<CBOR::XS>. It is meant to be generic enough to support any kind of generic object serialiser. This protocol is called "the Types::Serialiser object serialisation protocol". =head2 ENCODING When the encoder encounters an object that it cannot otherwise encode (for example, L<CBOR::XS> can encode a few special types itself, and will first attempt to use the special C<TO_CBOR> serialisation protocol), it will look up the C<FREEZE> method on the object. Note that the C<FREEZE> method will normally be called I<during> encoding, and I<MUST NOT> change the data structure that is being encoded in any way, or it might cause memory corruption or worse. If it exists, it will call it with two arguments: the object to serialise, and a constant string that indicates the name of the data model. For example L<CBOR::XS> uses C<CBOR>, and the L<JSON> and L<JSON::XS> modules (or any other JSON serialiser), would use C<JSON> as second argument. The C<FREEZE> method can then return zero or more values to identify the object instance. The serialiser is then supposed to encode the class name and all of these return values (which must be encodable in the format) using the relevant form for Perl objects. In CBOR for example, there is a registered tag number for encoded perl objects. The values that C<FREEZE> returns must be serialisable with the serialiser that calls it. Therefore, it is recommended to use simple types such as strings and numbers, and maybe array references and hashes (basically, the JSON data model). You can always use a more complex format for a specific data model by checking the second argument, the data model. The "data model" is not the same as the "data format" - the data model indicates what types and kinds of return values can be returned from C<FREEZE>. For example, in C<CBOR> it is permissible to return tagged CBOR values, while JSON does not support these at all, so C<JSON> would be a valid (but too limited) data model name for C<CBOR::XS>. similarly, a serialising format that supports more or less the same data model as JSON could use C<JSON> as data model without losing anything. =head2 DECODING When the decoder then encounters such an encoded perl object, it should look up the C<THAW> method on the stored classname, and invoke it with the classname, the constant string to identify the data model/data format, and all the return values returned by C<FREEZE>. =head2 EXAMPLES See the C<OBJECT SERIALISATION> section in the L<CBOR::XS> manpage for more details, an example implementation, and code examples. Here is an example C<FREEZE>/C<THAW> method pair: sub My::Object::FREEZE { my ($self, $model) = @_; ($self->{type}, $self->{id}, $self->{variant}) } sub My::Object::THAW { my ($class, $model, $type, $id, $variant) = @_; $class->new (type => $type, id => $id, variant => $variant) } =head1 BUGS The use of L<overload> makes this module much heavier than it should be (on my system, this module: 4kB RSS, overload: 260kB RSS). =head1 SEE ALSO Currently, L<JSON::XS> and L<CBOR::XS> use these types. =head1 AUTHOR Marc Lehmann <schmorp@schmorp.de> http://home.schmorp.de/ =cut 1 ��������������������������������������������������������������������auto/LWP/Protocol/https/.packlist�������������������������������������������������������������������0000644�����������������00000000145�15130473315�0012736 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/LWP::Protocol::https.3pm /usr/local/share/perl5/5.32/LWP/Protocol/https.pm ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/LWP/UserAgent/DNS/Hosts/.packlist��������������������������������������������������������������0000644�����������������00000000522�15130473315�0013413 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/LWP::Protocol::http::hosts.3pm /usr/local/share/man/man3/LWP::Protocol::https::hosts.3pm /usr/local/share/man/man3/LWP::UserAgent::DNS::Hosts.3pm /usr/local/share/perl5/5.32/LWP/Protocol/http/hosts.pm /usr/local/share/perl5/5.32/LWP/Protocol/https/hosts.pm /usr/local/share/perl5/5.32/LWP/UserAgent/DNS/Hosts.pm ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/HTTP/Tiny/.packlist����������������������������������������������������������������������������0000644�����������������00000000122�15130473315�0011026 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/HTTP::Tiny.3pm /usr/local/share/perl5/5.32/HTTP/Tiny.pm ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/CPAN/.packlist���������������������������������������������������������������������������������0000644�����������������00000005227�15130473315�0010060 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/bin/cpan /usr/local/bin/cpan-mirrors /usr/local/share/man/man1/cpan-mirrors.1 /usr/local/share/man/man1/cpan.1 /usr/local/share/man/man3/App::Cpan.3pm /usr/local/share/man/man3/CPAN.3pm /usr/local/share/man/man3/CPAN::API::HOWTO.3pm /usr/local/share/man/man3/CPAN::Admin.3pm /usr/local/share/man/man3/CPAN::Debug.3pm /usr/local/share/man/man3/CPAN::Distroprefs.3pm /usr/local/share/man/man3/CPAN::FirstTime.3pm /usr/local/share/man/man3/CPAN::HandleConfig.3pm /usr/local/share/man/man3/CPAN::Kwalify.3pm /usr/local/share/man/man3/CPAN::Mirrors.3pm /usr/local/share/man/man3/CPAN::Nox.3pm /usr/local/share/man/man3/CPAN::Plugin.3pm /usr/local/share/man/man3/CPAN::Plugin::Specfile.3pm /usr/local/share/man/man3/CPAN::Queue.3pm /usr/local/share/man/man3/CPAN::Tarzip.3pm /usr/local/share/man/man3/CPAN::Version.3pm /usr/local/share/perl5/5.32/App/Cpan.pm /usr/local/share/perl5/5.32/CPAN.pm /usr/local/share/perl5/5.32/CPAN/API/HOWTO.pod /usr/local/share/perl5/5.32/CPAN/Admin.pm /usr/local/share/perl5/5.32/CPAN/Author.pm /usr/local/share/perl5/5.32/CPAN/Bundle.pm /usr/local/share/perl5/5.32/CPAN/CacheMgr.pm /usr/local/share/perl5/5.32/CPAN/Complete.pm /usr/local/share/perl5/5.32/CPAN/Debug.pm /usr/local/share/perl5/5.32/CPAN/DeferredCode.pm /usr/local/share/perl5/5.32/CPAN/Distribution.pm /usr/local/share/perl5/5.32/CPAN/Distroprefs.pm /usr/local/share/perl5/5.32/CPAN/Distrostatus.pm /usr/local/share/perl5/5.32/CPAN/Exception/RecursiveDependency.pm /usr/local/share/perl5/5.32/CPAN/Exception/blocked_urllist.pm /usr/local/share/perl5/5.32/CPAN/Exception/yaml_not_installed.pm /usr/local/share/perl5/5.32/CPAN/Exception/yaml_process_error.pm /usr/local/share/perl5/5.32/CPAN/FTP.pm /usr/local/share/perl5/5.32/CPAN/FTP/netrc.pm /usr/local/share/perl5/5.32/CPAN/FirstTime.pm /usr/local/share/perl5/5.32/CPAN/HTTP/Client.pm /usr/local/share/perl5/5.32/CPAN/HTTP/Credentials.pm /usr/local/share/perl5/5.32/CPAN/HandleConfig.pm /usr/local/share/perl5/5.32/CPAN/Index.pm /usr/local/share/perl5/5.32/CPAN/InfoObj.pm /usr/local/share/perl5/5.32/CPAN/Kwalify.pm /usr/local/share/perl5/5.32/CPAN/Kwalify/distroprefs.dd /usr/local/share/perl5/5.32/CPAN/Kwalify/distroprefs.yml /usr/local/share/perl5/5.32/CPAN/LWP/UserAgent.pm /usr/local/share/perl5/5.32/CPAN/Mirrors.pm /usr/local/share/perl5/5.32/CPAN/Module.pm /usr/local/share/perl5/5.32/CPAN/Nox.pm /usr/local/share/perl5/5.32/CPAN/Plugin.pm /usr/local/share/perl5/5.32/CPAN/Plugin/Specfile.pm /usr/local/share/perl5/5.32/CPAN/Prompt.pm /usr/local/share/perl5/5.32/CPAN/Queue.pm /usr/local/share/perl5/5.32/CPAN/Shell.pm /usr/local/share/perl5/5.32/CPAN/Tarzip.pm /usr/local/share/perl5/5.32/CPAN/URL.pm /usr/local/share/perl5/5.32/CPAN/Version.pm �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/CPAN/Meta/Requirements/.packlist���������������������������������������������������������������0000644�����������������00000000347�15130473315�0013427 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/CPAN::Meta::Requirements.3pm /usr/local/share/man/man3/CPAN::Meta::Requirements::Range.3pm /usr/local/share/perl5/5.32/CPAN/Meta/Requirements.pm /usr/local/share/perl5/5.32/CPAN/Meta/Requirements/Range.pm �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/CPAN/Meta/YAML/.packlist�����������������������������������������������������������������������0000644�����������������00000000135�15130473315�0011501 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/CPAN::Meta::YAML.3pm /usr/local/share/perl5/5.32/CPAN/Meta/YAML.pm �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/Canary/Stability/.packlist���������������������������������������������������������������������0000644�����������������00000000140�15130473315�0012505 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/Canary::Stability.3pm /usr/local/share/perl5/5.32/Canary/Stability.pm ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/Scope/Guard/.packlist��������������������������������������������������������������������������0000644�����������������00000000126�15130473315�0011443 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/Scope::Guard.3pm /usr/local/share/perl5/5.32/Scope/Guard.pm ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/Switch/.packlist�������������������������������������������������������������������������������0000644�����������������00000000113�15130473315�0010565 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/Switch.3pm /usr/local/share/perl5/5.32/Switch.pm �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/common/sense/.packlist�������������������������������������������������������������������������0000644�����������������00000000205�15130473315�0011733 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/lib64/perl5/5.32/common/sense.pm /usr/local/lib64/perl5/5.32/common/sense.pod /usr/local/share/man/man3/common::sense.3pm �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/JSON/XS/.packlist������������������������������������������������������������������������������0000644�����������������00000000427�15130473315�0010437 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/bin/json_xs /usr/local/lib64/perl5/5.32/JSON/XS.pm /usr/local/lib64/perl5/5.32/JSON/XS/Boolean.pm /usr/local/lib64/perl5/5.32/auto/JSON/XS/XS.so /usr/local/share/man/man1/json_xs.1 /usr/local/share/man/man3/JSON::XS.3pm /usr/local/share/man/man3/JSON::XS::Boolean.3pm �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/JSON/XS/XS.so����������������������������������������������������������������������������������0000555�����������������00000761130�15130473315�0007527 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������>����)������@����������������@�8� �@�&�%��������������������������������������������������������� ������� ������� ������a������a�������������������������������������������������������������������8������8������8�������������������������������P������P������P������������������������������������������������� ������� ��������������������������������������$�������$��������������Std��������������������� ������� ��������������Ptd���0������0������0������T������T�������������Qtd��������������������������������������������������Rtd���8������8������8����������������������������������GNU����������������������GNU�ח~MPQW4�������U���������� ���������U���{6�������������������������������������������������<������������������������������������������B���������������������k��������������������� ������������������������ �������������������i���������������������V���������������������`����������������������f������������������������������������������~���������������������5����������������������������������������������������������������������������������������������������������U��������������������������������������������������������������������������������������������������������������������������������5���������������������B������������������������������������������)������������������������������������������+������������������������ �������������������B�������������������������������������������������������������������������������������������������������������������������������2������������������������������������������ ���������������������������������������������������������������t�����������������������������������������������������������������������������������������������������������1��������������������� ���������������������V����������������������������������������������������������������������������������������������������������������������������������������������������0���������������������r��������������������������������������������������������������������������������������������������������������������������������+���������������������K����������������������������������������������������������������o������������������������������������������}���������������������y������������������������������������������,��� �������������������l���������������������Y���������������������������������������������������������������F���"�����������������������������������������������������������������������������������������������������������������������������S������������������������ �������D�������__gmon_start__�_ITM_deregisterTMCloneTable�_ITM_registerTMCloneTable�__cxa_finalize�PL_thr_key�pthread_getspecific�Perl_get_sv�Perl_sv_grow�Perl_croak_nocontext�pow�Perl_sv_2pv_flags�__stack_chk_fail�PL_hexdigit�__sprintf_chk�Perl_utf8n_to_uvuni�Perl_mg_get�memcmp�Perl_sv_cmp_flags�Perl_newSVpvn_flags�gcvt�strlen�__snprintf_chk�Perl_newRV�Perl_sv_2mortal�Perl_av_len�Perl_av_fetch�memset�Perl_sv_2iv_flags�Perl_gv_fetchmethod_autoload�Perl_push_scope�Perl_savetmps�Perl_sv_bless�Perl_call_sv�Perl_pop_scope�Perl_free_tmps�Perl_stack_grow�Perl_markstack_grow�Perl_hv_iterinit�Perl_hv_iternext_flags�Perl_hv_iterval�qsort�Perl_save_vptr�Perl_newSV�Perl_sv_utf8_downgrade_flags�Perl_safesysrealloc�memcpy�Perl_newSVpvn�Perl_newSVnv�Perl_newSVsv_flags�Perl_newSV_type�Perl_hv_common�Perl_av_push�Perl_gv_stashsv�Perl_hv_common_key_len�Perl_newRV_noinc�Perl_hv_placeholders_get�Perl_newSViv�Perl_sv_free2�Perl_grok_number�Perl_newSVuv�Perl_hv_iterkeysv�Perl_sv_utf8_upgrade_flags_grow�Perl_sv_upgrade�Perl_sv_newmortal�PL_WARN_NONE�Perl_pv_uni_display�Perl_utf8_length�strcmp�Perl_gv_stashpv�Perl_croak_xs_usage�Perl_sv_derived_from�Perl_sv_2uv_flags�Perl_sv_setuv_mg�Perl_sv_setiv_mg�Perl_sv_chop�memmove�PL_utf8skip�boot_JSON__XS�Perl_xs_handshake�Perl_newXS_deffile�Perl_apply_attrs_string�Perl_newXS_flags�Perl_newSVpv�Perl_get_cv�Perl_xs_boot_epilog�libperl.so.5.32�libc.so.6�GLIBC_2.14�GLIBC_2.4�GLIBC_2.34�GLIBC_2.2.5�GLIBC_2.3.4������������������������������������������������������������������������������������������������������������������������������������������������������������������Y������������c�����ii ���n��������x�����ui ��������ti ���������8�������������*������@�������������@*������H�������������H�����������������������������������������������������������������������������������������������C��������������������I��������������������N��������������������R�����������X��������������������`��������������������h��������������������p��������������������x��������������������������������������������������������������������� �������������������� �������������������� �������������������� �������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��������������������(��������������������0��������� �����������8���������!�����������@���������"�����������H���������#�����������P���������$�����������X���������%�����������`���������&�����������h���������'�����������p���������(�����������x���������)��������������������*��������������������+��������������������,��������������������-��������������������.��������������������/��������������������0��������������������1��������������������2��������������������3��������������������4��������������������5��������������������6��������������������7��������������������8��������������������9���������������������:��������������������;��������������������<��������������������=����������� ���������>�����������(���������?�����������0���������@�����������8���������A�����������@���������B�����������H���������D�����������P���������E�����������X���������F�����������`���������G�����������h���������H�����������p���������J�����������x���������K��������������������L��������������������M��������������������N��������������������O��������������������P��������������������Q��������������������S��������������������T�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������HH��HtH�����5"��%#���h����h���h���h���h���h���h���h���qh���ah ���Qh ���Ah ���1h ���!h ���h���h���h���h���h���h���h���h���h���h���qh���ah���Qh���Ah���1h���!h���h���h���h ���h!���h"���h#���h$���h%���h&���h'���qh(���ah)���Qh*���Ah+���1h,���!h-���h.���h/���h0���h1���h2���h3���h4���h5���h6���h7���qh8���ah9���Qh:���Ah;���1h<���!h=���h>���h?���h@���hA���hB���hC���hD���hE���hF���hG���qhH���ahI���QhJ���AhK���1hL���!%M��D��%E��D��%=��D��%5��D��%-��D��%%��D��%��D��%��D��% ��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%}��D��%u��D��%m��D��%e��D��%]��D��%U��D��%M��D��%E��D��%=��D��%5��D��%-��D��%%��D��%��D��%��D��% ��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%��D��%}��D��%u��D��%m��D��%e��D��%]��D��%U��D��%M��D��%E��D��%=��D��%5��D��%-��D��%%��D��%��D��%��D��% ��D��%��D��%��D��%��D��%��D��H=)��H"��H9tH��Ht ��������H=��H5��H)HH?HHHtH��HtfD������=���u+UH=���Ht H=�� d��]�����w����H9��UH8���HHHPH ���J ���]f.�����ATUH-��S}�}�}�HPxHJHHxx}�HcH������H$������H������H������A}�L`5ITH[]A\�����ATUHHrhHHHr]IHI��IH=��LFGu HL9`s'H(��8HLHH]A\@�HGH]A\H=%��1ff.������AAVIAUAATIUSE��H11%����HHHPH9���0< v<+�� E$<5���EuG����f��A*Hx5fH*Y[AXAA,$]A\A]A^ÐA)E$����H؃fHH H*XD��Et{A),$���LLE$봐G<-���<+���PЀ w0HO11@�HTPp@ vAA)ЅDDE$ERf1LLE$f.�����H���LL\E$E f.�����Ѓ0 RWHЃ0 ~:GHO���PЀ 2WGHOPЀ C1HL&��AxRHI xEpI40@x7DHOEx(HHHHHH H L H ����H��HGHD��UAHdH%(���HD$G < wJH$����t5%� �H=���uJHHPHGH$Hu�A���<1t <0tLAHD$dH+%(���u;HD]D��H��8ZH���HHH$랐E1fD�����G t%H1W vHH5��H91t(���t����HHD�����f.�����AWL<AVAUAATIUHSHH8HdH%(���HD$(1HGH)H9��HD$ HL5��HD$"�����I$<"tH<\ttHJI $HL9���E�IHP_vʍP��IcL>ID$H)L9w��HBI$\I$HPI$�"�ID$H)L9��HBI$\I$HPI$�\fHD$(dH+%(�����H8[]A\A]A^A_D��I$IT$H)L9e��HPHI$�\I$HPI$�r I$IT$H)L9q��HPHI$�\I$HPI$�f�����I$IT$H)L9u��HPHI$�\I$HPI$�n�����I$IT$H)L9y��HPHI$�\I$HPI$�tR�����I$IT$H)L9)��HPHI$�\I$HPI$�b�����E��HD$ ���DI���I$IT$IH)I9J��HPLI$H �\I$HPI$�uI$HPI$HS�� I $HAI$LHI $HAI$LHI $HAI$L���I|$HLH+OHHL$\HL$HIL$I$H HIHDID$EM9D$h���I��I�C��I<$ID$IQ H)H9��DI�������H H N��D���D���1H I$ Hl$ uI|$HLH+OHHL$HL$HIL$I$H HIHDID$I$AD$���HBI$DLH)Hv><wE<?��H��LL$HT$80HL$HT$HA���HH|$ LL$IHH=Ǜ��1HH+WIHLJ0I$IT$HHRHDID$(ID$H)E���I I9 ��I����LHJHD?ȀBI $ ML$LD$I+yH|$HL)HL$IT$LD$H<I<$HHRHDID$LD$ II9v��f.�����I$HHBI$EHl$ uI|$LLD$H+GHHD$HL$LD$HHIL$I$H HIHT IT$h����E�UHD$ ���?%�� LcI|$LH+GHHD$)HL$HHIL$I$H HIHT IT$I|$LH+GHHD$HL$HHIL$I$H HIHT IT$WI|$LH+GHHD$HL$HHIL$I$H HIHT IT$KI|$LH+GHHD$]HL$HHIL$I$H HIHT IT$GI|$LH+GHHD$HL$HHIL$I$H HIHT IT$CI|$HLH+OHHL$HL$HIL$I$H HIHDID$I��wLHJH LH?ȀBD?ȀB I|$HLLD$H+OHHL$OHL$LD$HIL$I$H HIHDID$HI�LHHLH ?ȀBLH?ȀBD?ȀB<LH= ��1ff.�����ATIUSHHHdH%(���HD$1H9W��HBH"It$HcV���HnM �� ���%� �=������HE�HuHPH$��� HvHH9S���HBH"C ���HH9S���HBH:C@4��HD$dH+%(�����H[]A\����H)��8H���HHM H$H\L HH߃HH9SVH{H+WHպ���HWHKH(HH HIHDHCHBH"C #�����HH9S���HBH HH9SH{H+WHպ���HHKH(HH HIHDHCHBH:C@D��HH9S���HBH @�H��8HHM ����HH+WHպ���HPHKH(HH HIHDHCH{H+WHպ���HH{H(HHHIHDHCH9H+WHպ���HHKH(HH HIHDHCH{H+WHպ���HH{H(HHHIHDHCH9H+WHպ���HpHKH(HH HIHDHCff.�����UISHHHpI�HxHcVHcGHH9HHGHH)1DH[]D��AWAVAUATUSHHHH@PuLLhHH-.��H@P���L`}�HLL[H]���A\A]A^A_QLcH-��LpFl }�AA��� LLA���HDIHH@P}Lc}�LxFt 6LLAHA��� A���DSIAff.�����AWAVAUIATUHSH(Df dH%(���HD$1A�� ���A�@�����A� �A�����IE�MuH@HD$HU�H9U��HBLHHE�"AM HT$��� HU�H9U��HBHE�"HD$dH+%(�����H([]A\A]A^A_f.�����A� ����A������A���I��A���t E ��HD$dH+%(���D��1ɺ���H5@��H(H[]A\A]A^A_fD��H��8HT$���LHEID��IM�Lq Ix��ExIx��H=�H}�HEH)@��H5��DD-HU�AHHU�ih��ɍq0@2@%HU�HU�@΍y0@:@%HU�HU�@΍y0@:@%HU�HU�y0@:1@%HU�HU�0HE������Hu�HEH)H.��IE����@(Le�LlILe�Hi��8LHgEe 3H��LI LN��ExLF��H������1UHHE�mf.�����MeAD$ �� � ��Щ���7�� �� �� C��E H��;[LH;HFHH;@ %� �=���0��!LHV;H HHQ;HH���1HHH=��1@�H}H+WIԺ���LHMJ HU�H HIHDHE?����H}H+WIԺ���LXHMJ HU�H HIHDHEI$L*L;-����E@��B��?Hn��;LHL;HHHG;@ %� �=�����LH;HHH ;HH���1HaHH=��1Hټ��8LHAD$ D��H}/���H+wI3HUJ4 Hu�HHRHDHEfL89����E 'HV��;LH4;HHH/;@ %� �=�����LH;HHH;HH���1HIHH=w��1h�����HD$dH+%(�����H(LH[]A\A]A^A_��LEH���I+XLHHUH<H}�HHRHDHEEe IM� LEH���I+XLHHUH<H}�HHRHDHEL5 ��A>LHIʼnËE9E` ��HE�H9E��HPHU��[Ey`HE�H;EV��HPHU��]%� �=���?��Hz �[��HD$dH+%(�����1ɺ���H5��VE��E`E1����AD9���A>Ic1LHIEu_M���I7HD9~HE�H9E��HPHU��,E���@tHM�H9M��HAHE� sE`H}�@HcHEH)H9�� ���H$H$HU�i1ɺ���H5��HUbE��m`HV��8���LHoHHD$dH+%(���D��1ɺ���H5Γ��HM�H9M&��HAHE� L5��A>1H��LHNH$H��A>iA>L8^HA>NHA>>A>HXxHHXx*H;��� ��A>LH+HHHA>H@ L)H ��A>ILHA>HD$Ht$H A>HD$Ht$LH.A>IGHP��IL8H$A>H@Lxq���LHA>WL8I@ t L;` ��HE�H9E��HPHU��(HE�H9EU��HPHU��"AE ��IE�HPIEHDH0H��x���H6H ��HcVL HtHHHE�H9Eh��HPHU��"HE�H9E��HPHU��)HE�H9E��HPHU��[t\DcE~>���)AAIcHI4@HE�H9EC��HPAHU��,E9uI7HHcHI)A>L8HE�H9Ew��HPHU��]A>A>HXPH;XX>��A>HHD$dH+%(�����H([]A\A]A^A_H}���H+GHH$\H $HHHMHE�H HIHT HUHE�H9Eo��HPHU�� E`PHE�U`ED$RHUMcH)I9��H}�L ���HE�LHE�vL55��A>1HH��LHH$H&��A>A>L8HAA>HA>A>HXxHHXxuH;��� ��A>`LH+HHHA>HH@ L)H��A>I,LHaA>HD$Ht$HWA>HD$Ht$LHzA>IL8H$A>H@Lh���LH(A>HL+AE t M;e��A>HLHHHE�H9E/��HPHU�� LLEHHT$I+pLH4$ H4$HT$H<0HuH}�H6HvHD0HEH}���H+GHHHMHHHE�H HIHT HUnH}���H+GHIHMHJ8HE�H HIHT HUE_E`A>HH}���H+OHH $;H $H}HHM�HHRHDHEH9H+O���HH $H $HUHHM�HHRHDHEw1113H}���H+GHH$H $HuHHHE�H6HvHT2HU|A>LL���HcISH}���H+OHH $RH $H}HHM�HHRHDHEH9H+O���HH $H $HUHHM�HHRHDHE`H}LH+GHHHHHE�HEH�H@HDHEH}���H+GHHH}HHHE�HHIHT HUH9QH+G���HHcHMHHHE�H HIHT HUA>WHoH11H}���H+GHIHMHJ HE�H HIHT HU\HcVL 1H}���H+GHIHMHJ HE�H HIHT HUAH}���H+GHI}HMHJ HE�H HIHT HUH}���H+GHHAHMHHHE�H HIHT HUMH}���H+GHIH}HJ(HE�HHIHT HUH9H+G���HIHMHJ(HE�H HIHT HUYH}���H+GHIHMHJ HE�H HIHT HUoH}���H+GHIQHMHJ HE�H HIHT HUA>ELL���HI>A>"H:H LHB;HHH=HpFI$1H�@t*HH@HRHDH0Htx�tH6HtHH=d��1H=6��1I$1H�@t*HH@HRHDH0Htx�tH6HtHH=~��18D%� �=���uIuDH=��111VHd��8 L���1HHLH&;HHH!HpLH;HHHHpf.�����AWAVAUATUSH��dH%(���H$x��G9G` ��HIHH9WO��HBI{AGt C|��L%}��A<$$HHIuMC ���uDI?I;?��HGI}H$x��dH+%(���# ��HĈ��[]A\A]A^A_@�A<$1HHIHtAGn��AG`f��������LLC �����IuLdA<$[1HH.IHr��II9W��HBI,AG��@tII9W��HBI AGp�����AG`I?,@IGHcH)H9S��H ���fI/6fD��L%��A<$HHC ���t<1 fD��A<$|1HHOHuA<$aHHBHcLl$pHD$@X��$���E1@�D ����D$$AA<$ 1HHHM��IcAvID�HPHcBy$����@�AG���Ao`�A<$HLHaH-f�����II9WK��HBI AGIH+WHպ���H8IOH(IH HIHDIG�����II9Wt��HBI AGrII9W��HBI AG`I?AG`AG@IGHcH)H9r��Hھ ���]IIH����$��Ht$H 1���LdAGx��AG`����HcLMt�LC ������IvL9���II9W���HBI,AG���@tM7M9w]��IFIA AGt����AG`I?D4@IGMcH)I9��L ���UM7HD��AGII;G��HPI� A<$wHLH)H II9W��HBI AGIH+WIֺ���LIOJ0IH HIHDIG�����HH+WHպ���HIOH(IH HIHDIGy�����MOH���I+YLHIWH<I?HHRHDIGD��MOHI+yIHLGIWJ<0I?HHRHDIGt����MGHLI+HLHH $H $IWH<I?HHRHDIG=����A<$A<$H���o�)D$oH)L$ oP )T$0oX0D$3�)\$@o`@)d$PH@PHD$`H8A<$HA<$~A<$IrI���HA<$ZHT$Ht$LH���H /���A<$,A<$LpPL;pXA<$H(A<$HI���H+GHHIHHIHHIHT IWH9!H+G���HH}IOHHIH HIHT IWf.�����IH+WHպ���H8IH(IHHIHDIGH9&H+WHպ���H�IOH(IH HIHDIGII9G��HPI� AGhA<$HHcH4����A<$ILHLhl@�I���L+wLkIIM7HHRHDIGI9kL+w���L7IWIM7HHRHDIG;IH+WHպ���H�IH(IHHIHDIGH9yH+WHպ���HIOH(IH HIHDIGEIH+WIֺ���LIJ0IHHIHDIGH99H+WIֺ���LXIOJ0IH HIHDIGMOHI+yHHLIWH<(I?HHRHDIGUIHӺ���H+_HIHIHHIHDIGH9HH+_���HIOHIH HIHDIGIH+WHպ���HwIH(IHHIHDIGH9PH+WHպ���H?IOH(IH HIHDIGI���H+GHIIHJ0IHHIHT IWH9 H+G���HIIOHJ0IH HIHT IWH=bs��1fD��ATUHSHHĀdH%(���HD$x1���u U��ooKoS o[0HC@H��D$L$(;T$8\$HHD$X?@���Hb;I(LHmHD$HPH$HHQL$D$`����HT$������HT$hHH` �_HD$H �D��D$���HT$H$H H+BHAHD$HHPHA�D$t,Hl$u<HD$HT$xdH+%(���,��H[]A\HD$H ��� D$Hl$tċ;?H������H*HE�HpHH9pvHpH}HEfHҁ��H���D��H$H9T$t%HBH$ H=r��1����H|$H+WHպ���HwH|$H(H$HHIHDHD$H9uH+WHպ���H@HL$H(H$H HIHDHD$h�����AWAVAUATUSL$�H���H $�L9uHHE1L7MndH%(���H$8@��1HD$HLd$ D$����HD$fL1�<\���P_��MHH$ @��MnH9sA<"uL)M��IHxH@H)H9��IHLIHXA>"uM��AG IW%�_DAG IH@�D$tAO ��� MWfD��AF<\~b<w3H }��HcH>@�<!~H �����PH���Hnq��HEE1Lu�H$8@��dH+%(���v��HH@��L[]A\A]A^A_MNHLM�Lu�IHtH=��U��H3��H IH IH IHv IHfIHVIHGfxh< 2��E�� MH �HHLIHx?fD��H��8HLHlI+LmM)I��><��AF<?���HD$������1Hq@�AHH)HHT$H9uD$���IHfH=�����A>\��A~u��MNLM�sLu�HH�$H��c��LH L�$LD$���HHCLH ?ȀCLH?ȀCDЃ?ȀC�����H��8RHL$LLHA���HL$HHn��HEXf.�����H=�����H=��w^LD$���HH CLH?ȀCDЃ?ȀCHn��HEuHn��HEH=�D$���HΙ��8w1H5y��HFICHs��HEHm��HE|Hom��HElyH=��!LDD$���HH?ʀCSH�AWAVAUATIUSHHH/dH%(���HD$81E�<{><Z~ [< w3Hy��HcH>D��<(��~L<-��PЀ vvHp��E1ID$����HD$8dH+%(���}��HHL[]A\A]A^A_<"uHH/HD$8dH+%(���L��HH[]A\A]A^A_�����H<0 ��0< ��f.�����HI$HЀ v<.��<E��HBI$JqՁ���u HBI$J0 n���HI$SЀ vHD$0����D$,����}�- ��HT$,Ht$01HA ���D$0H��$87$HHD$8dH+%(���<��HH[]A\A]A^A_鈽HGH)H~ }�fals��Hq��E1ID$jHGH)Hy ��}�nulll ��HH/H-��}�覼}�H蛼HHD$8dH+%(��� ��HP�����HH[]A\A]A^A_5HGH)H ��}�true ��HH/HoXH;��H|��8%HHD$8dH+%(���/ �����HHM��HH/; ���H2AL$`HōAAD$`A;D$/��I4$H< ��<#&��<} ��<"I��IfL~HM<$I9t.L f.�����H<~<\tHJ<"v��H9uLIH���I$< ��<#V ��<: ��HJI $B< ��<#_ ��L ;IH��HLE1j�HE11AWHj7;H 輺AV��AVI$< ��<#Q ��<}G��<,.��HrI4$B<  ��<#U ��<}u��<"D��Hn��ID$;=HtU��UAl$`E1Ha��HH/; ���HFAt$`HōFAD$`A;D$C��I$H< ��<#��<]m��IfD��LIHP;荹LHHI$< ���<#��<] ��<, ��HJI $B< ���<#��<]uAD$tAD$`HI $p���Ha��Du�H)IՋ;A-�� ��HDt��HcH>fD��t< t < [ ��HJI $BH;�t< t < HQI$AH?�t< t < $��HJI $BH�t< t < HVI$FH�t< t <  ��HJI $BH�t< t < HQI$AH�AD$AD$`HI4$H������O@A ��HU���HEH< ��<# ��LHH ��@  ��I$H< X��<#��<)U��HJHI $B< \ ��<# ��L-Hf��IH-��;@  ��LpA~  ��LH;Iն1HHH ��;H$贶H4$1Hok��HoHD$H ��;芶;L 耶H;qH蹷;b;HpxHVHPxH$KH$H;��� ��;H$/LH$H+pHHAEHcH4$ ��;H@ L)HH;$ ��ID$Il$HD$H��ID$AE~[DE1H$HHD$fD��;詵L���LHfH�KDLIH9$uHD$Ht$HHD$;gHt$H0HD$;H@L`K���LH諸;4;L *UHǃ ��U;AWHǃ ��AWM<$Il$5��f�����I $ABE)< ~n<# ��<:O��HQI$A< ~v<#��LHH$M;芴HDLj�LL$HA$���H蘷ZYt< t < ��HQI$AHi�t< t < HJI $BHa�t< t < HVI$FH2�t< t < "HJI $BH�t< t < #HrI4$BH�H1`��ID$C����AD$��HJI $HH< wIr�Hhh��E1ID$@�AD$HQ�����I$HH< wIr�tn< tj< tcHg��H5��E1ID$;ֲUJ��U;轲MDAW��AWE1(�����HJI $BH@�HJI $BHʍp@ Hc��E1ID$HI$At$`;4HHD$8dH+%(���>��HHH[]A\A]A^A_鷱HI4$AL$`;HH蘱IAD$iI|$0�t0HE�Hx�Lht;軱HHHI)I} ��I|$(�%;莱;L(脱H;uH轲;f;HpxHnHhxSH;��� ��;?LH+pHHE�;'H@ L)H ��;LHU;IEIIEI�;Il$(H���HD;AʰH(AJ ��E ��MtAG;裰H(;虰;HhP莰H;hX ��;}H蕰���Hk��HcH>AUi'��Ui��UPUkd,HcHHD$8dH+%(���!��HHH[]A\A]A^A_隳AUi��/UkdU,PAUkd0E,BECPo良HHD$8dH+%(������A0HcfAD$d����HI$< wHrEUi'��Ui��UPUkd,HcHHHD$8dH+%(���*EUi��/UkdU,PEUkd0E,BEUBzm0nHc��ID$fD��H`c��E1ID$@�AD$��HJI $HH< wIrr�AD$HVI$HH< wIrn�HUHEAD$UHJI $HH< wIrmAD$HQ�I$HH< wIraH��H]��HG;Z;E1P}eSHH/HoPHH-և��Hd��Il$PHBI$R0 0���HI$JЀ v߀ETHt< t < HQI$AHwLH1HE]��Hކ��E1ID$AD$`HI$p,H[��ID$H4]��ID$mHT$,Ht$0A ���1H}蟲D$0fWh��HH衯H]��E1ID$AD$FHJ�����I $HH< wIrAD$HHQ�I$HH< wIrLHE1HHkLt$0IcHHLEtL|$0;��Me��1}�-DD;)ƒh��HD$0����HT$,D$,����}�-��A ���1LH\D$0$DEHI$< wHrAD$HJfD��I $HH< wHrAD$'HV�I$HH< wHrHY��ID$AD$`HI$H.Hn\��E1ID$AVHǃ��AVH-��HC��Il$XH'��;ЩqH[��E1ID$GM$$D諩I)HHD$8dH+%(���HHLH[]A\A]A^A_X;qHyAD$3HI $< wHr HOZ��ID$PH}A ���1L良D$0fWe�� HOZ��ID$;H $LLH謨IϨLHHqIIH=]��轭HH=]��詭H茨LHI L(;eH}HLݫQHЫ&;4HLH;HHB; 1HHި;IHH;LHw;Il$0IȧE1E11HPLHj�j�j�HD$ H H;蒧;H(舧H ;yH;j;HpxLvLpxWL;�����;CHH+pHHA;+H@ H)H%��IE;HHE�LHM;H(H$;Hh���HHD;˦L(������;谦L(MtAG;蜦;HhP葦H;hX���;耦H蘦;LeHm�dH���LH!I;BLL���HI;Im�I���HHة;IL(V;HH���H謥HH=U��1趧;迥HǨ+;諥HèIQH=U��1}ff.�����fAWAVAUATIUHSHH���dH%(���H$���G ������������<��HE�HSHpH9s Hl��L-Z��A}���1ɺ���HHVHE�HHLqEuL9psA}�ŤLHHHC@ooKoS o[0H$���$���D$x$���$���HEHD$`HU�HBH$����HD$p����HD$hDŽ$���������L$x�����HT$`Ht$x���< &��<#>��H|$`IMp��HD$`H+EI$M��A}�٣LHID$y���H$���dH+%(���G��H���L[]A\A]A^A_@�HHz�OL-}��A}�rH���H2A}�HVHH蛥Hŋ@ <H}��8/���HHf.����� ���1HH@�t< t < HJHL$`BHf.�����H般H=U��1蒤fH$����OR@�Ht`HT$`t$xH���< b��<#��A}�H9T$hiHW��HD$p:AVHǃ)��AVA}�H"A}�IH���H���H8|��A}�HD$@HxA}�ϡA}�IáI$���HA}�誡HLt$`Ld$hH���A}�M)芡LLLA���A���H}A}�L%V��]HuLt$`L;t$htAG %� �=������MgL}LL)E ���Ht$p1LH="T��D��t< t < HJHL$`BHpf.����������HHT$`< wHrH=fS��1臢����.HJ@�HL$`HH< wHr譠A}�THM9s;LL1HA}�0���1LHޠLt$`ILLL艣@�AWAVIAUATUSHH$z��;͟;L ß;HPxHJHHxHc*譟H@HLH)H��;DmMt$Mc;H@J@ %� �=���"��];H@J,N���1HHIŋ;2H���HUfH������Hŋ@ %�_DE HE�H@@����H;@@ @0H@ L)H���L=)T��LLߟ;���L-Ny��M���;螞HHC;H艞LHH ;HqHH趠;IF[L H[]A\A]A^A_f�����;H@JLhf.�����L���H蛟I^�;LL���H趝IL` f.�����˝���LHKIH5S��LԞ@�AVAUIATUSHPHw��dH%(���HD$H1;o;L e;HPxHJHHxHc*OH@HLH)H���;DuIMc";fH@J,D$H�����D$D$(D$8H$HHL /;I՜;H(˜H@ H)H~/Le;H谜H(HD$HdH+%(���u,HP[]A\A]A^Ë;艜HH���HFH輜H57R��L荝ff.�����fAVAUIATUSHPHv��dH%(���HD$H1;;L ;HPxHJHHxHc*H@HLH)H���;DuIMcқ;fH@J,D$H�����D$D$(D$8H$蝛H1HL ;I胛;H(yH@ H)H~5Le;H^H(HD$HdH+%(���u2HP[]A\A]A^fD��;1HH���HHdH5P��L5D��AWIAVAUATUSHH4u��;ݚ;H(Ӛ;HPxIHJHHxD2躚IcH@HI)IAD$��;IcEnHMcH)肚H@J@ ��;iH@JH@@��;LH@JH@H�L8Ht��H:��;I9t.;H@N<HgO��HLG��;1E1H@JH@LxA���;L$Ht$这Ht$���H};IG8袙H4$���Ha;IG@膙H@ H)H���;Hk;H@JHE�XH(H[]A\A]A^A_fD��;L$5AL$IAFIQHH4G;H4$AMcH4$H@N$�;���H5<N��He;ɘHH���H膘H8H=gL��1萚H5N��LAVIAUATUSHr��;s;H(i;HPxHJHHxLc*SH@JHH)HU��;EeHMc&H@J@ #��; H@JH@@��;H@JH@H�L(Hwr��H���;I9t.—;H@N,賗H M��HL著���;蒗H@JH@L`I|$8�t2I|$@�t*;kH@ H)H~NID$8HHEID$@HE�;AH([]A\A]A^D��;)���H5|L��H襘B; HH���HƖHH=J��1ӘH5`L��L@�AWAVAUATUHSHHq��;譖;L 裖;HPxMHrHpxD2芖IcANH@L$HI)HE�IDx(AE��;IcLHH)GLcd$H@J@ ��;)H@JH@@��; H@JH@H�HHp��Hj��;H9t5ޕ;H@J4Ht$ʕHt$HK��H覕X��;觕H@JH@HHAQD 9D9;脕H@ H)H���;Hi;H@JHE�VH(H[]A\A]A^A_@�;HL$AMc-HL$H@J@ %� �=���tI;HL$;H@N,���HLfHL$FAA!>@�;HL$H@JH�@ @�;衔HH���H^HfD��;HL$t���H5I��HHL$lH=H��18H5I��Hif�����AWIAVAUATUSHHdn��; ;L(;HPxHJHHxLc"IH@Dr(JLH)Ha��;Al$MeHc踓H@H@ .��;蟓H@HH@@��;肓H@HH@H�L8H n��H���;I9t.T;H@L<EHH��HL#���;$;H@HH@Hh H@ L)H~AD#u�;u)Hh��ID$;L(H[]A\A]A^A_˒H8���;蹒LL���HvILhD��;葒���H5G��H H=1F��1ZH5G��L苓ff.�����AWIAVAUATUSHHl��;-;H(#;HPxIHJHHxD2 IcH@HI)IAE��;IcEfHMcH)ӑH@J@ ��;躑H@JH@@��;蝑H@JH@H�L8H$l��H#��;I9t.o;H@N<`HF��HL>N��;?;H@JH@LxA���AH@McN,Mw;MtAV���AV1AE ���ugIG;ԐH@ H)H���;H蹐;H@JHE�覐H(H[]A\A]A^A_@�苐LP��p����;q���LH1����;Q���H5E��H͑�����;)HH���HHJfD��LH荓 H=C��1ڑH5|E��L ff.�����AWIAVAUATUSHHj��;譏;H(裏;HPxIHJHHxD*芏IcH@HI)IAF��;IcEeHMcH)S;IAEIPHHHD$6H@J@ ��;H@JH@@v��;�H@JH@H�L8Hi��H��;I9t.Ҏ;H@N<ÎHD��HL衎��;袎;H@JH@LxA���聎AI�H@McN,���;AE ������RL���H;MwI4HE1E1j�H1LAUjHT$(肐H ;H@ H)H7��;H;H@JHE�ٍH(H[]A\A]A^A_����軍I�LP��B;袍 ���H;IGAE ���*MowHE1E1j�H1Lj�jDHT$(ŏMoH ;IE�Hx�LptL7LHlHI9Mo;MtAUvjAUIG����@�Mϋ;ٌ���H5,B��HU;蹌HH���HvHLH#H=J@��1sH5,B��L褍@�AVIAUATUSHf��;S;L I;HPxHJHHx*4HcH@HLH)Hs��;DmIHc;H@L4IcH@H@ ,��;֋H@HH@@��;蹋H@HH@H�L(H@f��H���;I9t.苋;H@L,|H@��HLZ���;[;H@HH@HhDLL H;I,;H("H@ H)H~FLe;HH([]A\A]A^�;���H5D@��HmA�����;ɊHH���H膊HH=j>��1蓌H5h@��Lċ@�AVIAUATUSHd��;s;L i;HPxHJHHx*THcH@HLH)Hs��;DmIHc!;H@L4IcH@H@ ,��;H@HH@@��;ىH@HH@H�L(H`d��H���;I9t.諉;H@L,蜉H>��HLz���;{;H@HH@Hhd1LL H;IJ;H(@H@ H)H~DLe;H%H([]A\A]A^f�����; ���H5\>��H腊9;HH���H覈HH=<��1賊H5>��L@�AVIAUATUSHHb��dH%(���HD$1;;L(u;HPxHJHHx*`HcH@HLH)Hg��;DeIHcMc*;H@L4H@J@ ��;H@JH@@��;H@JH@H�H(Hlb��H��;H9t.跇;H@J,訇H�=��HH膇��;臇;H@JH@HhpHLL(H;IU;H(KH@ H)H*��LeAV %� �=���u|MfML$$MM)��� ���;HLHq;ILH,;HE�цH(HD$dH+%(������H[]A\A]A^D��;衆1L���HOAV MvIb����;qHM9sLLNHIL�LL5I6D��;1���H5;��H譇U�����; HH���HƅHH=9��1Ї+H5;��Lff.�����AVAUIATUSH_��;装;L 虅;HPxHJHHxLc2胅H@JI)IAv��;AnHc\L,����H@H@ 2��;;H@HH@@��;H@HH@H�L H_��H���;I9t.;H@L$H9:��HL迄���;H@HH@H@Hx(����L` ;MtRAD$;荄LH҆;IxH@L$;i;Hh^LH([]A\A]A^����CLP��f.�����;)���H5|9��H襅H=7��1H=7��1H5q9��LD��AVAUIATUSH^��;Ã;L 蹃;HPxHJHHxLc2裃H@JI)IAU��;AnHc|L$����H@H@ ��;[H@HH@@��;>H@HH@H�L(H]��H���;I9t.;H@L,HY8��HL߂���;;H@HH@HhLm łMtAUvgAUHE ����;HE(����E0����E4�莂;Hh胂JT%H[]A\A]A^f;i���H57��H4LHՅH=5��1%H57��LVfD��AVAUIATUSHZ\��;;L ;HPxHJHHxLc2H@JI)IA��;AnHc輁L$����H@H@ ��;蛁H@HH@@��;~H@HH@H�L(H\��H$��;I9t.P;H@L,AH6��HLg��; ;H@HH@HhLm8MtAU���AULm@;MtAU���AULm;轀MtAU���AULm;虀MtAU���AUHm ;uHtU���U;W;HhLJT%H[]A\A]A^�;1���H55��H譁�����LH蕃LH腃(LHu<LHePHHUbH=y3��1袁H5/5��LӀ�AWIAVAUATUSHHY��;};H(s;HPxIHJHHxD2ZIcH@HI)IAE��;IcEfHMcH)#H@J@ ��; H@JH@@��;~H@JH@H�L8HtY��HC��;I9t.~;H@N<~H4��HL~.��;~H@JH@Lx���ATAG;i~H@ H)H���;HN~;H@JHE�;~H(H[]A\A]A^A_f�����;AMc~;H@J@ %� =��t)};H@N,}���HL}\}H@JH�@ D�����;}HH���Hf}H5fD��;}���H52��H~H=!1��1JH5:3��L{~ff.�����AWIAVAUATUSHHtW��;};H(};HPxIHJHHxD2|IcH@HI)IAE��;IcEfHMcH)|H@J@ ��;|H@JH@@��;|H@JH@H�L8HW��HC��;I9t._|;H@N<P|H1��HL.|.��;/|H@JH@Lx1AOIG; |H@ H)H���;H{;H@JHE�{H(H[]A\A]A^A_@�;AMc{;H@J@ %� =��t1{;H@N,{���HL={_fD��k{H@JH�@ A�����;I{HH���H{H2fD��;!{���H5t0��H|H=.��1|H50��L|ff.�����AWAVAUIATUSHHU��;z;H(z;HPxHJHHxLc2zH@JH)H��;Efzz;H@@#B��ezHm~Hŋ;McN,����HzH@J@ p��;/zH@JH@@S��;zH@JH@H�L0HT��H���;I9t.y;H@N4yH-/��HLy���;y;H@JH@H@D`y;H@Nt(E %Azy������E���M ���LeIn;Ny;HhCyLH(H[]A\A]A^A_fD��#y;HhyH@H@Hl�fD��;x���H5L.��Huz;xLHHK|mH=,��1zH55.��Lyf�����AWAVAUIATUSHHR��;}x;H(sx;HPxHJHHxLc2]xH@JH)H��;Ef:x;H@@#B��%xH-|Hŋ;McN,����xH@J@ p��;wH@JH@@S��;wH@JH@H�L0HYR��H���;I9t.w;H@N4wH,��HLsw���;tw;H@JH@H@L`Yw;H@McNt(E %A7w������E���M ���LeIn; w;Hh�wLH(H[]A\A]A^A_�v;HhvH@H@Hl�fD��;v���H5 ,��H5x;vLHHvpH=?*��1hxH5+��Lwf�����AVAUIATUSHP��;Cv;L 9v;HPxHJHHxLc2#vH@JI)IA��;AnHcuL$����H@H@ R��;uH@HH@@5��;uH@HH@H�L(HEP��H���;I9t.u;H@L,uH*��HL_u���;`uH@HH@HhHM(HtCLu AF %� �=���uSMn;I%uLLHuHE(����E0����E4�;t;HhtJT%H[]A\A]A^Ð;t���L1HuHM(Lu If.�����;t���H5)��H%vH=I(��1rvH5)��Lu�AWAVAUIATUSH(H-N��dH%(���HD$1}�<t}�L 1t}�HPxMHJHHxD:tIcH@HI)IAFX��}�IcA_HHcI)sH@H@ ��}�sH@HH@@��}�sH@HH@H�L(H.N��H5��}�I9t/xs}�H@L,hsH(��HLFs��}�E1CsH@HH@HXA~}�AMcsH@N,Ls MY��AF 8t2}���HS(H`��}�r���1LHvM���HS AE J 1ʁ��� t)}���� ��r���1LHyvAE %� �=���5��IE�HPHT$MmH{ HHpH@H)H9Q��HwHLtHC HT$H�HPHC HHPHA�}�qHPR"u*H���HcB(O��H @HHJHP/��L5H-��I����C0K4 1��H{ Hs(Dٺ�I������L(-��I������HwwKcL>�HHNa<wHrC0C4������L^99xa9w\Ic L>fD��HHN+<:wIs����["w$HH���L��L��L����FH< ��uHSH+wHs(H9s H��DC0C4E��<��}�apHT$HL H{ }�HD$@p}�L85pH@ L)H��HD$}�MgIGHT$H)S(Hs C0����C4�HVHt$IoHt$LHp}�oHPR"u&H���HcB(x(H @HHJHP�����}�oL HD$dH+%(�����H([]A\A]A^A_f< D��B�����A���K4@�}�(oHT$L���HoHT$IfD��n���1LHYpHS(HHC HJHpHg��H=8I��H@� HHsH)HC(@�>�C4H<"u����HHp@<"ti<\u~�HFuC4H����< ~5f�����<#H��C4< y@�FH< Ԅu_�H����sH{ HT$HHpfD��m1ɺ���LH!oAE %� �=���8h�����z,C4LoHHfD��HP@<HufD��PHtHHuC4LD{0ETo�C4Luk07C4AL< @�K0K0;K H=_��1n�����}�l1H5&��HwpHC I�K4/����M~M4klLLHMoLs HC(x}�Hl���H5!��Hm����}� lLL���HkI1E1K0K0.LFfC4H>6HC(����HH@����H=g��1mH5!��LlkH=M��1nmff.������AVL5!��AUATL%USHE��;HkLi!��LH d!��Hƿ1m;k;k;kHZpH5=!��Hl;jHH50!��Hl;jHH5!!��Hl;jH#H5!��Hl;jLH5!!��Hl;H�@(���zjLH5!��Hhl;H�@(���WjLH5 !��HEl;H�@(�@��4jLH5 ��H"l;H�@(� ��jLH5 ��Hk;H�@(���iLH5 ��Hk;H�@(���iLH5 ��Hk;H�@(���iLH5 ��Hk;H�@(���iLH5 ��Hsk;H�@(���biLH5 ��HPk;H�@(h���?iLH5 ��H-k;H�@(���iLH5 ��H k;H�@(���hLH5m ��Hj;H�@(@���hLH5` ��Hj;H�@( ���hLH5T ��L%rHj;H�@(���hLH59 ��Hwj;H�@(���fhLH52 ��HTj;H�@(���ChLH5* ��H1j;H�@(�@�� hLH5 ��Hj;H�@(� ��gLH5 ��Hi;H�@(���gLH5 ��Hi;H�@(���gLH5��Hi;H�@(���gLH5��Hi;H�@(���qgLH5��H_i;H�@(���NgLH5��H<i;H�@(���+gLH5��Hi;H�@(���gLH5��Hh;H�@(@���fLH5��Hh;H�@( ���fLH5��L% ��Hh;H�@(���fHH5��Hh;{fHH5��Heh;^fHH5��HHh;AfHH5��H+h;$fHH5y��Hh;fH0H5)��Hg;eHsH5\��Hg;eH6H5P��Hg;eHH5D��Hg;eHH5?��H}g;veHLHdg;IZeLE1L-��H )��HL[i;4eHH5��Hg;eH0H5��Hg;dHH5��Hf;dE1LL��H)H5��Hf;dH\E1LHL��H5��fHI?�� fH '��Hσ.��H��=���u΋;GdL���He;H>��)d���H5p��He;H?��dH5�����He;HPH ���H>��J ���cH5�����He;HPH ���HS>��J ���c1H5 ��HNe;H ���H>?��ycL1Hld;H�H\���[c[]HA\A]A^d H Hو HH�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������JSON::XS: string size overflow��exactly four hexadecimal digits expected��������malformed or illegal unicode character in string [%.11s], cannot convert to JSON��������out of range codepoint (0x%lx) encountered, unrepresentable in JSON�����%s::FREEZE method returned same object as was passed instead of a new one�������%s::TO_JSON method returned same object as was passed instead of a new one������encountered object '%s', but neither allow_blessed, convert_blessed nor allow_tags settings are enabled (or TO_JSON/FREEZE method missing)������json text or perl structure exceeds maximum nesting level (max_depth set too low?)������cannot encode reference to scalar '%s' unless the scalar is 0 or 1������encountered %s, but JSON can only represent references to arrays or hashes������encountered perl type (%s,0x%x) that JSON cannot handle, check your input data��hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)��������missing low surrogate character in surrogate pair�������missing high surrogate character in surrogate pair������illegal backslash escape sequence in string�����malformed UTF-8 character in JSON string��������unexpected end of string while parsing JSON string������invalid character encountered while parsing JSON string�, or ] expected while parsing array�����, or } expected while parsing object/hash�������filter_json_single_key_object callbacks must not return more than one scalar����filter_json_object callbacks must not return more than one scalar�������malformed JSON string, neither array, object, number, string or atom����malformed JSON string, (tag) must be a string���malformed JSON string, tag value must be an array�������cannot decode perl-object (package does not exist)������cannot decode perl-object (package does not have a THAW method)�malformed number (leading zero must not be followed by another digit)���malformed number (no digits after initial minus)��������malformed number (no digits after decimal point)��������malformed number (no digits after exp sign)�����malformed JSON string, neither tag, array, object, number, string or atom�������attempted decode of JSON text of %lu bytes size, but max_size is set to %lu�����%s, at character offset %d (before "%s")��������JSON text must be an object or array (but found number, string, true, false or null, use allow_nonref to allow this)����object is not of type JSON::XS��incr_text can not be called when the incremental parser already started parsing�JSON::XS::filter_json_single_key_object�\u%04x\u%04x�%lu�%ld�FREEZE�TO_JSON�null�surrogate pair expected�'"' expected�':' expected�) expected after tag�THAW�Types::Serialiser::false�'false' expected�Types::Serialiser::true�'true' expected�'null' expected�(end of string)�garbage after JSON object�klass�JSON::XS�self, v_false= 0, v_true= 0�self�self, enable= 1�self, cb= &PL_sv_undef�self, key, cb= &PL_sv_undef�self, scalar�self, jsonstr�self, max_depth= 0x80000000UL�self, max_size= 0�self, jsonstr= 0�4.03�v5.32.0�XS.c�JSON::XS::CLONE�JSON::XS::new�JSON::XS::boolean_values�JSON::XS::get_boolean_values�JSON::XS::allow_blessed�JSON::XS::allow_nonref�JSON::XS::allow_tags�JSON::XS::allow_unknown�JSON::XS::ascii�JSON::XS::canonical�JSON::XS::convert_blessed�JSON::XS::indent�JSON::XS::latin1�JSON::XS::pretty�JSON::XS::relaxed�JSON::XS::shrink�JSON::XS::space_after�JSON::XS::space_before�JSON::XS::utf8�JSON::XS::get_allow_blessed�JSON::XS::get_allow_nonref�JSON::XS::get_allow_tags�JSON::XS::get_allow_unknown�JSON::XS::get_ascii�JSON::XS::get_canonical�JSON::XS::get_convert_blessed�JSON::XS::get_indent�JSON::XS::get_latin1�JSON::XS::get_relaxed�JSON::XS::get_shrink�JSON::XS::get_space_after�JSON::XS::get_space_before�JSON::XS::get_utf8�JSON::XS::max_depth�JSON::XS::get_max_depth�JSON::XS::max_size�JSON::XS::get_max_size�JSON::XS::filter_json_object�JSON::XS::encode�JSON::XS::decode�JSON::XS::decode_prefix�JSON::XS::incr_parse�JSON::XS::incr_text�lvalue�JSON::XS::incr_skip�JSON::XS::incr_reset�JSON::XS::DESTROY�$�JSON::XS::encode_json�JSON::XS::decode_json�Types::Serialiser::Boolean���QQpQ0R0QPgWG7' NÈ$4PܕV0k>3333H]xT������$@���������������;T��)���=p��B��`H��H��0I��I0��Kp��`L�� M��M��V ��Y@��@Zh��P[��pL��|�� ��`(��НL��`�����,��Pp������ H�������L �� �� �� ��pP ��0 �� �� ��ph �� ��� ��@ �� �������������zR�x ��$������x<���FJ w�?:*3$"�������D���0A�������������\���F6����Hm���(���x���F����FAH uAB��4������0G����BAD L JBI H ABA �<������G ���FEE D(A0 (O BBBB ��������hI{������� ���0��I����AJ s DF ����T��pJV����|I�H���l��J���BFB E(D0D8Gp  8A0A(B BBBF 0�����lSe���BDA G0  AABH �$�����VK����EDD {AA�H�����V���FBB B(A0A8G@} 8G0D(G BBBF �����`��Wf���BBB E(A0D8D` 8A0A(B BBBK ` 8D0A(B BBBK P 8G0A(B BBBE  8A0A(B BBBE H�����ll ���BBB B(A0A8G 8A0A(B BBBE 4���D���xx���BAD G=  AABA ���T���|��Hz=���BBB B(A0A8H Q Dl 8D0A(B BBBA � ����0c���BBB B(D0A8D 8D0A(B BBBA c 8A0A(B BBBM  8A0A(B BBBE  8A0A(B BBBE HJEK;HWAM 8D0A(B BBBE  8A0A(B BBBE ] 8G0A(B BBBE HBBN��L�����|���BBB B(D0D8J 8D0A(B BBBE ���H���H��L���FBE B(A0A8D@ 8A0A(B BBBJ @�����C���FBE A(A0D 0A(A BBBA @�����̟K���FBE A(A0D 0A(A BBBG H�����ؠo���FEB B(A0A8DP 8A0A(B BBBG <���h�����FEB A(A0D (A BBBF ��H�����|���FBB B(A0D8DPl 8A0A(B BBBE H�����Ц���FEB B(A0A8D@@ 8A0A(B BBBA H���@��du���FEB B(A0A8D@ 8A0A(B BBBE h�����\���FEB B(A0A8DPXH`JhBpNP| 8A0A(B BBBH MXH`JhBpRP�<��������FEB A(A0^ (A BBBD ��<���8��,���FEB A(A0` (A BBBJ ��@���x��̰���FEB A(A0D@ 0A(A BBBF <�����x���FBE A(A0Z (A BBBH ��<��������FBE A(A0W (A BBBC ��<���< ��}���FBE A(A0 (A BBBD ��H���| ��ظU���FEB B(A0A8D@W 8A0A(B BBBJ H��� ��U���FEB B(A0A8D@T 8A0A(B BBBE H��� ���7���FBB E(A0A8D@ 8A0A(B BBBG H���` ��7���FBB E(A0A8D@ 8A0A(B BBBD <��� �����FBE A(A0h (A BBBB ��H��� �����FBB E(A0A8D` 8A0A(B BBBC <���8 ��LD���FIB H(A0 (C EBBE ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������*������@*������H�������������I�������������Y������ �������� ������ �������T�������������8���������������������������@��������������������o�����������������( ������������������� ������������� ���������������������@�������������8����������������������������������������������������������� �����������������������������o�����������o����p������o�����������o����������o�������������������������������������������������������������������������������������������P����������������������0 ������@ ������P ������` ������p ������ ������ ������ ������ ������ ������ ������ ������ �������!������!������ !������0!������@!������P!������`!������p!������!������!������!������!������!������!������!������!�������"������"������ "������0"������@"������P"������`"������p"������"������"������"������"������"������"������"������"�������#������#������ #������0#������@#������P#������`#������p#������#������#������#������#������#������#������#������#�������$������$������ $������0$������@$������P$������`$������p$������$������$������$������$������$������$������$������$����������������������������������������������������������������������GCC: (GNU) 11.5.0 20240719 (Red Hat 11.5.0-5)�AV:4g1265�RV:running gcc 11.5.0 20240719�BV:annobin gcc 11.5.0 20240719�GW:0x291056a lto�SP:3�CF:8 lto�FL:-2 lto�GA:1�PI:2�SE:0�SC:1�����������GA$3a1�)������)���������������GA$3a1�� ������ ���������������GA$3a1�T������\���������������GA$3a1�)������*���������������GA$3a1�T������T���������������GA$3a1�T������T���������������GA$3a1� ������ ���������������GA$3a1�\������a������,�������������*������ė�����������������������������!�������� ���*������ė����������"�� =�*������6����������'=���� ���3=�'���%���*���������*���������Tv�Q1�� !� `� �0�*�������������k�� 0�1���-���0�D���@���%0�10�Y���S���=0�J0�B>�*�������� ��� ��Q>�n���l����W0����C��#X0��?+���������K+����������*���������*���������+���������� <�`+���������������<����t���<�������<�����<�`+���������� ��<�����<��������<�+��������U ����������<�l+�������,��� N��<�����<�����,���<��� ����+���������+��������TUQUTQ"1%T"Q" !HTQ"1%T"Q"TQ"1%T"Q"#蟀+(��� � 8 � ;�+������ ��������;�C��#��;�����;�A��5��;�{��o��;�����;�4��&��;�v��h��;�<�����;�����;�e�����;�����;�@��6���,����������T~�Q|�R1�m-����������T~�Q|�R0�-��������T~�Q|�R1��,��������a .������$@�� � 5�.������{�������~��$5�U5�j��h��5�x��r��5�����5�����5�����5�� :�.��������������� ;�-��!��;�f��^��&;�z�����';�`4;�����&/���������9/��������Tv�Q`R2��J/������һ��� � :�P/������V�������u��:����� :�_/�����������j:�:�����/������~���� 9�/������������ �� :�����:�'����$:�����1:�����>:�6��0��:�/����������Q��:�W��S��:�l��f�� :�4������9�������:�����:�����4������k��T~�Qs����K:���� ��L:�����X:����C ��Y:�f:� �� ��]<�4�����������E��<�, ��( ��v<�G ��? ��l<�o ��k ��]<�D4���������<� ��~ ��v<� �� ��l<� �� ��P4���������k4������ ��Tv�QRX@<$���:�M2������������:� �� ��:� �� �� :�5������U�������:� �� ��:� �� ��5������k��T���:�^3��������,��=��:� �� ��:� �� �� :�5������K�������:� �� ��:�4 ��2 ��75������k��T���>�j3�������F����>�> ��< ��>�Q ��O ��3������ۻ��T1Q R ��������:�4��������V�� ��:�^ ��Z ��:�q ��m �� :�7������N�������:� �� ��:� �� ��8������k��T���8<�4�������a��" ��Q<� �� ��G<� �� �� 8<�N8��������N8������A�������Q<� �� ��G<� �� ����:�l5��������q�� ��:�8 ��4 ��:�K ��G �� :�r7������D�������:�^ ��Z ��:�q ��o ��7������k��T���4��������' ��U P������Tv��8��������U ��������:�`0�������|�� ��:� ��y ��:� �� �� :�2������D�������:� �� ��:� �� ��3������k��TQs���:�0���������G ��:� �� ��:� �� �� :�3������D�������:� �� ��:� �� ��3������k��TQs���:�0��������� ��:� �� ��:�; ��7 �� :�b6������D�������:�N ��J ��:�c ��_ ��{6������k��TQs���:� 1���������K ��:�x ��r ��:� �� �� :�6������D�������:� �� ��:� �� ��6������k��TQs���:�`1��������� ��:� �� ��:� �� �� :�6������D�������:���� ��:�����7������k��TQs���:�1���������O ��:�*��$��:�F��B�� :�.7������D�������:�Y��U��:�n��j��G7������k��TQs��� :�1���������:���}��:����� :�6������D�������:�����:�����76������k��TQs����8������һ��� � 8�8������e��������8�����9� ����v:�8���������^��:�<��8��:�U��O�� :�8���������:�o��k��:����� :�:������8�������:�����:�����;������k��Tv�Q1����9���`��9�����9�P+9�����:9������u����Us��9���������9��������>��Tv�QPR2�:���������:��������Tv���v:�:9���������n��:�����:����� :�:9������� ��:������:����� :�9������3�������:�,��(��:�=��;�� :������k��Tv�Q1����v:�[9���������q��:�I��E��:�`��\�� :�[9���������:�s��o��:����� :�d:������3�������:�����:�����|:������k��Tv�Q1����9�@:�������*��p)#��9�����:�@:�������5��i��:�����:����� :�0;������3�������:�����:�����H;������k��Tv�Q1��� v:�c;�������@��:��� ��:�����:�c;�������c;������=�������:�$��"��:�/��-�� :�l;������4�������:�;��7��:�L��J��;������k��Tv�Q1�����9�:�������K��r)��9�X��T��:�:�������V����:�k��g��:���|�� :�;������3�������:�����:�����;������k��Tv�Q1��� v:�;�������a��:�����:�����:�;�������;������=�������:�����:����� :�;������4�������:�����:�����;������k��Tv�Q1�����9������u����Us��<������һ��� v� 8� <������K�������3��8�����8��� ��8�+��)��8�5��3��8�@��>��8�M��I��8�^��\��_<������3��� �� a8�p<��������������q8�n��f��|8�����<���������<����������R2��=���������=����������T~�Q|�R}��J=���������m=��������T�Q|�R~��� Q� w� 6�=������f������l4��6�����6�����6�l�����6�6�K��G��v:�=�����������:�^��Z��:�u��q�� :�=���������:�����:����� :�A������9�������:�����:�����B������k��T|�Q1����v:�>�����������:�����:����� :�>���������:�����:� ���� :�A������@�������:�����:�0��.��A������k��T|�Q1����>������u����Uv�T~��>���������>��������T}�QR2��6�@����������i1��7�\��8�� 7�������"7�����/7�����<7���"��A7���{��N7�E����S7�����`7�<��(��l7�d����q7�����BG���������VG���������kG���������^Q���������fQ������sL���v:�?H�������o��* ��:�����:����� :�?H�������z��:�����:����� :�P������<�������:�����:� ����Q������k��T|�Q1����v:�XH���������+ S��:�����:�4��0�� :�XH���������:�G��C��:�\��X�� :�P������<�������:�o��k��:���~��P������k��T|�Q1����v:�H���������- ��:�����:����� :�H���������:�����:����� :�BO������<�������:�����:�����ZO������k��T|�Q1����v:�H���������. ��:� �� ��:�$�� �� :�H���������:�7��3��:�L��H�� :�O������<�������:�_��[��:�r��n��O������k��T|�Q1����v:�H���������/ f��:�����:����� :�H���������:�����:����� :�O������<�������:�����:�����O������k��T|�Q1����7���`��7�����v:�@I���������83��:�8��4��:�O��K�� :�@I���������:�b��^��:�w��s�� :�M������A�������:�����:�����M������k��TQ1����@I��������K��Uv��mI��������Uv���v:�I���������A ��:�����:����� :�I���������:�����:����� :�P������<�������:������:�����P������k��Ts�Q1����G���������"G���������*G������PL��2G���������:G������UL��G���������G���������G������v4����T|��G���������G������{4����T�G���������G������ZL����TQ}��G���������H���������H������_L����T�Q3�)H���������H������u��4��Uv��I���������;Q���������NQ������nL��q��T�Q�R2�8R��������U ��������7��� ��7�8��&��7�=����7���|��J��������� K��������� K���������)O���������1O������sL���I���������I���������I���������I������dL��J���������J���������J������PL��J���������J������UL��8K���������TK���������_K������v4����T|��lK���������yK������{4����T�K���������K������ZL����TQ}��K���������K���������K������_L��5 ��T}�Q2�K���������K���������L��������m ��Uv�T}��L���������M������iL��M���������M������nL�� ��T�Q�R1�Q��������U @��������>������u�� ��UU�iB���������tB������v4��!��T|��~B���������B������{4��6!��Tv��B���������B������v4��[!��T|��B���������B������{4��!��Tv��B���������B��������!��Tv�Q0R2�B��������!��U �������F���������F������FL��!��T|�Q2�F���������G������KL��/"��T}�Q ������R0�J���������J������KL��f"��T}�Q ������R0�sQ���������~Q������v4��"��T|��Q���������Q������{4��Tv���7�H��$��7�����XC������~��"��U|��C���������C������v4��#��T|��C���������C������{4��)#��Tv��C���������C������v4��N#��T|��C���������C������{4��s#��Tv��C���������C��������#��Tv�Q0R2�D��������#��U x�������R���������R������v4��#��T|��R���������R������{4��Tv���:9�D�������S��g0��S9�����F9�!����S��_9�o��a��j9�����v:�D���������@ %��:�����:����� :�D���������:�����:����� :�L������<�������:�/��+��:�B��>��L������k��T�Q1����9�E���������U'W&��9�W��Q�� 9�\J����������9�q��m�� 9���9�����:�gJ����������%��:�����:����� :�oN������:�������:�����:�����N������k��Ts�Q|����>�sJ�������sJ�������������>�����>�����>�����J��������T Q|������v:� E���������X'��:�����:�6��2�� :� E���������:�I��E��:�^��Z�� :�jL������<�������:�q��m��:�����L������k��Ts�Q1����9�fE���������D(��9����� 9�L���������"9�����:�L������� ��&'��:�����:����� :�CP������7�������:�����:� ����[P������k��T}�Q1��� v:�zP���������':� �� ��:� �� ��:�zP�������zP������A�������:�' ��% ��:�2 ��0 �� :�P������8�������:�> ��: ��:�Q ��M ��P������k��T}�Q1������w9�"��9.��x9�t ��` ��9�E�������A��J )��9� �� �� 9� F�������Q��9� �� �� 9�\��9� �� ��:�F��������g��)��:�!�� !��:�-!��)!�� :�$L������F�������:�@!��<!��:�S!��Q!��@L������k��TQ���>�*F�������*F�������������>�_!��[!��>�r!��p!��>�~!��|!��8F��������T Q�����9�E�������w��R -��9�!��!��v:�E���������.*��:�!��!��:�!��!�� :�E���������:�!��!��:�!��!�� :�I������A�������:� "��"��:� "��"��J������k��TQ1����9�E���������38,��9�3"��/"��:�E���������|+��:�F"��B"��:�["��W"�� :� M������8�������:�n"��j"��:�"��"��%M������k��TQ1��� v:�DM���������:�"��"��:�"��"��:�DM�������DM������B�������:�"��"��:�"��"�� :�MM������9�������:�"��"��:�"��"��bM������k��TQ1����� 9�F���������19�"��"�� 9�F���������"9�"��"��:�F���������&-��:�"��"��:�#��#�� :�M������8�������:�%#��!#��:�8#��6#��N������k��TQ1��� v:�-N���������':�B#��@#��:�M#��K#��:�-N�������-N������B�������:�W#��U#��:�b#��`#�� :�6N������9�������:�n#��j#��:�#��#��KN������k��TQ1�������E���������E������AL��-��T|�Q}� $ &R0�E�������� .��Uv��[F������u��Uv�T ������Q4R0��9�`F���������U/��9�#��#�� 9�0J������� ��"9�#��#��:�0J���������&/��:�#��#��:�#��#�� :�N������7�������:�#��#��:�#��#��N������k��Ts�Q1��� v:�N�������)��':�$��$��:�$��$��:�N�������N������A�������:�$��$��:�($��&$�� :�N������8�������:�4$��0$��:�G$��C$��N������k��Ts�Q1������D���������D������<L��/��T|��Q��������U  ���������%A���������0A������v4��&0��T|��:A���������EA������{4��K0��Tv��_A���������jA������v4��p0��T|��tA���������A������{4��0��Tv��A���������A��������0��Tv�Q0R2�A��������0��U �������B��������� C��������1��T|��=D������4��!1��UU�R���������R������v4��F1��T|��R���������R������{4��Tv����:� @�������9��1��:�Z$��V$��:�q$��m$�� :�C������8�������:�$��$��:�$��$��-C������k��T|�Q/���6�I��2��6�$��$��6�$��$��6�%��%��6�%��%�� :�B?�������T�� :�F'��B'��:�['��W'�� :�D������<�������:�l'��j'��:�v'��t'��D������k��Ts�Q6����:�@�������_�� %3��:�'��~'��:�'��'�� :�=D������7�������:�'��'��:�'��'��XD������k��Ts�QF���%^>�@�������������R3��>�y>�m>��^>�@��������@�������������3��>�'��'��y>�'��'��m>�'��'��&@������TFQ1R ��H@������l4��3��U?�T@������q4��3��U|��n@���������y@��������4��T}��L������һ��\R��������@4��U ������Q|��sR���������R��������T}�Q0R2�� U� >� � g � 7�R������ ������<L��7�'��'��7�_(��;(��7�$)��(��v:�'S�������j��s5��:�)��)��:�)��)�� :�'S�������z��:�*��*��:�)*��#*�� :�X������@�������:�C*��?*��:�T*��R*��X������k��Tv�Q1����7���'A��7�v*��\*��7���?��7�*��*��8�!+��+��8�{ 8�z+��h+��9�tS�������tS�������������+Z6��9�+��+��9�tS�������������9� 9�tS�������������9�:�tS�������������:�:�����-8���6��28�+��+��[���������[������L��6��T v� $ &3$�[���������[������{4��T}���9�V��������� I8��9�+��+�� 9�[���������"9�+��+��:�[���������&7��:�,��,��:�,��,�� :�A^������6�������:�/,��+,��:�B,��>,��Y^������k��T~�Q1��� v:�w^���������':�S,��Q,��:�^,��\,��:�w^�������w^������@�������:�h,��f,��:�s,��q,�� :�^������7�������:�,��{,��:�,��,��^������k��T~�Q1������9�W�������,��9��9�,��,�� 9�W��������K��9�,��,�� 9�K��9�,��,��:�W��������V��-9��:�,��,��:�-��-�� :�@Y������H�������:�-��-��:�.-��,-��]Y������k��TzQ~����>�W�������W������ �������>�8-��6-��>�B-��@-��>�N-��L-��W��������T Q~������9�OW�������k��R=��9�_-��W-��v:�OW���������.n:��:�-��|-��:�-��-�� :�OW���������:�-��-��:�-��-�� :�@X������@�������:�-��-��:�-��-��XX������k��T~�Q1����9�vW���������3;��9�-��-��:�vW���������;��:�.��-��:�.��.�� :�[������/�������:�(.��&.��:�2.��0.��[������k��T~�Q1��� v:�\���������:�<.��:.��:�H.��F.��:�\�������\������9�������:�R.��P.��:�].��[.�� :�\������0�������:�g.��e.��:�q.��o.��)\������k��T~�Q1����� 9� X���������19�}.��y.�� 9� X���������"9�.��.��:� X���������&<��:�.��.��:�.��.�� :�\������3�������:�.��.��:�.��.��\������k��T~�Q1��� v:�\���������':�.��.��:�.��.��:�\�������\������=�������:�.��.��:�/��/�� :�\������4�������:�/��/��:�#/��!/��]������k��T~�Q1�������9�W��������� >��9�//��+/�� 9�W���������"9�B/��>/��:�W���������&>��:�U/��Q/��:�j/��f/�� :�Z������6�������:�}/��y/��:�/��/��Z������k��Ts�Q1��� v:�Z�������"��':�/��/��:�/��/��:�Z�������Z������J�������:�/��/��:�/��/�� :�Z������A�������:�/��/��:�/��/��Z������k��Ts�Q1������uU���������U������}L��?��Ts�Q0�V������L��4?��U}�TzQ8R  <�������.W������ ��R?��U�T~��GW��������j?��U�� X���������X������L��Ts�Q~���?8�Y������������@��@8�zY���������Y���������Y������PL��Y���������Y������UL��Z���������Z���������Z������L��(@��T~�&Z���������KZ������L��g@��U}�TzQ8R p<�������TZ���������aZ���������pZ���������xZ������dL��Z���������Z������iL���T���������T������xL��@��Ts��U���������U������}L��A��Ts�Q0�U���������*U������xL��Ts���v:�wS�������-���A��:�/��/��:� 0��0�� :�wS�������8��:�0��0��:�20��.0�� :�X������@�������:�C0��A0��:�M0��K0��X������k��Ts�Q1����9�S�������C�� ^C��9�[0��U0�� 9�PV�������X��"9�u0��q0��:�PV�������h��&B��:�0��0��:�0��0�� :�]������3�������:�0��0��:�0��0��]������k��Tv�Q1��� v:�^�������s��':�0��0��:�0��0��:�^�������^������=�������:�0��0��:�0��0�� :� ^������4�������:�0��0��:�1��1��!^������k��Tv�Q1������9�S���������D��9�1��1�� 9�T����������9�01��,1�� 9���9�C1��?1��:�T����������@D��:�d1��`1��:�w1��s1�� :��Y������@�������:�1��1��:�1��1��Y������k��T~�Qv����>�T�������T������ �������>�1��1��>�1��1��>�1��1��T��������T Qv������9�>T���������eH��9�1��1��v:�>T���������.E��:�1��1��:� 2�� 2�� :�>T���������:� 2��2��:�52��12�� :�V������@�������:�H2��D2��:�Y2��W2��(V������k��Tv�Q1����9�eT������� ��3F��9�e2��a2��:�eT������� ��(F��:�x2��t2��:�2��2�� :�[������3�������:�2��2��:�2��2��([������k��Tv�Q1��� v:�C[������� ��:�2��2��:�2��2��:�C[�������C[������=�������:�2��2��:�2��2�� :�L[������4�������:�2��2��:�2��2��`[������k��Tv�Q1����� 9�U�������" ��19�3��3�� 9�U�������2 ��"9�3��3��:�U�������= ��&G��:�+3��'3��:�@3��<3�� :�H\������3�������:�S3��O3��:�d3��b3��`\������k��Tv�Q1��� v:�{\�������H ��':�n3��l3��:�y3��w3��:�{\�������{\������=�������:�3��3��:�3��3�� :�\������4�������:�3��3��:�3��3��\������k��Tv�Q1�������9�U�������S �� I��9�3��3�� 9�sV�������c ��"9�3��3��:�sV�������n ��&0I��:�3��3��:�3��3�� :�a]������3�������:� 4�� 4��:�4��4��y]������k��Ts�Q1��� v:�]�������y ��':� 4��4��:�+4��)4��:�]�������]������=�������:�54��34��:�@4��>4�� :�]������4�������:�J4��H4��:�T4��R4��]������k��Ts�Q1������9�V������� ��-5K��9�`4��\4�� 9�V�������� ��9�s4��o4�� 9� ��9�4��4��:�V�������� ��J��:�4��4��:�4��4�� :�(]������9�������:�4��4��:�4��4��A]������k��Tv�Qs����>�V�������V������ �������>�4��4��>�5��5��>�5��5��V��������T Qs������\S���������gS������xL��ZK��Ts��S���������S������}L��K��Ts�Q0�T������ ��K��U�T}��T��������K��U��%T���������2T������}L��K��Ts�Q0�U���������U������L��L��Ts�Q}��^��������.L��U  �������^������һ��� � � � � � ^� � &� F� � � G� � � � � � � I6�^������x������O��Y6�+5��5��f6�p5��f5��s6�~9�_������� ��ZN��9�5��5�� 9�`�������� ��"9�5��5��:�`������� ��&M��:�5��5��:�5��5�� :�`������7�������:� 6��6��:�6��6��`������k��Tv�Q1��� v:�a������� ��':�%6��#6��:�06��.6��:�a�������a������<�������:�=6��;6��:�H6��F6�� :� a������7�������:�W6��S6��:�h6��f6�� a������k��Tv�Q1������<�:`�������:`������F�������N��<�r6��p6�� =�h`�������������N�� =�~6��|6��u`������O���A`���������V`������O��Tv�Q1R2��^��������O��Uv��A_���������N_������L��*O��T@�X_���������c_������{4��OO��T|��_��������nO��U~Tv��`��������O��U `�������Ha������һ���  � 5� 4�Pa������=������R��4�6��6��4�7��7��4�h7��R7��4�!8��7�� 5�b������5� ��R��5�~"5�9��|9��/5� ��Q��45�:��:��^5�< �� Q��_5�~ ]<�d��������Q ��<�;��;��v<�;��;��l<�;��;��]<� e�������k ��<�;��;��v<�;��;��l<�;��;��.e���������Ge������ ��T~�Q}�R~X@<$���� @5�{ ��E5�;��;��Q5�*<��<��8<�d������� ��!Q��Q<�<��<��G<�<��<��8<�d������� ��Q<�=��=��G<�'=��!=����c��������Q��Uu��d���������� n5� ��o5�G=��A=��|5� ��`R��}5�c=��]=��$?�b������� �� ER��H?�=��}=��<?�=��=��0?�=��=�� b��������T|�Qs���c������k��U�Qs���c���������c������R��T|�Qs���� f���������f������R��R��T ������Q0�Wf������һ��� � ^1�f������c������h��n1�0>��=��{1�_4�w������� ��U��o4�@��@�� ��|4�@��@��4�NA��LA��4�B;�g�������s ��I S��R;�bA��XA��s ��];�j;�w;�A��A��0h��������S��Uv�TQR0X:�Qy��������UvTQR0X:���4� ��U��4�A��A��4� ��T��4�4�pB��jB��z���������+z������ i��dT��Tv�Q}� $ &R~��|���������|������h��T��T��|���������|������i��T���B;�az������� ��BMU��R;�B��B�� ��];�j;�w;�B��B��z��������'U��Uv�T~�QR0X:�g|��������UvT~�R0X:���fu���������u������h��u���������v���������{���������U��U��|������R���Ih���������xh������h��a.���2�i������� ��$D`��2�C��B�� ��2�C��C��2�$D��D��2�<k������2�o ��]V��2�D��D��i���������i������h��T<��6� ��V��6� 6� ��!6�>E��8E��/6� ��;6����2� ��Y��2�nE��^E��2�E��E��2�3� ��X��3�E��E��6�8 ��W��6� 6�Q ��!6�=F��7F��/6�o ��;6����6�jj�������| ��yW��6�cF��]F�� 6� ��!6�F��}F��/6�w������������� ;6����=�j�������� ��W��>�F��F��>�F��F�� >� ��>�F��F��x������i��T~����=�s{�������� ��;X��>�F��F��>�G�� G�� >� ��>�*G��"G��}������i��T~����7j������O��SX��U|��j������R��kX��U|��j���������j������h��X��Tv�Q~�R0X0Y0�j���������l{���������� 43� ��53�VG��RG��B3�kG��iG��6�p������� ��AY��6�G��zG�� 6� ��!6�G��G��/6�y������������� ;6����6�p���������Y��6�G��G�� 6�2��!6�G��G��/6�y������������� ;6����p������R��Y��U|��p���������q������h��Tv�Q�R~�X$Y���6�O�� ?Z��6� 6�e��!6�H��H��/6�Pw������ ������� ;6����6�k��������� Z��6�<H��6H�� 6���!6�^H��XH��/6�w������ ������� ;6����=�Ck����������.Z��>�H��H��>�H��H�� >���>�H��H��oy������i��Tv����3���\��3�H��H��3�WI��OI��3���[��3�I��|I��t���������-t���������At���������L}���������T}������sL���s���������s���������t������PL�� t���������t������UL��Yt���������pt���������{t������{4��[��T��t���������t���������t������_L��+\��Tv�Q3�t������������������/������h��\\��QB�>���������Q������nL��\��T}�Q}�R1���������U ��������Q3���_��V3�I��I��b3�I��I��n3���_��o3�J��I��{3�J��zJ��3���R]��3�J��J��~���������)~���������=~������������������������sL���'>�~�������~������ ������� ]��6>�J��J���}���������}����������~������PL��~���������~������UL��U~���������x~���������~������{4��]��T��~���������~���������~������_L��0^��Tv�Q3�~���������~���������~���������~�������������������������dL��h���������x������h��^��Tv�QB�������������������������nL��^��Tv�Qv�R1���������_��U P����������������������iL���c}���������n}������xL��D_��Tv��u}���������}������}L��n_��Tv�Q0�}���������}������xL��_��Tv��}���������}������i��_��T}��}���������}������h��Tv�Q~�R0X0Y0��Ck���������s���������s������h��!`��Tv��s���������s������h��Tv����3�vk�������,��$%b�� 4�J��J��,��4�[K��GK��#4�|i������+4�z��`��04�K��K��}k���������k������h��T;��6���Ua��6� 6���!6�+L��%L��/6���;6����=4��� b��B4�YL��ML��6�l���������d ka��6�L��L�� 6�6��!6�L��L��/6�Z��;6����6�0l�������i��q a��6�L��L�� 6���!6�M��L��/6�`r������ ������� ;6����k������R��a��U|��k���������l������h��Tv�Q~���Ls���������ys������h����1�m���������$=h��1�HM��$M����1�N��M��1�N��N��1�6�m���������>b��6�O��O�� 6�#��!6�O��O��/6�G��;6����6�V��Gb��6� 6�s��!6�P��P��/6���;6����6�Gn���������NKc��6�CP��=P�� 6���!6�eP��_P��/6���;6����1���g��1�P��P��1�P��P��1�P��P��1�GQ��?Q��1�rQ��pQ�� 2�Q��|Q��2�Q��Q��#2�(��d��(2�~R��rR��o���������5o���������Qo���������}���������#}������sL���=�\p��������7��upd��>�R��R��>�R��R�� >�7��>�R��R��@}������i��Tv����=�yp��������M��vd��>�S�� S��>�'S��#S�� >�M��>�@S��:S��3}������i��T����Z2�c��d��[2�_S��]S���'>�t�������t������ �������w e��6>�kS��iS���n���������n������<L��Ee��T~��n���������n������h��oe��Tv�Q0�n���������n������KL��e��TQ H������R0�n����������o���������o������PL��o���������o������UL��o���������o���������o������AL��&f��T~�Q}�R1�p���������5p���������Ep������_L��]f��T|�Q2�Lp���������Vp���������rp���������t���������t���������t���������u��������� u������dL��|���������|������iL��|���������|������nL��T|�Q|�R��=�r��������r��pg��>�}S��uS��>�S��S�� >���>�S��S�� z������i��Tv����=�r����������g��>�S��S��>�T�� T�� >���>�+T��#T��y������i����m������R��g��U|��cn������R��g��U|��r���������r���������&x���������0x���������{�����������hg������O��Vh��UU�h���������h���������i������h��[i���������v������һ��|������5���h��U M�������|������5���U w�������� � � � � {� � � _� � o� � D� (� q� 0��������������Nn��0�_T��ST��0�T��T��0�T��T��1�~1�^U��DU��1���i��!1�U��U����������U H��������6�I���������i��6�U��U�� 6���!6�'V��!V��/6�!��;6����6�0�� 1j��6� 6�F��!6�RV��LV��/6�c��;6����=�M��������r�� j��>�{V��wV��>�V��V�� >�r��>�V��V��������i��T~����A1���l��B1�V��V��O1�}<�O����������&k��+<�V��V�� <�W�� W��<�#���������� <� <�+<�2W��.W��=�/���������� k��=�=�IW��EW��=�`W��\W��?������bn��kk��T~�Q��z������bn��T�Q~���,�����������f���������n������Xn��z������������������������PL��������������������̓������L�� l��T|�փ������������������������]n��Ql��T�Q~�R|�XDY7�#���������+������dL��{��������l��U ������R|��P���������b��������T�Q0R2�����������������Nn��l��Tv�Q2R0����������ɀ��������m��Tv�Q~��|������R��/m��U~����������������{4��Tm��T~�����������������h��~m��Tv�QB�*���������5������{4��m��Tv��Q���������a������Sn��m��Tv�Q3�u���������������O��m��Tv�Q0R2�Ȃ��������n��U|��ނ��������3n��U �������F���������#������һ��� � J � � Y� � /�������L������Iq��/�wW��sW��/�W��W��/�W��W��/�X�� X��/�ZX��VX��/�X��X��B>����������� n��Q>�X��X���/����q��/�X��X��/���p��/�X��X��?=�~����������0 Vo��J=�X��X���N���������[������L��{o��TH����������������Iq��o��U}�T�����������������h��o��Tv�����������������ZL��o��Tv�Q}�����������������{4��p��Tv��e���������u������Nq��Ep��T}�Q1����������������nL��up��T~�Q~�R1����������Ň������Nq��T�Q1�����������#���������2���������D��������p��Tv�Q0R2�%���������E����������������������������Ӆ���������܇������Sq��U~�T �������� � D!� � %�������C������'s��"%�Y��Y��/%�%Y��Y��;%�_Y��MY��G%�Y��Y��S%�Y��Y��`%�%Z��#Z��B>����������� q��Q>�DZ��BZ���m%�+��r��n%�VZ��NZ��{%�O��r��|%�?=�l�������z��G Cr��J=�}Z��{Z������������������L��or��Uv�T���������������������������� ������nL��Tv�Qv�R1��^���������Ј����������������������������1���������������һ��#������Sq��U}�T R�������� $�0������K������t��$�Z��Z��$�Z��Z��$�Z��Z��$�F[��<[��$�|[��x[��$�[��[��B>�k���������� s��Q>�[��[���$���t��$�[��[��%���t��%�?=����������Q t��J=�\��\������������������i��Ct��Uv�TQ0�������������������O���������b������nL��Tv�Qv�R1�����������"����������a���������k������������������l������һ��{������Sq��U}�T _�������� /�������o������w��/�\��\��#/�3\��+\��//�e\��[\��;/�\��\��G/�\��\��T/�]��]��B>����������� u��Q>�']��%]���a/�(��Jw��b/�7]��1]��o/�W]��S]��|/�t]��l]��������������������4���������b���������q���������������w��'v��T�Q �������������������������Ӌ������h��`v��TQB�ދ���������������h��v��TQB�������������������(���������K���������|������������������������Nq��v��T ������Q1����������ʌ������nL��.w��Tv�Qv�R1���������U @��������������������������Ɗ���������������Sq��U�T �������� � .�������������y��.�]��]��.�]��]��.�]��]��.�Q^��E^��.�^��^��.�^��^��B>���������E�� *x��Q>�^��^���.�T��Ny��.�^��^��Z���������s���������������������������͍���������ߍ������w��x��T}�Q �������������������������?���������W���������k������Nq��y��T ������Q1�w���������������nL��2y��Tv�Qv�R2���������U @�������� ������������������-���������������Sq��U~�T �������� .�������������/|��.�_���_��*.�"_��_��6.�X_��J_��B.�_��_��N.�[.�`��`��h.�N`��D`��B>�ݎ��������j��' .z��Q>�`��`���t.���������������Uz��y.�`��`���.���{��.�`��`��.�`��`��9���������W���������t���������������������������ʏ������w��z��TQ �������ُ���������������������������*���������S���������{������������������������FL��\{��T}�Q2����������ߐ���������������nL��{��Tv�Qv�R1� ��������� ������Nq��{��T ������Q1�8��������U @��������ӎ���������ݎ������������������G������Sq��Uv�T  �������� -�P������������|~��-�`��`��-�a��a��-�Qa��Ca��-�a��a��-�a��a��-�b��b��-�$b�� b��B>�}����������T |��Q>�9b��7b���-���������������|��-�Gb��Cb���.���3~��.�`b��\b��ȑ���������������������������,���������;���������M������w��u}��T�Q �������\���������s������������������������������������ǒ���������ڒ������nL��}��T|�Q|�R1����������������Nq��~��T ������Q1���������U @��������s���������}������������������%������Sq��U�T �������� *�0������u������n��*�wb��sb��*�b��b��+�b��b��+�c��b��+�9c��3c��'+�xc��pc��B>�]���������� ��Q>�c��c���4+�����5+�c��c��B+�c��c��=�|���������� ��>�c��c��>�d��d�� >���>�!d��d��������i��T~�������������Ɠ��������������������������� ���������2������w����T�Q �������A���������b���������|������������������ǔ���������ڔ���������������������������������h����T}�QB�/���������C������Nq����T ������Q1�W���������j������nL����Tv�Qv�R1���������U @��������N+�q�������������%��O+�Dd��>d���S���������]���������v���������������Sq��U�T �������� +*�������\������z��7*�dd��`d��D*�d��zd��P*�d��d��\*�d��d��h*�&e�� e��u*�ee��]e��B>�ݕ�������� ��" ���Q>�e��e���*�"����*�e��e��*�e��e��*�e��e��*�?��O��*�e��e���=�j��������N����>�f�� f��>�2f��,f�� >�d��>�Tf��Nf��������i��T}����-���������J���������c������������������������������������ϖ������w��#��T�Q �������ޖ������������������.���������>������h��g��T}�QB�L���������n������h����T~�QR0X0Y0�y���������������������������ŗ���������I���������T������h����T}��j���������������������������������Nq��G��T ������Q1�ǘ���������ژ������nL��w��Tv�Qv�R1���������U @��������*�ח�������������ڄ��*�sf��qf��ޗ���������������h��T<��*��������+�������1��*� ���������+������h��T}�QR0X0Y0��ӕ���������ݕ������������������ ������Sq��U�T 0�������� )�������������ˇ��)�f��}f��)�f��f��)�f��f��)�<g��.g��)�g��g��*�g��g��B>�7��������s��[ ��Q>�g��g���*�����*�g��g��*� h��g�����������������������������Ǚ���������������������������������w����T}�Q �������%���������<���������J������L��ކ��U~�Tv��T���������^���������y������������������������Nq��6��T ������Q1����������ʚ������nL��f��Tv�Qv�R1�ݚ��������U @��������-���������7���������L���������������Sq��U~�T L�������� C)�������������!��O)�Fh��Bh��\)�dh��\h��h)�h��h��t)�i��h��)�Mi��Ei��)�i��i��B>�����������{ ]��Q>�i��i���)���؉��)�i��i��)�i��i��_���������q���������������������������՛������������������������w����T}�Q �������������������������,������i��4��U~�Tv�Q0�6���������@���������[���������w���������������Nq����T ������Q1����������������nL����Tv�Qv�R1���������U @�������� ������������������,���������̜������Sq��U~�T Y�������� (�М��������������(� j��j��(�)j��!j��(�cj��Qj��(�j��j��(� k��k��(�Lk��Jk��B>� ���������� ��Q>�kk��ik��� )����� )�wk��uk��)�k��k��&)���ތ��')�k��k��3)�@<�g��������"�� ��+<�k��k�� <� <���������������@������� <� <�+<�k��k��'=���������������6������� ދ��=�=�l��k��=�l�� l��"������bn��Ë��T|�Q~��;������bn��T~�Q|�����������������������!������i����U~�Tv�Q@�+���������5������������������������i��]��T}�����������������{4����T|��ߞ�������������������T~�Q0R2�w���������������nL��Tv�Qv�R2��V���������e���������~������������������ɝ���������؝���������������w��Q��Tv�Q �������������������������O���������c������Nq����T ������Q1���������U @����������������� ��������� ���������������һ��������Sq��U~�T Y�������� c'���������������o'�&l��"l��|'�Dl��<l��'�nl��ll��'�l��xl��'�l��l��'�l��l��B>���������8��; ��Q>�m��m���'�G����'�$m��m��'�Fm��@m��'>��������������������� $��6>�dm��bm���$���������E���������b���������������������������������w��a��T|�Q �������������������������������{4����T|�����������=���������W���������k������Nq��ޏ��T ������Q1�~����������U @���������������U `��������'��������������Z��'�pm��nm�����������"����������ݟ���������������������������������Sq��U}�T �������� a&�������������"��m&�m��{m��z&�m��m��&�m��m��&�m��m��&�n��n��&�Nn��Ln��B>�ǡ��������]�� 5��Q>�mn��kn���&�l����&�{n��wn��&��������������v��&�n��n���=�����������2 ב��>�n��n��>�n��n�� >���>�n��n��;������i��T}�������������%���������B���������p������������������������w��=��T}�Q ����������������������������������+������Nq����T ������Q1�K��������U @��������&���ْ��&�n��n������������������������������ǡ���������ݡ���������Z������Sq��U}�T �������� %�`������}��������%�o��o��%�%o��o��%�Oo��Mo��%�eo��Yo��%�o��o��%�o��o��B>����������� ��Q>�o��o���%���w��%�p��o��%�r���������������%�p��p���=�{����������; V��>�1p��-p��>�Hp��Dp�� >���>�ap��[p��{������i��T}����&��������������}�� &�p��~p���=�����������< ޔ��>�p��p��>�p��p�� >���>�p��p��������i��T}����&����������������&�p��p���=�ä�������� ��= f��>�q��p��>�q��q�� >���>�1q��+q��������i��T}����+&�ܤ���������������0&�Rq��Nq���=���������'��> ��>�iq��eq��>�q��|q�� >�6��>�q��q��������i��T}����=&�����������������>&�q��q���=� ��������E��? v��>�q��q��>�q��q�� >�T��>�r��q��������i��Tv����ģ���������������������������0���������?���������Q������w��ܖ��T}�Q �������`���������{������������������ä������������������ ���������O���������c������Nq��[��T ������Q1�Υ��������U @��������L&�"���������������M&� r��r��)���������4����������}���������������������������ݥ������Sq��U}�T �������� -�������U������k��$-�/r��+r��1-�Mr��Er��=-�r��ur��I-�r��r��U-�r��r��b-�0s��(s��B>� ��������c��r ��Q>�Ws��Us���o-�y��"��p-�es��as��}-�zs��xs��]���������v���������������������������Ц���������������w�� ��T�Q �������������������������2���������E���������n���������������������������������k����T}�Q2����������ק���������������nL��ՙ��Tv�Qv�R1����������������Nq����T ������Q1�&��������U @����������������� ���������&���������5������Sq��U�T g�������� � ,�@������U������Ҝ�� ,�s��s��,�s��s��&,�s��s��2,�t��t��>,�Jt��Dt��K,�t��t��B>�m���������� ��Q>�t��t���X,�����Y,�t��t��f,����������֨������������������!���������0���������B������w����T�Q �������Q���������t���������������������������Ʃ���������������������������������k����T}�Q2����������7���������J������nL��<��Tv�Qv�R1�_���������s������Nq��m��T ������Q1���������U @��������c���������m������������������������Sq��U�T �������� u,�������7��������,�t��t��,�t��t��,�:u��4u��,�bu��Vu��,�u��u��,�u��u��B>�ͪ���������� d��Q>�u��u���,�����,�u��u��,�v��v��,�,v��(v��,�����,�Xv��Tv����������������������������Tv�Q|���������������������#������Xn��8���������Q���������n���������������������������������w��t��T~�Q �������̫������������������]���������h������������������������Nq��ٞ��T ������Q1�Ȭ��������U @��������-�+�������������6�� -�}v��{v��2���������=����������ê���������ͪ������������������׬������Sq��U}�T �������� � ^+�������7������1��j+�v��v��w+�v��v��+�v��v��+�w�� w��+�Xw��Tw��+�w��w��B>� ���������� ��Q>�w��w���+� ����+�w��w��+�w��w��+�w��w��+�����+�x�� x��I������������������������1��Tv�Q|���F���������[���������c������Xn��x���������������������������ܭ������������������������w��&��T~�Q ������� ���������'���������������������������Ǯ���������ۮ������Nq����T ������Q1���������U @��������+�n���������������+�$x��"x��u���������������������������� ���������#���������������Sq��U}�T �������� � &� ��������������&�3x��/x��'�Qx��Ix��'�{x��yx��'�x��x��&'�x��x��3'�y���y��B>�G��������8��] Ȣ��Q>�!y��y���@'�G����E'�/y��+y��������������������¯���������������������������������w��H��T}�Q ������� ���������[���������i����������T~�Q}���������������������T~�Q0R2�װ���������������Nq����T ������Q1���������U @��������S'�|�������������=��T'�Dy��By���������������������=���������G���������]��������� ������Sq��U}�T �������� � '�������������&��(�Sy��Oy��(�sy��iy��(�y��y��)(�y��y��5(�Qz��Kz��B(�z��z��B>�O��������V�� ��Q>�z��z���O(�l��̪��P(�z��z��](�z��z��j(��� ��o(�|(�5{��1{��(�L{��H{��>�G���������˥��?�a{��_{�� ?�o{��m{��>�{��y{��V��������T}���X���������m����������T}�QR2�������k���b=����������� ��r=�{��{����=�=�{��{�� b=����������� r=�{��{����=�{��{��=�����(�����(�|���|��(�g0����������B��s0�4|��&|����0�|��t|��0�?������0�0�������0�0�0�0�0�D������ȸ��������U  ������������������3������i��n��Ts�Q�@���������K����������������������������TQ��`���������s������nL����T�Q�R1���������U H��������b=���������������3������� ��r=�}��}��=�=�}��}��b=���������������&������� r=�}��}��=�}��}��=���=���������Z��"��=�}��}��=� ~��~���������������������ٱ���������������������������*������w��&��T}�Q �������=���������b������������������²������O��o��T~�Q0R2����������������O����T}�Q0R2�������������������������������������������Nn����T~�Q2R0����������Ϸ������Nn��#��T}�Q2R0�ظ���������������R��T��T ������Q0����������#������bn����T�Q~��8���������L������Nq����T ������Q1���������U @��������D���������O���������i���������������Sq����U}�T �������������һ��� "�������D������U��""�R~��N~��/"�~��h~��;"�����G"�T"�`"�m"� ������z"�p����"������"�����"������"���ث��"�)��'���"�����"�5��3���"�����"�A��?���"���)��"�M��K���"���D��"�Y��W���"���_��"�e��c��� #���z��#�q��o���#�����!#�}��{���.#�����3#������@#���ˬ��E#������R#�$����W#������d#�3����i#������v#�B����{#������#�Q��7��#�ŀ��À���#�`��R��#�р��π���#�o��m��#�݀��ۀ���#�~����#������#�����#������#�����#������#���٭��#� �� ���$����� $������$�����$�%��#���*$���*��/$�1��/���<$���E��A$�=��;���N$���`��S$�I��G���`$���{��e$�U��S���r$�����w$�a��_���$�#��L��$���k��=�t�������@����'=�����@��3=�����y������������������T M������Q1���=��������]����'=�����]��3=�%��#��������������������T w������Q1���$�z��ï��$�1��/�����������������n��T|�Q0��9���������I������Nq����T}�Q1�W���������k������Nq����T ������Q1����������������i��T ������Q0��8���������X������U����U Q~�R ������X �������a���������h���������o���������������Z����T ������Q *����������������������Z�� ��T ������Q ����������������������Z��Y��T ������Q �������ƺ���������ܺ������Z����T ������Q ����������������������Z��ı��T ������Q|�����������������Z����T &������Q|��)���������;������Z��(��T =������Q|��L���������^������Z��Z��T R������Q|��o���������������Z����T j������Q|�����������������Z����T z������Q|�����������ǻ������Z����T ������Q|��ػ���������������Z��"��T ������Q|����������� ������Z��T��T ������Q|�����������0������Z����T ������Q|��A���������S������Z����T ������Q|��d���������v������Z����T ������Q|�����������������Z����T ������Q|�����������������Z��N��T ������Q|��ͼ���������������Z����T +������Q ���������������� ������Z����T :������Q|�����������,������Z����T V������Q|��=���������O������Z����T q������Q|��`���������r������Z��O��T ������Q|�����������������Z����T ������Q|�����������������Z����T ������Q|��ɽ���������۽������Z����T ������Q|�����������������Z����T ������Q|�����������!������Z��I��T ������Q|��2���������D������Z��{��T ������Q|��U���������g������Z����T 0������Q|��x���������������Z��߶��T E������Q|�����������������Z����T _������Q|�����������׾������Z��J��T z������Q P����������������������Z����T ������Q ����������������������Z����T ������Q �������"���������8������Z����T ������Q @�������?���������U������Z��.��T ������Q �������\���������r������Z��g��T ������Q 0�������y���������������Z����T ������Q ����������������������Z��ٸ��T �������Q ����������������ɿ������Z����T ������Q �������п���������������Z��K��T "������Q М����������������������Z����T :������Q ������� ���������������Z����T|�Q �������&���������E������_����T}�R c������X0�L���������b������Z��&��T j������Q  �������i���������������Z��_��T ~������Q ����������������������Z����T ������Q `����������������������d����T ������Q ������R~�X ������Y0����������������d��:��T ������Q 0������R~�X ������Y0�%���������7������s��� !� !� !� e!� #!� !� �N�  ������o� (������d� 0������S� @������z� @�����Y� H�����(���������������������� ������R���.��D=��k��� ���v��!!�����!)��*���!6��!6��!!��! ,��Eint�R���%��*6���!4��:��=���"��6�����6�����=���!>��6���)(��=���!$��j���1��R���%��j���.��j���F1�����j�����j���@.��j���6<��!j���*��1 ��!!��*����@���"��O}���B.��l��.�� �����=���% ��:������=�����=����$3��r��V�� ��2�� ���� �����!6��*����=���� ��16���!/����  ��6��� �� ���% =��!��R���� �����Y����[�� =��D�� ���� ���W+�� "��h_�� #��p*�� $��x,�� '�������=��� ���-A ��1���Y�������=��� �1����1��# �� *!��U��% �� ��%�� ^����� Z=���^4��(��j���8��=������m��:�� �����! R���@a��"��H�8��}��=�����+��Fm��7���� g��R��� =�� ����>����%8��Z7��:����'��;}����%?��2��A R����%��B R�����C���%G?��Z7��I����'��J}�����K���% O��Z7��Q����'��R}�����S R���=��T���+��U����%a����c ����{*��d ����^�� ��e�� 4 ��g^����% Y��6��[ ���� ��]K���-��h ���%l%��o?��nj����f(��o R����%tV����v ����5��w R���9 ��x6��� �p3�� ��5�� !��<�� 5��D��._rt�L�� 5��V?�� U1��i�� ?��p�� :��y%���R�����=����%$ ��,��& R������( R�����* R�����0 R��� 2��{ V���o��|��,��(7��R���� ��;��.I��N��R���b��b��b���l��1b��G!�� =��E��;�� q����o1��$ �����,���u;��-���'��/ }���(��0 ��� ��2 R���$!��4 q���( ��9 ���0T3��=���8&��?���@_��J��H�/��K��X1#��L��h.��YE��x���U��=����!d ��!+��)tm�8���� R����I �� R��� �� R����� R���  �� R���2��R���N4��R�����R���;+��R��� k:�� j���(����0�2��@��8�� ���:�� 7��) ��R���j8�� ����� b��f��d �����e 7����fR�����gR���a+��h 7���'�� ��:�� ���<�� 7��k��R���/�� �����D ��9��F ���0��G 7��}=��HR����*�� ��=����+DIR�* ��-4��&IV�oj���/ ��&UV�p=���? ��&NV�_ ��O ��!��!!����f f��m ��&OP�h  ��)op�(\ ��O��-���#��-����D��!��B��)��$-�� � ��$-�� 3��$-�� <��$-�� s)��$-�� 9:��$-�� ��$-��_1��$-��=4��-��"��-��#�&COP�i h ��8cop�X ��O��-���#��-����D��!��B��")��$-�� �" ��$-�� "3��$-�� "<��$-�� "s)��$-�� "9:��$-�� "��$-��"_1��$-��=4��-��"��-��#1��\-��$ ��B��(;�� ��0V�� F-��8 �� F-��<@&��K��@y-�� L��Hv1�� F-��P���o  ��Z��` ��O��-���#��-����D��!��B��)��$-�� � ��$-�� 3��$-�� <��$-�� s)��$-�� 9:��$-�� ��$-��_1��$-��=4��-��"��-��#�� -��(1�� -��0)���B��8I3��F-��@�� D��H ��D��PI,�� -��X�!��s � ��<��P!��O��-���#��-����D��!��B��")��$-�� �" ��$-�� "3��$-�� "<��$-�� "s)��$-�� "9:��$-�� "��$-��"_1��$-��=4��-��"��-��#�� -��(1�� -��0�� -��8�� -��@S!�� -��H�_��~ .��=��r ��6.��#-���/Iop�$-��.��%-��Y ��'-��;��(-�� ��*W��(3��,0-��0��-0-��4<��/W��8��00-��@��10-��D_��3-��H7��4N��P��5N��X)��6N��`]!��8F-��h��:W��pO:��<W��xR��=W����A-��4��C? ��0��E-��$��HD��B3��K;��9��L;��$.��N-��4;��O-��C:��^$-����h-��m>��i-��3��j-��(��v-��,��} -��*;��-��"%��-��..�� O����-��/��W��73��-��L��/ ���8��-��p ��-����;�� ��W��  ��W��(&��=C��0 �� ��8�� ��P $�� ��hC$�� ��@*��y,��V ��y,��9ISv�ϓ-����W���/��-��9Ina�m ����t��&��-��"��-��9Irs�-��q��-����-����-������<��-��=;��-��7��-����-����9M�����9M��/��K��m��-��6�� 20��"�� -���?7��-����-��(��-��:��-�� 8�� ��(d ��m ��0*,��$-��839��?-��: ��A-��;��B-��<&1��C-��=?��D-��> ��H ��?��K-��@��NW��H ��eUW��x��}UW��a��W��3��7��`=�� �� ��F-��H7��F-����R���r��R���|��7��G ��-����-��V4��-��%�� ��U5���� ��-��?��-����-��*��X��6�������-��A��-�� J��-�� 8��-��  ��-�� ��-�� ��-����-�� �� ����-��1 ��Z�� ��0-��(,��0-��,��0-��08��R���4+��X��8��-��@��-��Hr��-��P3��-��XS��-��`;>��-��h7��-��p,��-��xP ��-��U<��-��9��-�� ��-��h4��-��&��-�� �� X��% ��-��8"��-��;��-�� !��-�� ��-����-����-��v��-���:8��-��; �� ��@���X6��Y��-��  ��-��((��-��0;��-��8'�� O��@:�� R���H �� R���L6,��  ��P��-��X/"��-��`$��-��h7/��R���p��0-��t?��-��x5��-��yL+��$-��z"��R���|��0-��":��#0-��5��$X��>0��3-��3��6"X����8\ ��)��:X6��D��;8C���.��<B��?��=B��*��EX6��0��FR��� ��IF-��$4$��K-��(��L-��) ��M-��*T��N-��+?��QB��,��RB��0��S6��4*2��T6��8HIan�UF-��<C>��XF-��@M&��`F-��D$��cF-��H.��dF-��L2��e7��P! ��i ��XD ��ka.��`��n*.��h��o6.��p��q'X��x.��sW-����uF-��#��/V��� 6��-�� 3��0-�� E"��B�� j ��B�� %-��B��( <��B��0 ��B��8 |9��7X��@ 8�� �� p"��f��  ��f�� ��F-��  5��-�� ��-�� 7��-�� t.��-�� 5�� �� 4��f�� -?�� �� ��f�� F4��-�� 9��-��  ��V�� *��-�� ��D��� ��R��� g*��-�� D)��-�� 2�� �� 6��-��� ���� i5��-�� =��-�� 2��-�� ��-�� 9��GX��( %��-��0 6�����8  ��-��@ 4��-��H / �� LX��P �� B��X  �� B��` 4��QX��h -��-��p m��-��x N ��.�� .��VX�� ��VX�� ��-�� +��-�� <�� j��� ��#PV�� *��$PV�� $��-PV�� )��1rV�� &��4V�� ^ ��7V��  ��:-�� '��@-��  ��C-�� ��E-�� 4��G[X��� ^?��K`X�� ��M(W�� 9��PW��X =��YeX��` 9��ZR���h ��bU��p (!��ojX�� P?��zB��  ��|m �� #��B�� 8��zX�� v,��-�� #��-�� 0��-�� G��-�� "5��-�� ��-�� !��-�� u#��-��� ��-��  ��-�� '��-��  ��-�� !��-��( a<��-��0 "��-��8 )��-��@ ��-��H ;��-��P ,��-��X H<��X��` )=��X�� >��-��`��-��h\/��-��p_#��-��xL��-��r4��-��m+��-��,��-��:��-����-��9��-�� ��I��5��I��7��I���&SV� & �� ��)sv�f ��,������(��F-��A��F-�� ��/���&AV� q ��)av� ��,��1���(��F-��A��F-�� ��71���&HV�  �� ��)hv�!��,��(2���(��F-��A��F-�� ��1���&CV� !��!��)cv�Q!��,��21���(��F-��A��F-�� ��0���2�� ^!��J��!��,��4���(��F-��A��F-�� �� 2���&GP� !��)gp�P \"��:�� -���$�� B��2�� X6��'�� F-��z�� F-��c5�� -�� ;2�� -��(3�� X6��0 �� -��8/3��F-���T'��F-��'�� 35��H�&GV� g"��)gv�"��,��0���(��F-��A��F-�� ��A0���8io�"��,��2���(��F-��A��F-�� ��-2���k�� "����hn#��*��rO����^:��h#���� -���#�� -��3�� $-��c�� 0-��J�� 0-�� *�� 0-�� {/�� O��?�� ;��F��N�� �� 0-��(,��N��0�})�� #��f��0B$��(�� ^5����� 0Q��a7�� $-��?�� *��%�� -���� N���� -�� w2�� ��(�&XPV� N$��8xpv� $��.��-���&��85��r��m ����c5����� $��$��(�$��.��-���&��85��r��m ����5��^;��4�� ��� %��. ��(X%��.��-���&��85��r��m ����5��$�� 4�� � �� e%����0%��.��-���&��85��r��m ����5��^;��4�� "��4��(��� %��S��08&��.��-���&��85��r��m ����5��^;��4�� "��4��(�y�� E&��=��( &��.�� -���&�� 85��1��  N��V;��  N��Q(��  -�� �@�� &��e�� !&��.��! -���&��!85��V,��!m ����!m ����� &�� ��0;S'��.��<-���&��<85��r��<m ����<6��^;��=4�� "��>4��(�n�� `'��?��h" $(��.��"-���&��"85��r��"m ����"C��V-��"-�� `0��"C��(I��"C��0��"�D��83'��" ��@ ��""D��Hv��"X6��P6��"F-��XU6��";6��\5��"0-��`��� 1(����e<)��.��f-���&��f85��r��fm ����fg6��^;��g4��  ��i<0��(?��v6��0+��x / ��8��y / ��@*��z / ��H9��{ ��PQ/��| -��XA��} ��`:��~ -��h3�� ��p �� -��x1�� *���� -���l)�� I)����@ )��W�� P���";�� P��N�� P��7�� P��6�� P�� =�� Q��(M6�� +Q��0 �� P��8�&ANY� )��Iany�*�� �� ��� ��-�� 8��-�� &��-�� F��-�� ��-�� }��-�� �� �� ��7�� 6�� 0-�� �� F-�� �� / �� �� ? �� 8�� j��� J.�� -�� s�� -�� `8�� -���n3��y*����zU���8��{b�� ��| ������ *��2��0`+��3(��U���*�� ? ���� ? ����U����U�� %��U��(��� m+����(+��/��-���4��? ��1��-��.��-��Q ��-�� �y �� +��G��#,��v ��# N�����#&BC�� ��#' F-��0��#( F-���&PAD� f ��b�� *,����(#+y,����#, N�����#-nC����#. N��o��#/B��j ��#0 F-�� � �� ,�� ��0#L -��w?��#M ���_3��#M-��#��#MxC��0��#MF-��-��#MF-��t(��#MF-�� :��#MR���$+��#M-��(z��#M-��)�+I8�$D���+U8�$���-��+U16�$*���+I32�$R���0-��>0-��+U32�$6���F-��>F-����$ F-��%�� ��(-������v-��2>�� �� ��-��-��-��\"��f �� ��-�� ��!s��-��(-��-������!��-��*��-��=�������%<.��-����%>.��-��4��%Q%.��-&�� ��&4'����&5B.��G.��(\.��R���\.������i-��i��&;*.��7`-��'.�� [8��' 0-�� '��' �� '��' -�� ,��' -���`-��'m.��26���(/��%����� ��!��#7����J����2���� A�� Y�� F�� n�� �������;��.��(/��+HE�D/��)he�! w/��*4��!$ 20�����!% 35����!)I���+HEK�/��)hek� !-/��-��!.F-�����!/0-��3��!5-���-0�� �� �� y��/ �� ��? �� ��O �� n��-�� :��-�� ��-0�� l��70�� ��<0���20��9/��!��.��0�� �� �� y��/ �� ��? �� ��O �� n��-�� :��-�� ��-0�� l��70�� ��<0���&��21�� �� �� y��/ �� ��? �� ��O �� n��-�� :��-�� ��-0�� l��70�� ��<0���S'��1�� �� �� y��/ �� ��? �� ��O �� n��-�� :��-�� ��-0�� l��70�� ��<0���8&��(2�� �� �� y��/ �� ��? �� ��O �� n��-�� :��-�� ��-0�� l��70�� ��<0���&��2�� �� �� y��/ �� ��? �� ��O �� n��-�� :��-�� ��-0�� l��70�� ��<0���$(�� 03�� ��  �� y�� / �� �� ? �� �� O �� n�� -�� :�� -�� �� -0�� l�� 70�� �� <0���9*��(n4��.��(o-���&��(o85��r��(om ����(o7��%��(p!8�� :��(q 7��(w'��(r -��0:��(x F-��8l(��(y F-��<$��(z N��@��({ N��H��(| m ��P8��(8��X0��( ���`8��( F-��h ��( F-��l��(8��p ��( 7��x+��( F-�� ��(F-�� *��(F-�� $ ��( ����(-��*��( N��v>��( N�� ��( N�� ��( N��% ��( X6���03��:*��4�� 6�� O �� 2�� -�� /�� \-�� =�� -���:;��35�� *�� / �� 60�� ? �� 5=�� 35�� 2 �� -���w/��:?��^5�� b�� ^5�� &�� m ���#��5�� u��m �� ��4���5�� u��m �� ��4���5�� u��m �� ��4���5�� u��m �� ��4���6�� u��m �� ��4���<;6�� u��<m �� ��<4���0��A F-��(X6��-��X6���!��H6��+��f6�� u��fm �� ��f4���s6�� ]'��t6�� 8��u ���� ��%���6��?�6��@ '��06����(7��4��(-�����( -��-0��( $-�����(6��S ��(((`7��O��() N���&��(* N��H��(+ -��C��(, -�� ��(- N�� ���(/7��44��(0 -���J ��(17���7��7��=����0��(<7�� ��(= N���/end�(> N��7��(E N���0��(F7��Q!��7��(o8�� u��(om �� ��(o4���%��h(8��:��(.9���e ��(j9��|��(9��6��(9��D��(9�� <?��(9��(P ��(:��0|<��(?:��8��(g:��@(��(:��H��(9��Pl7��(:��X:��(:��`�8��8��`7��7��9*��(03�� ��(9��k2��( 7���H��(9���N��-��(8��7��.9��-��-��F-���9��0-��j9��-��7�� �� �� ��N��-�����F-���39�� ��9��-��7��-���� �� ��R-��9��� 9��o9��-��9��-��7���9��(9��-��7���9��(9��-��7��<-��-���9��(:��-��7��<-��:���! ��:��9��0-��?:��-��7��:��<-���!:��-��g:��-��7��-��-��R-���D:��-��:��-��7��:��R-���l:�����:��-��7��:���`+��:��7��:��-��-��R���-��8��7��:��F-��F-���-��:��X( ;��rex�( ;���C3��(;����(-�� ��( ��*��( m �� v>��( m ��( ��( m ��0sv�(-��8'��(^5��@pos�( N��HO ��( -��P�8�� ��I0��(:�� ( ;��R0��(;�����(I<����(<��} ��( ���;�� ��(I<��!��( R���� ��( ��x ��( ��sr0�( >��u�(w�B�� �;��= ��x(<�� ��(B���<��(<��h��("<��p�N<��S.��(;��5��( 0-��(<��u:��(I<����( =��u:��(I<��� ��( F-��+��( F-�� cp�(<��� (T=��u:��(I<��� ��( F-��+��( F-�� cp�(�<��$��(T=���7��@( >��u:��(I<��� ��( F-��+��(  F-�� cp�( <��,��(  F-��.��(  -��~ ��( >�� me�(T=��( ��( >��0��( F-��8��( $-��</ ��( $-��>�$-��-��@(>��u:��(I<�����(I<��-��($I<�� ��(7��cp�(<�� <��(<��$��( F-��(B�(!T=��0 ��(" ��8�0(%?��u:��('I<���4��(( 0-��//��() 0-�� !��(*-�� ��(+ ��end�(, �� me�(-T=��(� (0I?��u:��(2I<�����(3I<����(4 -��=��(5 ���(8b?��val�(9 R�����8(>?��u:��(@I<�����(AI<��me�(BT=��B�(CT=��cp�(D<��  ��(E -��$ ��(F R���(!��(I R���,��(J ��0�((MS@��u:��(OI<���&��(PI<��cp�(Q<��<��(R<����(S ��4��(T 0-�� ��(U 0-��$�`(XA��u:��(ZI<���c1�([ R���c2�([R��� cp�(\<�� ��(] F-��+��(^ F-��.��(_ 0-��!��(` 0-��  ��(a -��$A�(bT=��(B�(bT=��0me�(cT=��8?9��(dA��@ ��(eA��N�-��*A��=��� �h(h�B����(i F-���cp�(j<�� ��(k F-��+��(l F-�� c1�(m R���c2�(mR���~2��(n ��_"��(o �� !��(p R���(min�(q R���,max�(qR���0A�(rT=��8B�(rT=��@?9��(sA��H ��(tA��V�h(B�� \.��(<�� R0��( ;��3yes�(<��  ��( <�� )��(=�� *��(Y=�� ^��(#>�� ,��(.>�� ,��(6?�� ��(:I?�� &��(Kb?�� :��(V?�� T��(fS@�� f��(u*A��� ��(x;��B��B��=���� ��(N<����)D=���"��J��#N��%#!8C��1��#"8C�����## =C����#$ =C���,��,��#dC�� L8��# dC�� ��#%iC���=C��C��sC��y,��#MC�� 1��#M-�� $��#MX6���"C�� u��"m �� ��"4���"C�� ,��"-�� >��")���"�D�� l#��"-�� ��"]6���""D�� r-��"-�� 6��"35���"DD�� C��"b6�� ��"���� D�� <��B��.sv�-��.iv�/ ��.uv�? ��.pv� �� m��N���&6��DD��-��D��-���D��D��D�� ��-�� 8�� B�� Y&��-���  E�� �� -�� 6)��  B���/%��0*1uE��T��*3 ���'��*4 ��4��*6 }���d:��*7 �����*8 ��3!��*9 �� 5��*: ��(� �� +*E��)��+, ���,%��+- ��p%��+. ���N5��+/ 7���A0���,HF��*��,MF���[��,VF�� ��,[*F���.��,b;F���m��,i*��v��,nLF����*��*F��0=����*��;F��0=����*��LF��0=����*��]F��0=���w�\<��H-+F����-- ��� ��-. ��\��-/j���7��-0j�����-1j��� ��-2j���(1��-4j���0��-6j���8*>��-8=���@�J. jI��x��. ��� ��. f��/��.jI��6��. ����. f�� ;��.uE��(U��. ��H7��. f��P ��.oI��X'��.@��` ��. ����. f��/��.tI��z3��.R���! ��. ��l��. f��0��.��G-��. ����. f��#*��.yI��.��.R���:��.��d;��. �����. f��s��.~I����. E��>��. ��H8��. f��P ��.I��X_��.��`g'��. ��,��. f��>��.I����.]F����. ����. f��<��.I��Q%��. c��-��.  c��0+��." ��h��.# f��p-��.' ��xB=��.( f��+��., R����E��uE��@������ E����]F��("��.-F��!&I�� 2��!'-�� ��!( f���;(��!EJ��O��!F .J���2��!G����!H $-��?��!I $-��-��!J F-���I��-��.J��-��-��F-���J��^*��H!MJ��_6��!O-��� ��!S-��<��!T-��;��!U F-�� ��!V F-�� ��!WJ�� /isa�!X-��(L$��!Y-��0��!ZX6��87 ��![ F-��@�J��7h-��!gJ�� ��!h 35�� >��!i J���35����8!ltK����!mJ���#��!n -����!o 20����!p 0-��')��!x 0-��Z*��!ytK�� !��!{F-��(��!|F-��, ��!F-��0�3J���� K��<��!K���q7��"}��!��# R���o<��$ -����%$-��35��&N���yK�� ��)yK��B��K��-.��m ��K��K��'��0ShL�� ��T -���>��U0-����W =C��cv�X X6��,��Z 0-�� };��[ -��(�#��0`L�� ��a -���>��b0-����d =C��cv�e X6��gv�g -�� |5��h -��(�X��89M�� �� -���>��0-��#�� -��(�� -��?�� -�� cv� X6��( 1��9M��0�K��aM��3svp� -��3gv� -���M��ary� -���ix� / ���M��z0�� 0-���ix� / ���M��cur� / ���end� / ���M��cur� -���end� -���9N��3ary�aM�� p ��M�� 2��M�� x7��M���<��0N��A�� N���J'��>M��_,��-��>1��M���� =C��(� ��0��N��p ��-���,-�� -���8 O�� �� L�� 4�� hL�� x!�� L�� 4�� 9N�� )�� N���\ ��;��X.O��Q7��/ -���g��0 -����1 $-�� ��2 0-��a��4 N��.��5 N��V��6 ����7 -�� /��8 -��(&��9 ��0&��: ��8 ��; ��@��< ���H<��=7��P�hoP�� "��p#�� ;��qO���/��8 P��m �� -�����P����P��5��P���� 0-�� !�� 0-��$�� 0-��(�� 0-��,�� 0-��0�"��P��d)��!P��R���P��-��-��^5���P��F-��P��-��-��^5���P��R��� Q��-��-��^5��-����0-���P��R���+Q��-��^5��:���Q��<)��%/ sQ��/val�/ .���"!��/ K���4��/ 0-�� *��/ X6��� ��/5Q��k��(/Q����/Q���G��/ -��%��/ ��;��/ ����/ -�� �Q����/ Q��A<��/"U��J��/&U���1��/'.��(��/(R�����/+R���p ��/-U����/.U�� /ps�//U��( ��/0R���0��/4 0-��4D/��/5 0-��84 ��/6 0-��<A ��/7 ��@E!��/8 ��H.��/9 -��P&��/: -��QX"��/< -��R?,��/= -��S&��/>-��TU��/? -��U��/@ -��Xh1��/A -��`M��/B -��h��/C $-��p4&��/D $-��r��/E 0-��t���/F -��x ��/G 0-����/H 0-��!��/I ? ��=��/J ? ��>��/K-��+��/L -����/M $-��'��/N 0-��/��/O -��-��/P -����/QU��J��/R -��%��/S ��L;��/V ��I;��/W ����/X ��>��/Y ����/Z ����/[ ��u��/` \-��'0��/a $-��l&��/b -��Y��/c -��+��/d -���"��/e <0��`>��/f -����/h U����/i U��@7��/j -��Tg&��/k -��U/��/l -��V��/m -��W��/n O��X��/o  ��`=��/p \-��`O*��/q \-��dD5��/t? ��h>��/u? ��p��/v*��x��/w-��y9#��/y-��z$��/{$-����/|$-����/}$-��1��/~$-���Q��sQ��Q��.��U��=����0-��U��=����<��/Q��*��U��-��U����*V����-���<��$*V����$*V���U����&<V��AV��R���PV��-�����']V��bV��(rV��-��-�����(<V��'8��*V��V��-��V��-��-���Y��+V��V��(V��-���1��V��?�V��B~��xV��B0��}z ��K)$��6���y(W�� ���w��7��R$��F1��e.��l$���;��HEW��pad�EW���� ��UW��=����#��&bW��gW��(wW��-��-���&#��:]6����>bW��6��BV����IW��fn�J -���ptr�K ����3��LW��)��0-��P��B��B��U�� ��X��=������R���/ ��X��=����W��F-�����7X��=���� ��GX��=��� �*��-��I��.��B$�������-��zX��=���"�*��X��=��� �-��X��=����@��1--��26���2>yY�����%��'��%��(��"�� ��A��l0��"�� ++�� �� =�� !�� 7-��/7��X ��5��C ��)��/��2��+��1��/*������ <��P=��*��/��_��8�� �26���3N[�����p8������ ��8��7��! ��U��/:�� $�� D�� "�� �� ����) ��?��-���� ����d��?��-���� ����c��{����+��%�� U��!/��" ��#D+��$��%"��&9��'#+��( ��)(9��*Z��+(��,q��-��.D(��/��0C(��13��2)��33��4(��5U��6i,��7T��8h,��9K��:(��;3��<3��=,��> ��?h ��@>��A#��B'��CG9��D��E��F7��G ��HA2��I>��J��K�*[(��_ -��*6��_-��*��` -��*"��`-��*,��a -��26���4c[��)���9��/����x������.���%H4p [\��4��4qF-���9��4rF-����4s m ����4u-��7��4v-��&��4y-�� >��4z m ��(w%��4{R���0&��4|���4��4~-��8$��4~-��@�.��4[��[\��p4 \��cur�4 ���end�4 ��sv�4-����4[\��-��4F-��`��4? ��h� ��4l\��h4 6]��cur�4 ���end�4 ��err�4����4[\��5��4F-��`w��4F-��d�.&��4\��D���S]��=����O#��4%C]�� ��5-��]��-����0-���;��6 R���]�������'��5]��-��-��J ��� 2��5 ? ��]��-��-��<-���'n6��5K]��-��-��: ���'=��5 ^��-��-�����1��59 -��5^��-��-�����%��5s ���Q^�����f���"��5" 0-��w^��-��-��-��R-�����5 -��^��-����z ��R-���7��5-��^��-��-��20���'��D ^�����f��f��<���<'��5 -��_��-��-��-���*��57 -��_��-��-���$<��5 / ��>_��-��-��<-���>��6f��U_����� ��z ��v_��_ ��R��� ���,��5R���_��-��-��� +��5P -��_��-��z �����5jm ��_��-��_��_���-��'#��5u _��-��-��R-���Lpow�<_ �� `��_ ��_ �����5o -��(`��-��Z �����5 -��D`��-��J ���%��5R���j`��-����m ��j`���? ��y��5k -��`��-��: ���;G��5-��`��-��-��N��0-���"��5-��`��-��-����0-�����5Z-��`��-��-��0-���q=��5N��a��-��-���'1��5N&a��-�����5d0-��Ga��-��-��A-���6%��5W��^a��-���'t��50 qa��-���>��5-��a��-��20���9��520��a��-��-��0-���)��50-��a��-��-���(��50-��a��-��a��� ����5o��� b��-��-����0-��Y���-��R-���=��5m���_b��-��-��-����m ��R���R���-��F-���j/��5< -��{b��-��-���'=��5b��-��-��-�����5^ -��b��-��4/���_��5? ��b��-��_��m ��K��F-���~��5 -���c��-����z ���;��6@ R��� c��b��b��f���7��5  ��Fc��-��-��K��<-���': ��5 Yc��-���b��5  ��c��-��-��_��m ��m ��? ���'��5 c��-������'��5 c��-���)��5 -��c��-�����5  ��c��-��-��m ���K2��5m ��d��-��-��<-��m �����5-��8d��-��-��-��R-���C��Jd����4�'`%��5gd��-��-��(/���=��5 -��d��-��-���7��5 -��d��-��-��0-���$��5} -��d��-��-��-��N���Cz8��d��d����� !��d��'j ��5&e��-��<-���~ ��5\X6��#e��-����0-�����5s -��De��-����z ���R��5T-��ee��-����0-���4��5 X6��e��-����wW������F-���M��5e��-����X6����m �����5 X6��e��-����wW�����5(0-��e��R-�������4�v��7���f�����N!��8 h��? ��8 -�� cv�8 X6��ax�8 0-��,��8 -��sp�8 -����8 0-�� ;��8 �� f��p_�8 ���� f��p_�8 ���� f��p_�8 ���� f��p_�8 ���� f��p_�8 ���� f��p_�8 ���� f��p_�8 ���� g��p_�8 ���� g��p_�8 ���� .g��p_�8 ���� @g��p_�8" ���� Rg��p_�8$ ���� dg��p_�8& ���� vg��p_�8( ���� g��p_�8* ���� g��p_�8, ���� g��p_�8. ���� g��p_�80 ���� g��p_�82 ���� g��p_�84 ���� g��p_�86 ���� h��p_�88 ���� h��p_�8: ���� *h��p_�8< ���� <h��p_�8> ���� Nh��p_�8@ ���� `h��p_�8B ���� rh��p_�8D ���� h��p_�8F ���� i�4 R��� p_�4 ��������8 i��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� 9��8 -�� ��4P [\�����}��8 i��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-��  ��8 -�� ��4F [\�������8 \j��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� Lj��5��8 \j�� j��p_�4; ���� j��p_�4< ���� +j��p_�4= ���� =j��p_�4> ���� p_�4? ����� e��8 : ����[\��-��8} j��? ��8} -�� cv�8} X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� j��5��8 \j�� p_�42 ����� e��8 : ������8[ ck��? ��8[ -�� cv�8[ X6��sp�8] -��ax�8] 0-��,��8] -����8] 0-�� Sk��5��8a \j��� e��8x : ���� &��89 k��? ��89 -�� cv�89 X6��sp�8; -��ax�8; 0-��,��8; -����8; 0-�� k��"��8? -��5��8@ \j�� p_�4 $����� e��8V : ������8 l��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� 5��8 \j��9��8 -�� l��len�4m ��str�4��cur�4m ��� sv�4-��<��4m �������8 Cm��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� 5��8 \j��9��8 -�� sv�4-��<��4m �������8y m��? ��8y -�� cv�8y X6��sp�8{ -��ax�8{ 0-��,��8{ -����8{ 0-�� 5��8 \j��9��8 -������8Y +n��? ��8Y -�� cv�8Y X6��sp�8[ -��ax�8[ 0-��,��8[ -����8[ 0-�� 5��8a \j�� ��8b -����R9��8 n��? ��8 -�� cv�8 X6��sp�8" -��ax�8" 0-��,��8" -����8" 0-�� 5��8( \j��key�8) -��cb�8+ -�� n��p_�4 ���� n��p_�4 ���� p_�4������ ��8 ^o��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� 5��8 \j��cb�8 -�� p_�4 ������46��8 p��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� o��"��8 R���!��8 -��5��8 \j�� ��8 / ���� e��8 : ������8 up��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� 5��8 \j����8 F-����;��8 q��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� q��"��8 F-��!��8 -��5��8 \j�� ��8 ? ���� e��8 : ����9��8p q��? ��8p -�� cv�8p X6��sp�8r -��ax�8r 0-��,��8r -����8r 0-�� 5��8x \j��9��8y F-����#��8R r��? ��8R -�� cv�8R X6��sp�8T -��ax�8T 0-��,��8T -����8T 0-��ix�8U 0-�� r��p_�8U ���� 5��8[ \j����/��8% r��? ��8% -�� cv�8% X6��sp�8' -��ax�8' 0-��,��8' -����8' 0-��ix�8( 0-�� r��p_�8( ���� 5��8. \j����8/ R�����(��8 s��? ��8 -�� cv�8 X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� 5��8 \j����'��8s��? ��8-�� cv�8X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� 5��8 \j����8-��$��8-����3��8�t��? ��8-�� cv�8X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� 0��8 �� pv�4.-�����(��8gt��? ��8-�� cv�8X6��sp�8 -��ax�8 0-��,��8 -����8 0-�� e��8: ������46t��5��46\j��p�48��# ��B #5�� #'��#v&��m #m!�� #!�� #k�� #"��� ��4-��^u����4-����4 \j����4.K��dec�4 6]��sv�4-�� /u��<��4 m ��� Au��p_�4 ���� uni�4 -��cop�4 \ ���� f"��4-��u�� dec�4u��#���6]�� A8��44-��v�� dec�44u��tag�46-��val�47-��#�� iv��av�4X -��i�4Y R���len�4Y R���.��4Z -��sv�4[ -��<��4` -��sp�4e-�� 6v����4hW��� Hv��p_�4u���� Zv��p_�4v���� p_�4w ����� {v��p_�4���� p_�4����� W��4-��w�� dec�4u��sv�4-��hv�4-��#��- v��p_�4 ���� Qw�� ��4-��p�4 ��e�4 �� 4w��key�4-�� %w��p_�4���� p_�4����� key�4 ��len�4R����� w��cb�420��he�420�� sp�4-��!��4R��� w����4W��� p_�4 ������ w��sp�4 -��!��4R��� w����4 W��� p_�4! ����� p_�4.����� o��4P-��_x�� dec�4Pu��av�4R-��#��} =x��p_�4R ���� Px�� ��4\ -��� p_�4~����� ��4-��x�� dec�4u��S��4R��� ��4 ��#��K len�4 R��� uv�41 ? ����42 R������ >��4>-��y�� dec�4>u��sv�4@-��B9��4AR���*��4B ��#�� buf�4F y��cur�4G �� ny��ch�4K��� ^y��lo�4b? ��hi�4b? ��� ��4m ���� len�4m �� cur�4m ������*��y��0=��� @� ��4(? ��z�� dec�4(u��d1�4*D���d2�4*D���d3�4*D���d4�4*D���cur�4+z��#��9����b$��4/z�� dec�4u�� ch�4 *����&��4Iz�� dec�4u��� ��4-��z�� ��4-����4 \j��enc�4 \�����4~z�� enc�4~z�� sv�4~-�� z��len�4m ��str�4 ��� i�40-��u�4F-����4*��nz�4*����\����4{�� enc�4z�� sv�4-��svt�4 (/��<��4-�� {��.��4 -�� {��!��4R���sp�4 -�� {����4 W��� i�43R����� sp�4G -�� ��4J W����� #?��4j R�����V>��4Q|�� enc�4z�� hv�4-��he�420�� !��4 R��� i�4R���-��4R�����4Q|��hes�4-0�� ?|��sv�4-��� cop�4\ ������20��a|��=���?� $��4R���|�� a�4b�� b�4)b��� -��4wR���|�� a_�4wb�� b_�4w*b��cmp�4yR���a�4{20��b�4|20��la�4~ m ��lb�4 m ���c��4\:}�� enc�4\z�� he�4\20�� sv�4b -��len�4cm ��str�4d ������49}�� enc�49z�� av�49-��i�4;R���len�4; R��� svp�4H-����9��4,}�� enc�4,z�����4"}�� enc�4"z�����4}�� enc�4z���-��4}�� enc�4z�� F��4 R�����O��4v~�� enc�4z�� str�4 �� len�4+m ��i=��44R���end�4 �� ch�4��� ��4m ��uch�4? �������4~�� enc�4z�� ch�4*���e ��4~�� enc�4z�� len�4m �� cur�4m ��buf�4 ���� ��4jR���~�� ��4j-��� ?��4UR���B�� sv�4U-��svt�4W (/�� len�4[m ��pv�4\ ���� ��4AO ���� s�4A����4CO ����4DR���neg�4ER����52��4 ��$s�4����4% ����41X��y��4;R���w��4GR���*��? ��*��R��� dig�4 -��  ��4R���neg�4R������O ��,=��4m ��8��$sv�4-��<��4 ���,��4z��]��$s�4z��$ch�4#? ���,k��4? ����$s�4z��$len�4'm ����44K���,$��4 ��€��$sv�4-��4��4f��4��4+f���,:!��4m ����$l1�4m ��$l2�4m ��<sum�4 f���5J%��4��$sv�4 -�� *c��z ����,-��4-��?��2��4��<sv�4-���5.��4b����4\j��*.��g\��� !��9 -����? ��9 -����9" 0-�� #��9# -��� S��9>��Á�� s�9_�� off�9$N��� +��9/ ����? ��9-�� a�9$_�� b�91_���5��9'��? ��9-��$sv�9-�� <rc�9F-����,d��9ד-��B��$sv�9-���,0��90-��^��? ��9-���6^��:3R�����$__s�:3%��$__n�:3f����:3��4�6M��:R�����$__s�:%����:��4�6���;9�����(/��;9�����;9R���`��;9f���6 ��;"���$��(/��;"�����;"b��`��;"f���O���;���(/��;�����;g��`��;f����I�~��H�}���1B��4�1B��H}��H}��1RBUX YW �� 1U�� .�1�� 1�� 1RBUX YW �� .1@z�� 1U���1��4�1��1RBX YW �� 1��4�1�� U�� �1��1UX!Y!W!��1X!YW ��1RBX!YW ��H�}�� �1��1UX!YW ��1RBUX!Y W!��H}��.�?<n:!;!���1RBUX!Y W ��H}�� 1RBX!Y W!��!% ��"$� > ��#4�1 ��$�1��%1X YW ��&H}��'1RBX Y W ��(.�?<n��� �: ; 9 I8 ���I�� �: ;9 I8 �� �: ;9 I8��4�: ;9 I��(� ��� !I��4�: ;9 I�� �: ;9 I�� �: ; 9 I�� �� �� �: ;9 I���: ; 9 I�� �: ;9 I8 ��.?: ;9 'I<�� �: ; 9 I8���: ;9 I��.: ;9!'���: ;9 I��I�� : ; 9 ��&�I��!�I/ �� : ;9 �� �: ; 9 I k���: ; 9 I�� : ;9 �� : ; 9 ��'I�� : ;9!�� .: ;9!'I��!$� > ��" �: ;9!I k��# �:!4;9 ��$�: ; 9 I��% : ; 9 ��&�:!;9 I��'.?: ;9 '<��('��) : ; 9!��*4�:!4; 9 I��+�: ; 9 I��,.: ; 9!'I��-�<��. �: ; 9 I��/ �: ; 9 I8 ��0!�I/��17�I��2>! !I: ; 9 ��3 �: ;9 I��4���5.: ; 9!'��6.?: ; 9!'I��7 !: ; 9!��8 : ;9!��9 �:!; 9!I8��: !:!;9!��;.?: ; 9 'I<��<4�: ; 9 I��= : ;9 ��>5�I��?!���@4�: ; 9 I?<��A : ; 9 ��B4�:!;9 I?<��C.?:!5;9!'<��D% ��E$� > ��F� ��G&���H �: ;9 I8��I : ;9 ��J : ;9 ��K> I: ;9 ��L.?: ; 9 'I<��M.?: ; 9 '<��N.?: ;9 '��O.?: ; 9 'I���v����K��� ������ ���=���S��������e����k����p���y����������� *������ s  =.LIuv K  =s !<Xp#  p<J . <X ~~f   pr z.q  uf< .uf  L �f���Y �I��  �-��K�tp tt� +�������zJ; .L-J:L;K h<n>  L <Jn-XXL  K ...+�<�ft s.�X� �K��Y���J� U<J+$+<vXH. f&J<+I$<+<iJ��J�� uY %O.KX/JEt <<KI.<zJK+ $]+<H+L$+<tt>#&>�J��#�&�>�J��#�&�>�X��#�&�>�X�� �G�N �F�> ��J�K�>�j>�r��t�J � >�stzf  qfKv'�J��t�  �  g = eLJ g�v��X�0xX W w.< Z <p! JXE w.< rf3 /Int<it ~  ~t cJP Q 0�J  < Xo_X< =JYX<&=J�.JfHJ16:XJ:J=JE<INJQX<_WIJ15:XJ:J=JE<INJQX<_JJ14:XJ:J=JE<INJQX<_KJ13:XJ:J=JE<INJQX<_LJ12:XJ:J=JE<INJQX<_K�J<�$;L$:;L'H=$J'=$J5'tg$J;Bt'<g$J;Bt'<g$JB'fiX'X=;KWK(<*���J<�|B/; |tBX:e |J#$XXX'X=;KWK(<��J #ZX3}f 6v  Xt ~<gXA'Ju<<Y(�� J�} =IJYf f 'KYY(XWJK(<�X<<��H�-�$�J)�'�J�0'YK(YV=KWK(<~+J I+<<#XXXB'Y;KW=KWK(<'Y;KW=KWK(<'Y;KW=KWK(<'Y;KW=KWK(<'Y;KW=KWK(<'X=;KWK(<~ >HJYt<gf'X=K(YWKWK(<~ <u ?GJYt<gt<gfhXQX� 8������~<~r  t<K � ����~��r���<�  t<~r<  t<< �t"��< �H�/XJ~r<'JuXIJ=(<J t<) ~<  t<~r<'JuXIJ=(<J t<)~<  t<�s ��~'JuX<YIJ=(<'JuXIJ=(<J~ruXIJ=(<'JuXIJ=(<J~ruXIJ=(<K Bz Pu sKI= :>J-Y[fkK �� �J� �I�g ��= ��Y ��f�f� >K=I�<��|��r���J�  J |fJ<=|rJ  <F.  ,  � �"~J<.~���0oJ J-� �s{  { 2=;<2%<K%e K  mXJ..5fJEJW<XE<><K>-UJ <J.<5fJEJW<XE<><K>-UJ <J.<<5<JEJW<XE<><K>-UJ <.5fJXW<>XWJ><UJ .<5X>\ {/J�q��X� 4{ C xJt�xX� ��t�t�+5~J>�      D |'JuXIJK(<'JuXIJK(< J>  < /    D }'KWuYIJK(<      DXr�<J�f...JX|'J=WKIJK(<J{'J=WKIJK(<} 0~rJ  <'X~J~r  <X X'~^ LJ  Df <   YZ~rJ  << Kh~J  <Xo ~< J>~<t|  9M1'~q~J  <X>1' :> f t=IB[9KV? $; /X?<f |rJ  < |rJ  < ? ?  X|rJ  < |rJ  < |rJ  < N%J>,<|rJ  J |JJ<*^ |rJ  < �fX|'KWKI=KIK(< J~  ~<~fJ'< ~JJ~<<| @3' :> f tttB[9?U@K<t ; KZc ?}~J  =Xt'J=WK(KWKIK(<'KWK;=ZH=JK(<'KWK;=ZH=JK(<<X<X� XJ|'KWKKI<K(<J~rKKI<K(< |'KWKKH=JK(< X|'KWKKI<K(<J~rKKI<K(<'K;K;=X=JK(t 'KWK;=ZH=JK(<J~rK=ZH=JK(< .Xb|'KWK;=ZH=JK(<? J|'KWK;=ZH=JK(<'KWK;=ZH=JK(<'KWK;=ZH=JK(<'KWK;=ZH=JK(<J~rK=ZH=JK(<'KWK;=ZH=JK(<'KWK;=ZH=JK(< X.X� % tb Jb. <bf tbf  } a Ja. <af taf   �t�J ~ %% %~.}~r  t<%�t�  �J+~~<}r  t<( kX  Z~J ~��J���~~r<  t<J Kh~<  t�<J ~ ~J <~<J~<<|   ; ?  7]V t  t=�j8XtYKtf <~A�~(q~<  t<�JX~'JuXIJ=(<~<  t�<JXr<~  t<}J<-t~ ~<J~<<| J <  ~J Z<>~=;Y��J��~~r<  t<J Kh~<  J }<<�JJ~ ~J <~JJ~<<|   ~~<~  t?�~q~<  t<�JX~'JuXIJ=(<'JuXIJ=(<'J=WKIJ=(<'K;ufYIJ=(<'J=;KKIJ=(<G)Wu=Zv�W�.}'KWK;=ZH=J=(<J~rK=ZH=J=(<.'JuXIJ=(<J~ruXIJ=(<~<  t�<JX#J}'KWKI<=(<J~rKI<=(<'JuXIJ=(<J~ruXIJ=(<'JuXIJ=(<J~ruXIJ=(<'K;ufYIJ=(<'J=WKIJ=(<J~ruIJ=(<'JuXIJ=(<J~ruXIJ=(<'KWK;=ZH=J=(<J~rK=ZH=J=(<t�t*�'�Xgs-'YJKvIY\e/Y|=JMW[ YzJWYytLJiJ-�|~Jt  =X{J'XuXWJK(<X~rZuXWJK(<�' >q/ yX yt  �< .�<J  LJ � >  < Lt v   vJ  thKI<   X:>��� � ;L#M9MYI=j .JXR/<(J9,9J[/<(J9,9JV/<(J9,9JY/<(J9,9JU/<(J9,9JV$..2J!2J<tJ� <�<J"Z  XV.xtx  x�X������X�=�"<?KX 5�%"Y%K - t0 tx  < xGJgt<gt<gfh^  Xc!x < xHJgt<gftJSKtJxX  � "XtJ]tJpXtJX�Xx = <xIK;=;= 9 J 6B y 7XzD&Lt$:<$F|< <!  << t J <  1wf  }fX  t*�f   $t \<X X t&�  u3rf nX t&�  vJ a/ }tt} ." V<<|wJ  t<   |  J vJJ <<5<JJ E T[| J<  $| J  +=XvK<J J:        | J     utY=<< g � }tt} ." V<<}wJ  t<  }    t} J<        } J    J  S 8 qUi +   J }=f7<  o=f7<  o=f7<  o=f7<  o=f7<  o=f7<  o  S 8<{Jf>{ wJt   {wJ  t<  {JtL{ J   !)JX-=!dXv,> XfX<$WYXtZ kJJfJJt$v,>tX <t<<< tt Jt<<J  JX~I |<J=|  L$| J  Lt" |=f7<  o=f7<  o=f7<  o=f7<  o=f7<  o}r)I =);J)J   t v<Xzrt)I =);J)J =f7<Jm<X7tt=<< ttK<J  < { o t <�t<XX�"Y  }J<.. }fX�YM %J""*  w+= J tfZ,LJ>tXD.D<X > �tYY  X } JfW.|J[*;+}r.) <)JW|X*e$  t k<Xzr)I =);J)Jr)I =);J)J |v~r)I =);J)Jr.)I =);J)J�t�.tX  /;tXX*�  �� �v��J#!� �Z|X t  << x ~=f7<  oyX trt<XX ~ nX o< \XtXXuOZ.X �t<XX~rt)I =);J)Jr.)I =);J)Jy  ft<XXX    KY+ . Jz< B < 1 w vX0fX}r) <)JrX)I =);J)Jr.)I =);J)J  S nX o<X ~ ?t A<XXvX Jv<<JX " �UtX  �t <X�XJ|Jff|JX<{r) <)JtXXu O. tX^X}XX#��t"��t }X.wX  XuXX   XR! =- =X *w+=J tfK-JKq? X? .?.X>uYY < f SXifJK-=W>wXX l< X ( Xf-�� ��M �G�M!��X�X"� �fh   JY Y g >ywX    XJJh" .0�0<�-X z=f7<  oX-�*��S t Wy  t<d    G  sX  Js<<J G? X s/IYK=u)Lr#�<sJL f  y=f7<  oXr) <)JJzr)W =);J)JsX  2 X2#�9}(XvK  XXoX#  p.J < X JtufZ<X ���~ � �p< � �=�<���pJ�� �������p.��J���t� � f� #� X � ~.� J� � � �p�� �~�t���J��� ������ /s .XXiX#  i.J < X JtufL< {Xn.J {n nX n KXf{<8X.8< Y8- =����J �z.�J� �zX�<�f... �z ��.nX� 0������ /s .XXiX#  i.J < X JtufL< zXn.J zn nX n KXz<=X.=< Y=- =����J �z.�J� �zX�<�f... �zt��.nX� ������yK  XXoX#  o. Jo< < XtJuX  y.s Q<y> X .*8+8[5& x 5u<~fXX!-K Y- K�J��f�X� �~.� �~X�<���.�Jntf\~tJ.NifJ< X*�o �~��X s� ������,K  XXoX#  o.J < X Jtu fzL <X .*8+8[5&  ~ � �<���Y�I�K�..J*�of �~��. u� ������K  XXoX#  o. Jo< < <u IKKJ z.fv X .*8+8[5-  ~f< @�f��f�X� �~.� �~X�<�p.tJ<XX�t�t��J �~X� �+ <<��f �~��*��t #t� P������)K  XXnX#  n.J < X ;KK tv tzL <X .*8+8[5&  ~t. ~���J�J��X���f�. �~t���J�t*� u� 0������K  XXmX#  m. Jm< < XtJuJ  z.s P<z> X .*8+8[5&  x*   J <|JJptYK<J 4� �L����f��f�X� �|.� �|X�<���.�.�.pX  Xt'�}*� �}(��pX t� ������'K  XXmX#  m. Jm< < XtJuJ +s R<x>  ] )t J. X .*8+8[5&  x* | X J <|J i  - <  +�f��f�X� �|.� �|X�<���fc |X X|t g < + L  J'f�.� JptYKY<J$pX.*� �|��Xp. r� ������6K  XXlX#  l.J . X tJf<L< \* < .*8+8[5&  |t. |X <<7.7< Y7- =����J �|.�J� �|X�<��.�.*�s �|(��. s� ������K  XXlX#  l.J . X tJf<L< \* < .*8+8[5&  |t. |X X<<.<< Y<- =����J �|.�J� �|X�<��.�.*�s �|��. s� М������ /s .XXlX#  l.J . X tJf<L @8 \*  .*8+8[5&  |t.| X f<=.=< Y=- =�X���K� �J�<�pJ���< �<� fX |.J |X< �|& �p� ��<p  2 X29֬<*�  �|(��X bX� ������K  XXjX#  k.J < X JJ<K b <X .*8+8[5&  {  FJC.$Xog� ��! �-�=�X��������J�JF�{C�X*�. {X� �������K  XXjX#  j.J < X JJ<K c <X .*8+8[5& {t.{JoXYKY<J  {.  uJf.*�o<j. w� `������K  XXjX#  j.J < X JJ<K c <X .*8+8[5&  {t. {JoXYK<J JotYK<J JotYK<J JotYK<J JotY=<<f.*�qJj(XXXXX w� ������{K  XXnX#  n. Jn< < XtJuJ  z.s P<z> X .*8+8[5& W }f Kt���f�X� �}.� �}X�<���fv!f<f!�t��� �}��*� t� @�������K  XXmX#  n. Jn< < XtJuJ  z.s P<z> X .*8+8[5& . }f K�f��f�X� �}.� �}X�<���fvX f<f �t��" �}��*� t� ������`K  XXnX#  n.J < X JJ<KeX��� ��X�� .*8+8[5&  }t)JJ}J X. �*��t�J��L��������n*�( � � yu� �������K  XXmX#  m.J < X JJ<KeX��� ��X�� .*8+8[5&  }t)JJ}JJJ X. J< X�*��t�J���L��������n*�( � � yu�  ������K  XXjX#  j.J < X JJ<K c <X .*8+8[5&  { J ZJ��J�u� �� �u��J �{*�0 w� ������~ Ks <XfkX#  k< Jk< < XtJuJ  z<s P<z> X <*898[5' @8it<X{ J ) J K"9J    %J"Jt;!mI!����t�L+��tnJ n<<X(t  ~ JL<)  tK%J<  <J�J|J,X �<<J B: L �X<J R<<JXg8xR vJJ,~JJJ7JJ� <K<KXYK;=�J��^�z<�J�L����G�u��L��v��~��� �J�L��<)  tKL%J<<X< x<%= !�1�!�U�<�lhJ2 zJ2JzJ 7 g t t ;J ." }  IK:<J lJJOLHL0)K A <)K <npJ nJ JJ�KZu!kI! ~B'Kb<X |  JK  = K I   J'Kh<rXJ'K^"<XK['K ~J�fX J+}" G*��|(��X%|�f~J)KX8* t�{XN  2pJet 2X$Xt   -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uXr  -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX ! -< uX tt -< uXY - =X! *w* t< wJ��� � ;� {t -u p . up 1Itptt p X<p.Itptt X ,s u  w.<tZ0>d�wX�I��X�=�-��X�=�-��q����i�� ������ ���}��S���R����O��=��� =k�������������������c��e��������������� ����*��q������������!�����������M������������� ����������9����7��I����{����(���������A��>��e����1��������k����p���y��������GNU GIMPLE 11.5.0 20240719 (Red Hat 11.5.0-5) -m64 -mtune=generic -march=x86-64-v2 -g -O2 -fno-openmp -fno-openacc -fcf-protection=full -fPIC -fstack-protector-strong -fltrans -fplugin=gcc-annobin�__builtin_memset�__builtin_memcpy�__builtin___sprintf_chk�__builtin_memmove�__stack_chk_fail�Ilaststatval�long long int�Perl_av_push�old_parser�subtr_ass_amg�blku_oldsaveix�Iorigargc�Iorigargv�XS_JSON__XS_incr_skip�si_errno�keeper�__pad0�tbl_arena_next�_spent_size�Iin_utf8_CTYPE_locale�hostent�ls_prev�close_paren�lex_stuff�xpvgv�Istatcache�Icompiling�Idbargs�new_perl�Perl_newSVsv_flags�blku_oldsp�sub_error_count�xpvhv�PERL_CONTEXT�_asctime_buffer�Inumeric_standard�XS_JSON__XS_DESTROY�prevcomppad�Ie_script�Perl_newSV_type�xhvnameu_name�sv_u�enable�Ipreambleav�Perl_save_vptr�IDBcontrol�xpvio�uintptr_t�xpviv�tbl_max�si_tid�Imy_cxt_size�blku_old_tmpsfloor�Imain_root�Perl_SvREFCNT_inc�xcv_outside�blku_type�_PerlIO�__locales�TARGu_uv�he_valu�Iutf8_totitle�_spent_struct�named_buff�want_vtbl_hintselem�h_length�op_first�Idoswitches�_netent_size�thrhook_proc_t�next_branch�Perl_POPMARK�GNU C17 11.5.0 20240719 (Red Hat 11.5.0-5) -m64 -march=x86-64-v2 -mtune=generic -g -g -O2 -flto -ffat-lto-objects -fexceptions -fstack-protector-strong -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection=full -fwrapv -fno-strict-aliasing -fPIC -fplugin=gcc-annobin�block_eval�_lEnGtH�s_port�Iargvgv�gp_refcnt�prev_mark�Idef_layerlist�saw_infix_sigil�Irestartjmpenv�save_lastloc�Iwarn_locale�_spent_buffer�Icolors�mg_obj�je_old_delaymagic�fallback_amg�multi_end�cb_object�PerlIO_list_s�PerlIO_list_t�COPHH�scream_pos�xpvmg�despatch_signals_proc_t�rshift_ass_amg�Perl_apply_attrs_string�xio_flags�Isharehook�tm_mday�old_regmatch_state�xcv_xsub�nextword�Iminus_E�Icheckav�pad_1�pad_2�Imarkstack�xpvnv�Idump_re_max_len�tm_zone�bool_true�Istatusvalue�IDBsingle�utf8_substr�min_offset�pmop�st_atim�sival_int�Ilast_in_gv�XS_JSON__XS_encode_json�Ireg_curpm�share_proc_t�Ihash_rand_bits_enabled�pw_gecos�uaccum�_call_addr�long double�op_private�lex_formbrack�SVt_LAST�eaccum�__ch�sbu_dstr�Irunops�Ipsig_pend�_ctime_buffer�Icomppad_name�Imarkstack_max�sbu_iters�incr_m_num�internal�Ireentrant_retint�saved_curcop�max_amg_code�strcmp�__blkcnt_t�PTR_TBL_t�clen�sig_seen�xhv_max�_protoent_size�XS_JSON__XS_encode�hent_hek�xhv_aux_flags�_grent_ptr�_getlogin_buffer�xivu_eval_seen�__locale_data�pos_flags�Istack_base�exec�Imax_intro_pending�poscache�op_pmstashstartu�to_gv_amg�group�sbu_strend�smart_amg�re_scream_pos_data_s�cop_stashoff�st_size�sge_amg�pthread_key_t�Iperldb�lastparen�si_addr_lsb�Iinplace�__locale_t�xpvuv�_pkey�Perl_pop_scope�tm_min�IDBline�want_vtbl_nkeys�sin_amg�Isv_arenaroot�jump�gp_egv�newval�padname�states�xio_bottom_gv�Iphase�yylen�subbeg�cos_amg�_asctime_size�Iblockhooks�end_of_scalar�end_shift�Perl_get_sv�sbu_oldsaveix�_pwent_ptr�Iosname�n_addrtype�lex_casemods�lex_brackstack�numbered_buff_STORE�Iefloatsize�leave_op�PADLIST�Ipeepp�PADNAME�mro_which�Iregex_pad�retop�pkg_gen�xcv_padlist_u�minmod�sp_pwdp�Iutf8_foldclosures�Ilocale_utf8ness�Isv_yes�parenfloor�branchlike�JMPENV�Imain_start�qr_anoncv�Istashpad�_arch�my_perl�Ienvgv�Iperlio�Ipadname_const�need�Perl_xs_boot_epilog�Perl_get_cv�__wchb�pow_ass_amg�mult_ass_amg�decode_4hex�Iregmatch_state�prev_rex�Iisarev�Iutf8locale�tm_mon�op_opt�c2_utf8�enc_t�subcoffset�svu_fp�gcvt�yy_stack_frame�Idebstash�topword�destroy_gen�want_vtbl_ovrld�reg_substr_datum�__int128�si_stack�xpadl_max�Inomemok�tm_hour�firstpos�XS_JSON__XS_filter_json_object�want_vtbl_debugvar�Imbrlen_ps�Idiehook�prev_recurse_locinput�any_ptr�Sighandler1_t�exp2�CLONE_PARAMS�bool_false�Icompcv�padnl�concat_ass_amg�spaces�lex_repl�timespec�PerlInterpreter�xpadnl_max_named�expo�Perl_hv_common_key_len�encode_utf8�ILatin1�Isighandler1p�st_nlink�Iminus_F�re_eval_str�Iscopestack_ix�sp_max�Iscopestack_max�iter_amg�Iminus_a�any_pvp�Iminus_c�want_vtbl_arylen_p�Iminus_l�Iminus_n�Iminus_p�Iargvout_stack�Perl_pv_uni_display�Iinitav�Perl_newSVpvn�Perl_newXS_deffile�Perl_SvREFCNT_dec�tm_yday�tbl_items�Perl_ophook_t�cache_mask�firstchars�xpvlenu_rx�Imaxsysfd�Ilocalizing�limit�Isighandler3p�lex_shared�servent�_crypt_struct_buffer�rxfree�ncmp_amg�pw_name�sp_lstchg�curly�_getlogin_size�nomethod_amg�add_amg�decode_num�Iunicode�__fmt�blku_sub�qr_package�neg_amg�Irestartop�encode_av�ICCC_non0_non230�PL_thr_key�gofs�__mask_was_saved�PERL_PHASE_CONSTRUCT�Ilastgotoprobe�cop_line�Isecondgv�__locale_struct�Isavebegin�want_vtbl_vec�initialized�XPVAV�to_av_amg�STRLEN�exitlistentry�encode_ch�Perl_utf8_length�abs_amg�op_ppaddr�xpadnl_alloc�Icheckav_save�Idebug_pad�__jmp_buf_tag�concat_amg�Iendav�blku_oldscopesp�Iutf8_idcont�Icomppad_name_fill�my_op�IHasMultiCharFold�__mbstate_t�tmpXSoff�XPVCV�Perl_savetmps�xhv_last_rand�mark_stack_entry�regnode�Perl_gv_stashsv�xhv_name_u�Iperl_destruct_level�si_cxix�mg_virtual�padnamelist�interpreter�PMOP�Istashpadix�Perl_xs_handshake�st_uid�longfold�sp_min�xcv_xsubany�PADOFFSET�sbxor_amg�snprintf�sbu_rflags�xpv_cur�xpadn_flags�Istderrgv�xio_page_len�xhv_eiter�perl_memory_debug_header�xhv_riter�Iin_clean_all�si_type�mark_name�Istashpadmax�old_regmatch_slab�json_atof�__ino_t�reg_substr_data�lex_super_state�_grent_struct�xcv_root_u�curlym�setting�encode_hk�Icustom_op_descs�PL_hexdigit�si_prev�XPVGV�_addr_bnd�sp_namp�lex_starts�Isavestack�Sighandler3_t�si_code�encode_rv�Imodcount�prev_curlyx�Isortstash�Istdingv�svt_local�sp_warn�Icustom_ops�sbor_ass_amg�CHECKPOINT�XPVHV�any_av�sprintf�_grent_buffer�sne_amg�decode_utf8�maxdepth�last_uni�XPVIO�sp_expire�XPVIV�XS_JSON__XS_max_size�lex_flags�minlenret�Iofsgv�TARGi_iv�Idelaymagic_gid�xcv_gv_u�Iunderlying_numeric_obj�Icollxfrm_mult�Perl_sv_grow�tbl_arena_end�Isavestack_ix�want_vtbl_defelem�is_nv�SVt_PVAV�xmg_magic�svu_gp�any_dptr�intuit�Ibody_roots�si_sigval�hek_len�Icollation_ix�memcmp�tokenbuf�op_nextop�line_t�Perl_sv_setuv_mg�mgvtbl�Iutf8_xidcont�si_cxstack�XS_JSON__XS_decode_json�yyerrstatus�Siginfo_t�_hostent_ptr�sbu_rx�xcv_padlist�resolve�svt_get�Perl_utf8n_to_uvuni�_Bool�svu_iv�XPVMG�encode_nl�sbu_rxtainted�seq_amg�div_ass_amg�op_moresib�xpv_len_u�Ipatchlevel�qsort�Perl_call_sv�Perl_sv_utf8_downgrade_flags�_pwent_struct�nextval�svu_pv�XPVNV�any_gv�XS_JSON__XS_incr_reset�not_amg�Isv_count�sbu_orig�_servent_struct�decode_av�postdp�__gid_t�Iparser�xpadlarr_dbg�stack_max1�runops_proc_t�any_hv�XS_JSON__XS_decode_prefix�Ipadlist_generation�destroy�SVt_PVFM�xpadnl_max�Idefoutgv�_lower�Istatusvalue_posix�Perl_utf8_distance�_pwent_buffer�Iutf8_tosimplefold�__ctype_tolower�siginfo_t�Perl_newSViv�any_iv�json_nonref�Ichopset�Irpeepp�oldcomppad�sbu_rxres�SVt_PVGV�Iincgv�Perl_newSVpvn_flags�si_markoff�xpadnl_fill�want_vtbl_arylen�tv_nsec�nexttype�sig_slurpy�want_vtbl_backref�Icurpm_under�SVt_PVHV�encode_str�lshift_ass_amg�Sighandler_t�pthread_getspecific�svu_hash�ISCX_invlist�svu_nv�sband_amg�si_cxsubix�lex_inpat�last_lop�tm_sec�encode_sv�ptr_tbl_arena�XS_JSON__XS_incr_parse�SVt_PVIO�SVt_PVIV�filtered�Ilastfd�want_vtbl_collxfrm�Ieval_start�ls_linestr�Perl_gv_stashpv�PADNAMELIST�SVt_PVCV�PERL_PHASE_START�__src�xcv_hscxt�any_u32�Perl_croak_nocontext�encode_space�_ctime_size�repeat_ass_amg�op_pmreplrootu�XS_JSON__XS_decode�Isavestack_max�Perl_newSVnv�XPVUV�xhv_rand�Ilocalpatches�Isv_root�SVt_PVLV�p5rx�op_next�decode_hv�__saved_mask�svu_rv�copline�any_op�Icurstack�numtype�SVt_PVMG�Ipadix_floor�Perl_push_scope�si_status�xpadl_arr�h_addrtype�_strerror_size�Idelaymagic_euid�bufend�Perl_newSVpv�lex_inwhat�any_pv�incr_m_tfn�atan2_amg�xnv_nv�ptr_to_index�SVt_PVNV�Perl_utf8_hop�Iopfreehook�ssize�_protoent_ptr�Iunitcheckav�svu_uv�PerlIOl�sgt_amg�to_hv_amg�Isetlocale_bufsize�xpvhv_aux�protoent�mg_len�Imemory_debug_header�SVt_IV�Itop_env�want_vtbl_sigelem�__blksize_t�hes_stack�short unsigned int�_spent_ptr�Perl_av_fetch�bool__amg�Itmps_stack�yy_lexshared�INCR_M_BS�INCR_M_STR�offs�any_sv�want_vtbl_substr�v_false�Isv_undef�Ipsig_name�LEXSHARED�INCR_M_C0�INCR_M_C1�clone_params�perl_drand48_t�Igensym�Iregmatch_slab�op_redoop�rsfp�_hostent_struct�start_tmp�xio_fmt_name�svt_len�cop_hints�__len�h_name�Ierrors�xpvlenu_len�h_aliases�offset_return�_hostent_size�op_pmreplstart�hent_refcount�Perl_newSVuv�saved_copy�lex_sub_inwhat�any_uv�Itmps_floor�Istrxfrm_is_behaved�__wch�xpadl_id�dec_amg�int_amg�Ibasetime�Iop_mask�Isighandlerp�unreferenced�Isignalhook�xpadnl_refcnt�loceol�IUpperLatin1�mro_linear_current�xio_ofp�__value�_hostent_buffer�cop_seq�multi_start�op_pmreplroot�SVt_NV�IDBtrace�maxlen�pre_prefix�op_targ�Ibeginav�je_ret�resume_state�Isv_consts�pw_dir�strlen_sum�lex_casestack�op_lastop�Isub_generation�incr_m_str�blku_eval�float�want_vtbl_isaelem�boot_JSON__XS�__count�unsigned char�si_cxmax�multi_open�Perl_gimme_V�_kill�st_rdev�LOOP�ILB_invlist�SVt_PV�Perl_sv_cmp_flags�want_vtbl_dbline�REENTR�Imess_sv�Iglobalstash�Imin_intro_pending�expect�oldloc�decode_sv�Icollxfrm_base�modechange�want_vtbl_envelem�copy_amg�Iutf8_perl_idcont�cx_blk�Istatname�RETVAL�Perl_gv_fetchmethod_autoload�modulo_amg�xnv_u�__uid_t�XS_JSON__XS_get_ascii�blku_gimme�XSUBADDR_t�st_ctim�recheck_utf8_validity�decode_hexdigit�Iutf8_tofold�xcv_root�ISB_invlist�block_format�op_sibparent�old_namesv�log_amg�xpadn_type_u�IAssigned_invlist�peep_t�xhv_backreferences�Iinternal_random_state�Perl_sv_free2�Isv_no�v_true�minlen�__off_t�perl_phase�Iin_clean_objs�Isv_zero�super�PERL_PHASE_INIT�decode_ws�PERL_PHASE_DESTRUCT�in_pod�Perl_stack_grow�gp_io�Imultideref_pc�he_cmp_slow�json_sv_grow�Iors_sv�string_amg�xpadn_protocv�xuv_u�Ievalseq�Iunlockhook�regexp_engine�mg_flags�subtr_amg�Icurstash�gr_passwd�Perl_markstack_grow�shrink�_gmtime_struct�Perl_sv_upgrade�gr_gid�incr_nest�want_vtbl_checkcall�Perl_safesysrealloc�si_overrun�__clock_t�SVt_NULL�ls_bufptr�Ibeginav_save�Perl_grok_number�__uint32_t�Iorigfilename�XS_JSON__XS_incr_text�xmg_hash_index�dec_t�last_lop_op�cop_warnings�Icop_seqmax�op_pmtargetgv�form_lex_state�incr_m_c�Istatgv�Idestroyhook�st_blocks�max_offset�sbu_m�decode_comment�sbu_s�save_curlyx�Icomppad�sub_no_recover�lex_dojoin�xmg_u�incr_mode�gp_cvgen�PL_utf8skip�XS_JSON__XS_boolean_values�xcv_file�Perl_sv_bless�itervar_u�gp_flags�xiou_dirp�_servent_buffer�paren_names�Iregistered_mros�si_uid�pw_passwd�Iin_some_fold�sqrt_amg�lex_allbrackets�opval�interrupt�Icurcopdb�block_sub�pos_magic�gp_file_hek�Perl_hv_placeholders_get�sv_refcnt�__nlink_t�tbl_ary�mro_alg�sband_ass_amg�xav_alloc�json_stash�si_fd�nparens�xpadn_refcnt�scmp_amg�Ieval_root�old_eval_root�named_buff_iter�st_gid�Idowarn�yychar�Ifirstgv�rshift_amg�XS_JSON__XS_CLONE�mg_moremagic�XS_JSON__XS_get_boolean_values�op_pmoffset�xhv_name_count�op_pmstashoff�Inumeric_underlying_is_standard�PERL_SI�MGVTBL�op_static�MAGIC�Perl_sv_newmortal�Itmps_max�want_vtbl_pack�Ithreadhook�blku_givwhen�gr_name�op_type�Iutf8_perl_idstart�INCR_M_WS�Perl_hv_iterinit�sublen�blku_oldmarksp�xivu_iv�_netent_ptr�want_vtbl_regexp�Ipadname_undef�preambling�xhv_mro_meta�Inumeric_underlying�_upper�cx_u�output�IDBcv�dec_cur�trie�Ilockhook�__ctype_toupper�Perl_newRV�_xnvu�xio_lines_left�compflags�want_vtbl_utf8�Iin_load_module�xio_page�Perl_newSV�sigjmp_buf�pow_amg�want_vtbl_hints�tm_isdst�div_amg�Ilaststype�__ctype_b�h_addr_list�Iutf8_charname_continue�in_my_stash�want_vtbl_regdata�xpadn_len�_strerror_buffer�add_ass_amg�dummy�__int128 unsigned�Iunitcheckav_save�si_stime�lastcloseparen�short int�ifmatch�Perl_mg_get�Idumpindent�Ioldname�preambled�op_code_list�xhv_keys�itersave�sbxor_ass_amg�IAboveLatin1�Iutf8_mark�_servent_size�si_signo�IDBgv�__names�sv_any�blk_u�xcv_start�accepted�gvval�IWB_invlist�olddepth�Iutf8cache�sv_json�_localtime_struct�_bounds�prev_eval�Ipadix�defsv_save�want_vtbl_lvref�_netent_buffer�xcv_stash�YYSTYPE�_xhvnameu�xcv_gv�cop_hints_hash�Icustom_op_names�lex_sub_repl�sle_amg�encode_indent�he_cmp_fast�xpadn_high�re_scream_pos_data�get_bool�hek_hash�_ttyname_buffer�Iknown_layers�_netent_errno�Itainting�Icurcop�Istack_sp�__ssize_t�any_bool�regmatch_info_aux�PERL_PHASE_END�Icollation_standard�__glibc_reserved�lex_defer�xmg_stash�Iorigalen�sbu_maxiters�Idebug�refcounted_he�Icurpad�INCR_M_JSON�json_init�__time_t�st_mtim�want_vtbl_uvar�s_proto�sbu_targ�__dest�logical�Iforkprocess�lex_brackets�xio_top_gv�Iutf8_tolower�Perl_newRV_noinc�blku_oldcop�Icurstackinfo�Istart_env�lex_fakeeof�lex_sub_op�stashes�Istashcache�INCR_M_NUM�XS_JSON__XS_ascii�xnv_lines�mult_amg�want_vtbl_packelem�p_aliases�_netent_struct�in_my�next_off�xivu_uv�Imodglobal�regmatch_info_aux_eval�xcv_start_u�want_vtbl_env�basesp�Igeneration�IGCB_invlist�Istrtab�xpadl_outid�xpadn_low�block_givwhen�regexp_paren_pair�klass�PL_WARN_NONE�crypt_data�pprivate�cv_flags_t�cur_top_env�xpadn_typestash�Iin_utf8_COLLATE_locale�state_u�PERL_PHASE_RUN�_sigfault�op_spare�lex_op�st_ino�cop_features�__pid_t�parsed_sub�op_last�Perl_free_tmps�proto_perl�want_vtbl_regdatum�xio_type�yylval�Perl_sv_derived_from�sp_inact�xav_fill�hent_val�Perl_sv_2uv_flags�Iorigenviron�Idelaymagic_egid�gp_av�ftest_amg�Perl_sv_utf8_upgrade_flags_grow�scream_olds�mg_ptr�maxpos�json_atof_scan1�ptr_tbl�Inumeric_name�lazyiv�_sifields�want_vtbl_pos�SVt_REGEXP�Ipsig_ptr�gp_cv�xgv_stash�netent�tv_sec�tm_year�blku_u16�Iprofiledata�sbor_amg�__sigset_t�gp_line�Imainstack�Icurpm�op_pmflags�st_blksize�xpadn_ourstash�ptr_tbl_ent�_hostent_errno�op_slabbed�scompl_amg�Isubline�Iargvoutgv�Iwatchaddr�Idefgv�hek_key�PerlExitListEntry�xio_bottom_name�XS_JSON__XS_new�gp_form�Perl_newXS_flags�Ireentrant_buffer�hent_next�check_ix�op_flags�Iunsafe�tm_wday�Ihintgv�__jmp_buf�IDBsignal�Iutf8_charname_begin�Ilanginfo_bufsize�blku_format�__dirstream�Ihash_rand_bits�IXpv�Iregex_padav�blku_loop�cache_offset�wanted�pw_uid�len1�len2�_timer�Istrxfrm_NUL_replacement�IInMultiCharFold�je_old_stack_hwm�sig_elems�gr_mem�Ixsubfilename�gp_hv�Ipad_reset_pending�dfoutgv�_sigchld�xcv_depth�Itaint_warn�incr_m_json�self�Imbrtowc_ps�pw_shell�si_next�want_vtbl_nonelem�_syscall�Iexitlist�Ilanginfo_buf�Isubname�any_i32�Ihv_fetch_ent_mh�UNOP_AUX_item�XS_JSON__XS_get_max_size�svt_dup�xcv_flags�mro_linear_all�Perl_sv_setiv_mg�Inumeric_radix_sv�globhook_t�xcv_outside_seq�bool_stash�Isplitstr�xcv_hek�svt_free�long long unsigned int�si_addr�Ibody_arenas�checkstr�_grent_size�Perl_hv_iterval�SVt_INVLIST�want_vtbl_mglob�Isortcop�Isignals�sbu_type�si_pid�mg_private�dupe�je_buf�lazysv�cb_sk_object�Iwcrtomb_ps�Itoptarget�Istrxfrm_max_cp�Ierrgv�Perl_sv_2pv_flags�inc_amg�svt_clear�PERL_PHASE_CHECK�nexttoke�Itmps_ix�Isig_pending�substrs�any_svp�intflags�destroyable_proc_t�Ifdpid�decode_tag�xpadlarr_alloc�ival�any_dxptr�n_net�to_sv_amg�Perl_croak_xs_usage�op_pmtargetoff�Icollation_name�Iefloatbuf�to_cv_amg�magic_vtable_max�_pwent_size�oldval�any_long�xiou_any�ITR_SPECIAL_HANDLING_UTF8�XS_JSON__XS_max_depth�lshift_amg�Iexit_flags�c1_utf8�repeat_amg�XS_JSON__XS_filter_json_single_key_object�Icurlocales�Iglobhook�jsonstr�encode_comma�xio_top_name�IPrivate_Use�INCR_M_TFN�modulo_ass_amg�Iptr_table�Icolorset�Perl_hv_iternext_flags�__jmpbuf�Ifilemode�__dev_t�Iexitlistlen�numer_amg�op_folded�Idelaymagic�Imarkstack_ptr�block�pw_gid�tm_gmtoff�prev_yes_state�s_name�_protoent_struct�op_comp�gp_sv�svu_array�whilem�IInBitmap�mother_re�__val�n_aliases�_sigsys�extflags�xio_fmt_gv�xpadn_gen�cop_file�Icurstname�cx_subst�svt_set�Idefstash�Itainted�Ibodytarget�oldoldbufptr�xav_max�xiv_u�_protoent_buffer�st_mode�savearray�_xivu�__compar_fn_t�Iutf8_xidstart�re_eval_start�perl_debug_pad�Istack_max�cache_gen�svtype�st_dev�XS_JSON__XS_get_max_depth�je_prev�want_vtbl_sv�Iclocktick�Perl_sv_2iv_flags�__syscall_slong_t�IXPosix_ptrs�IDBsub�spwd�Iutf8_idstart�je_mustcatch�numbered_buff_LENGTH�yy_parser�block_loop�op_savefree�Iscopestack�Iformtarget�pad_offset�mro_nextmethod�s_aliases�lastcp�Iconstpadix�multi_close�stat�herelines�Imy_cxt_list�IPosix_ptrs�xivu_namehek�_ttyname_size�want_vtbl_taint�Iwatchok�is_utf8�Perl_av_len�p_proto�Perl_sv_2mortal�want_vtbl_isa�svt_copy�xnv_bm_tail�Perl_sv_chop�sival_ptr�Perl_hv_common�mark_loc�si_utime�xpvav�Isrand_called�strlen�decode_str�regexp_amg�__mode_t�sp_flag�perl_key�Ireplgv�Ibreakable_sub_gen�encode_hv�rsfp_filters�Iin_eval�suboffset�__sigval_t�_servent_ptr�lex_re_reparsing�Iutf8_toupper�Perl_hv_iterkeysv�linestart�sig_optelems�xhvnameu_names�incr_pos�old_cxsubix�Icv_has_eval�mg_type�xpvcv�ref_bool_type�Isetlocale_buf�numbered_buff_FETCH�Irandom_state�Iscopestack_name�si_band�xpadn_pv�Icomppad_name_floor�kflags�Idelaymagic_uid�Iwarnhook�_xmgu�_sigpoll�slt_amg�xio_dirpu�Iin_utf8_turkic_locale�cur_text�blku_oldpm�Imain_cv�<artificial>�/root/.cpanm/work/1747340161.50608/JSON-XS-4.03�/usr/lib64/perl5/CORE�/usr/include/bits�XS.xs�XS.c�inline.h�stdio2.h�string_fortified.h�<built-in>�__sigset_t.h�intrpvar.h�pthread.h�stdint.h�av.h�__locale_t.h�iperlsys.h�string.h�handy.h�perlvars.h�grp.h�struct___jmp_buf_tag.h�dirent.h�__sigval_t.h�util.h�overload.h�pwd.h�/usr/lib/gcc/x86_64-redhat-linux/11/include�crypt.h�stdlib.h�gv.h�netdb.h�utf8.h�mg.h�perlio.h�/usr/include�struct_timespec.h�time_t.h�regexp.h�cv.h�pthreadtypes.h�cop.h�hv.h�stdint-uintn.h�pad.h�parser.h�mathcalls.h�reentr.h�proto.h�perly.h�mg_vtable.h�sv.h�/usr/include/sys�__mbstate_t.h�siginfo_t.h�/usr/include/bits/types�stddef.h�struct_tm.h�perl.h�setjmp.h�shadow.h�struct_stat.h�7��������������� U 5V56U���"6P�����@RURU�����@VTVT�������psSswq�wxs���_pP�������������UVTUUU���������������TTTP\QVTQ"1%T"Q" !HTQ"1%T"Q"TQ"1%T"Q"#蟀+(�\8t�1%t�" !Ht�1%t�"t�1%t�"#蟀+(����������QQQQ�Q���TT�����t�1%Pt�1%�T����p�t�"P�����������������������������UUUUuRRUUuUUuRuR���������������������������T^T^T^T^T^T^T�������������Q\Q\Q\�������������R]R]R]�����������������������XXX1XX1XX1XX1XX1��������������0SSPS0S������������0VvVV0V������������������PPPpPpPTPPpPpP��������0QQ0��������0U010���Q�������Rp@������"u�#@������"�����Tu�#@������"���X�������������Pu�p|u�4Pu��������������UV U U V U���������p�Qu  Q���P����� U U������ U U U������������� U \ U \U\������������������������ T V V V v V v V vVvVvVTVV�������������� Q S s S s S SS������������� R ] R ]R]������ _ TQ" _���� SS������ UU\�����Q^���P�������������������������������������������������� P P v� P v� P v� P v� P v�Pv�Pv�Pv�v�Pv�v�v�v�v�v�v��������������������������������XXXPXXXXXXXX��������������QQ�v������VV���������V�����YY�����\\�����P�����PQ�����QQ�����\\�������UT���P�� ��������|������YY�����\\�����R���P�����XXX���������|�|�#R|�|�#R|���X����|�|�#R�����XX�����\\�����R���P������ YYs���� \\�����R���P������ YYs���� \\�����R���P������ YYs���� \\�����P�����PQ������ YYs���� \\�����P�����PQ������ YYs���� \\�����P�����PQ������YYs����\\�����P�����PQ������YYs����\\�����P�����PQ���������USU#S�������������T\T \ T #\����" !"������US !S����1 !1����U !S����� Q !V���!!P�������VV V���T���""���SS���11���SS�����QV���P���: :���S S���1 1���S S�����Q V��� P����S!"S����1!!1����S!!S�����!!Q!!V���!!P��!" ��!"S��!"1��!"S�����!!Q!"V���!"P���� S"#S���� 1""1���� S""S�����""Q""V���""P��"# ��"#S��"#1��"#S�����""Q"#V���""P�����##U##U�������##T##X##T���##P��##u����##t������##Q##V���##P���������#$U$$U$$U$%U���������#$T$$S$$T$%S�����������������������������������������������%&U&'V''U'(V((U((U(+V++U+-V-.U./V/0U02V22U23V33U33U3>V>>U>MVMNUNOVOPU���������������������������������%&T&']''T'(]((T(+]++T+/]/0T03]33T34]4OTOO]OOTOP]�����&'^./^����&&"./"����&&V./V����&&1./1����&&V./V�����..Q./\���./P����''".."����''V..V����''1..1����''V..V�����..Q..\���..P�����������������������������������,.\/1\13\33T44\5;\>?\@B\BB]BC\DD\DE\FG\HI\KL\LL\MO\OP\�������������������������������������,-V-.U//V/0U01V12V22U23V33U33U4>V>>U>DVDMVMNUNOVOOVOPU���������������,-Q//Q//p�11Q23Q44Q55p��������������������������������89P9=@@P@BDDPDDDDPDDEFFFIJLMNNNOOO�������������������������/0]55]78]8=]@B]DD]EF]FF]IJ]LN]NO]OO]���������������;;P;>SEFSIKSLMSNOSOOS��������������������9:_::p;>_EF_IK_LM_MMPMM_NO_OO_�������9;SMMSMMP����;;(LM(����;;VLMV����;;1LM1����;;VLMV�����LLPLM\�����MMPMMQ����;;"LL"����;;VLLV����;;1LL1����;;VLLV�����LLPLL\�����LLPLLQ����<<"II"����<<VIIV����<<1II1����<<VIIV�����IIPII\�����IIPIIQ����<<)JJ)����<<VJJV����<<1JJ1����<<VJJV�����JJPJJ\�����JJPJJQ����<=[IJ[����<=VIJV����<<1IJ1����<<VIJV�����JJPJJ\�����JJPJJQ��������==0==s�}�"1==s�}�"2FFs�}�"1����==,FF,����==VFFV����==1FF1����==VFFV�����FFPFF�����FFPFFQ����=>]JK]����=>VJKV����=>1JK1����=>VJKV�����KKPKKS�����KKPKKQ�����������������@A_ABxBBSBBsxBBSFF_FFPII_NNS���������@BSFFSIISIIP���11P����������������������44\57\88\>?\BC\DD\DE\FG\HI\KL\NN\������������������45V57V88V>@VBDVDEVFIVKLVNNV��������������55057]88]>@]CC]DE]FI]�������������44]55]BC]DD]KK]NN]����44[DD[����44VDDV����441DD1����44VDDV�����DDPDD_�����DDPDDQ�����44V?@VGHV�����?@VGHV������??q�3??v�3GGv�3�����??\GH\�����??VGHV�����GGPGHS�����GGPGHQ��??\��?? ��??v�����45]CD]����45VCDV����451CD1����45VCDV�����CCPCDS�����CCPCDQ������55VBCVKLV�����BCVKLV����BC1KK1����BCVKKV�����KKPKK]�����KKPKKQ��KL:��KLV��KL1��KLV�����LLPLL]�����LLPLLQ���������������������55_66P66_67P77_88_>@_CC_DE_FI_������66V67VCCV�����67VCCV����67v�3CCv�3�������77QCCQCC�����77VCCV�����CCTCC���CCP����77Q77��77 ��77v�����������66V88V>?VDEVFGV����66,>?,����66V>?V����661>?1����66V>?V�����>>P>?�����>>P>?Q����66VDEV����661DE1����66VDEV�����EEREE���EEP��EE ��EEV��EE1��EEV�����EEREE���EEP����88VFGV����88VFGV����881FG1����88VFGV�����FFRFG���FGP��GG:��GGV��GG1��GGV�����GGRGG���GGP�����77V??VHIV����??VHIV����??1HH1����??VHHV�����HHPHHS�����HHPHHQ��HI:��HIV��HI1��HIV�����HHPHIS�����HHPHIQ����++/11/����++V11V�����11T11\���11P����)+^34^���������))P)*~�~�~� $0*(� h**P**~�~�~� $0*(� h 5**P**'~�~�~� $0*(� h 5 5**P**/~�~�~� $0*(� h 5 5 5��������������)*R**~�~�~� $0*(� hL%**R**uP**R**uP**R������������������))0)*T**~�~�~� $0*(� hL%0.**T**&uP~�~�~� $0*(� hL%0.!0.**D~�~�~� $0*(� hL%0.~�~�~� $0*(� h 5K%!0.**T** t�r�!0.*+z~�~�~� $0*(� hL%0.~�~�~� $0*(� h 5K%!0.~�~�~� $0*(� h 5 5J%!0.r�!0.340����))6346����))V34V���44S���44P����++F33F����++V33V���33S���33P���,, ���������,,F���,,v��������������������PQUQR_RRUR[_[[U[h_hhUhhUhh_�������������������������������������PQTQQSQRTRWSWXTXZSZZTZ\S\\T\`S``T`eSeeTeeSefTfhShhThhT�����������������������������������������������RRPRS]SSPST]UUr�3$}�"UVPVVr�3$}�"VVPVW]WWPWW]WWPWW]XXPYY^Z[^[[^\]]``^`a]cd]eePeg]����QQ{[\{��������QQUQQ_[[U[\_����QQ1[\1������QQU[[U[\_�����[\Q\\V���\\P������������������������TTPTVVXYVYZvZZVZZ0ZZ Z[V]`V`` acVdeVghV������������UU0UUTUV^VVTXX^]^^����������������UU1UUzUVzXXz]`zabzbb1ghz������������������UU{UV]X[]]`]ab]bb{bc]de]gh]��QQ_���bbP������XX_ab_gh_�����ab_gh_����aa1gg1����aa_gg_�����ggPgg^�����ggPggQ��gh:��gh_��gh1��gh_�����ghPhh^�����hhPhhQ�����YY_ZZ_]]_�����ZZ_]]_����ZZ�3]]�3�����ZZ^]]^�����ZZ_]]_�����]]R]]z���]]P��ZZ^��ZZ ��ZZ���������YZ_[[_bc_de_����YY,[[,����YY_[[_����YY1[[1����YY_[[_�����[[Q[[^���[[P����YZ_bc_����YY1bb1����YY_bb_���bb^���bbP��bc ��bc_��bc1��bc_���cc^���ccP����[[_de_����[[_de_����[[1dd1����[[_dd_�����ddQdd^���ddP��de:��de_��de1��de_�����ddQde^���deP����ZZ_``_�����ZZ_``_����ZZ1``1����ZZ_``_�����``P``S�����``P``Q��``:��``_��``1��``_�����``P``S�����``P``Q����QQ}\\}����QQ_\\_����QQ1\\1����QQ_\\_���\\S���\\P������RR_WW_fg_�����WW_fg_����WW1ff1����WW_ff_�����ffQffV���ffP��fg:��fg_��fg1��fg_�����ggQggV���ggP�����RR_ST_\]_�����TT_\]_����TT�3\]�3�����TTV\]V�����TT_\]_�������\]U]]T]]^���]]P��TTV��TT ��TT���������SS_VW_`a_cd_����SS,WW,����SS_WW_����SS1WW1����SS_WW_�����WWQWWV���WWP����SS_`a_����SS1`a1����SS_`a_�����aaQaaV���aaP��aa ��aa_��aa1��aa_�����aaQaaV���aaP����VW_cd_����VV_cd_����VV1cc1����VV_cc_�����ccQccV���ccP��cd:��cd_��cd1��cd_�����cdQddV���ddP������VV_WW_ef_�����WW_ef_����WW1ef1����WW_ef_���efS���efP��ff:��ff_��ff1��ff_���ffS���ffP����XX_ee_�����XX_ee_������XXp�3XX�3ee�3�����XXSeeS�����XX_ee_�������eeUeeTeeV���eeP��XXS��XX ��XX����������������hhUhjVjkUklVllUllVlmU�����������hhThiSilTllSlmT������jj~ll~lm~�����ll~lm~����ll1ll1����ll~ll~�����llQllV���llP��lm:��lm~��lm1��lm~�����mmQmmV���mmP��kk~��kkT�������������������������������mnUnpVppUppVpqUqsVsuUuuVuvUvvVvvUvwVwwUwwVwwU��������������nn0np_pr_rw_wwPww_ww_���������������nn0ns~ss1st~tu1uu~uv1vv~vv1vw~ww1���������������������������������������������������������������nn^nn]nn^nn]no^oo~oo}op]pp^pp]pp~pq^qq]qq^qq]qq^qq]qq^qq]qq^qq]qq^qq]qq^qr]rr^rr]rs^ss~�p�"ss~�p�"#ss~�p�"ssp�1$~�"ss~�p�"st^tt~ttu�#ttu�tv^vv]vv^vw~ww^ww^��������������������������������������������������nn\nnSnnsnnSopSpqSqqsqqSqqsqqSqqsqqSqqsqqSqqsqqSqqsqqSqqsqrSrrsrrSrsSsss�p�"sss�p�"#sss�p�"stStus|uvSvvs}vvSwwSwwSwws~���������������������������������������nnPnnPooPop~�pp~�qq~�qq~�qq~�qq~�qq~�qq~�qrPrsPss~�uu~�vvPvv~�vvPvv~�����rs~uu~����rs]uu]����rs^uu^���uu~���uu]���uu^������ttPwwPwwr���������������������������pqPstPtuZuuPuvZvvPwwZwwPwwZwwPwwZwwPwwZ����������tuZuuPuvZvvPwwPwwZ������������ttStus|uuSuvsvvs~vvSwwSwwswwS�tuZ� ���ttSttstts}�������noSrrSvwS�������noUrrUrr�#��ooS��oo\����oou�"ooU���������������������������������������������������������������������������������������������������������������������������������������xxUxyUyyUyyUy{U{{\{{U{|U|}\}}U}}U}}\}}U}؁\؁UՃ\ՃU\ԆUԆ\U\U\Uޑ\ޑU\U\U\U\U\U\U\ӛUӛݛ\ݛU\U\Uá\áסUס\U\U\U\U\Uˤ\ˤҤUҤ\U\Uժ\ժU��U����������������������������zz0z{1Ճ000001ԝ100ã00ߤ0��V��������z{VVvUԝv����z{00ԝ1���������������������������QP]QP]]]]]ڟ}�v�-)Ģ}�v�-)]ߤ]�������PPߤP��������џVããVãǣvǣ֣U֣v����0ãã0ã1�����������������������������������}}U}\\\\U\\\ĜԜ\ݞ\á\ס\\\Uժ\ժU���������������P__P_P_���������������������������~~P~VVVϒVVVVĜԜVݞVáVסVV̥V����������������������������~~P~VVVϒVVVVĜԜVݞVáVסVV̥V������~~|�ݍ|�|������������������PȀ_ҌPĜԜ_סۡPۡ__�������~~_~rQqQ���������������PȀ^^^ĜԜ^ס^^������|�х|�|�������\݅\͚\������|�݅|�͚ݚ|������Ȁ^ĜԜ^�����ȀPĜΜP�������ĀQĀȀ~ĜΜQ�����^^�����PP��������Q~qQ����ݍ_ݞ_��q��������\\ݞ\������|�|�|�������ʌ\ݍ\\������ʌ|�э|�|�������π߀|�|�|�������\\ݙ\������|�|�ݙ|������ʁVԝV�����ʁPԝޝP�������ǁQǁʁvԝޝQ�����������������������]}P}V̥]V\ĩ]ĩɩPժV���������P\\ժ\���������VǥVǥ̥PɩV�������ʦݦPݦɩժ���������P]]ժ]�������������������էVէߧvߧV]ɩɩ]ɩө}xө]VP]ժV���������PڨVɩϩVV�������^ɩЪ^ЪժP�èͨ_��������������������UՃ\\\\\ޑ\ޑU\\��������������������PՃVVVVVܑVܑTVV���������������������PՃVVVVVܑVܑTVV������|�|�ʠ|��������������ӂPՃ^^^^^������\\\������|�|�|�������\\\������|�ل|�ȏ|�������������������������������������ԆUԆ\UՎ\\\U\Ĝ\Ԝ\\ݞ\\\ã\\UU����������������������������������0PVՎ0VV0ĜVԜۜPۜVVݞV00ãVVV������������������������������������������݇0݇P_Վ00Ӑ_ؐ000Ĝ0Ԝ0P_ݞ_T_000ã___��������ɆԆUԆ\Վ\\������Ԇ|�Ɏ|�|���������|�|�ؐ|�|�������ˇ\Ĝ\\������ˇ|�|�|���������^ã^^^����0]}���������������}2p#ã}}}���������PP��ƔД_�����������ֈPP�������������������������\|Pڊ }�3$|�"#ڊ }�3$|�"# p�3$|�"# }3$|�"#\V\P\�������������QQP�����̋ۋVV�����̋ۋPP������̋؋Q؋ۋvQ�����__�����PP������QQ���Ɣ_��Ɣ_��������V0V0�����PP�������QvQ���������Ӑ_0ݞ_T�����ؐPݞP���������̐Q̐ААؐqݞQ�������������ūUūӮVܮUVUV���������ūTūҮSҮܮTܮS���������������������Q\ܮQܮ\ΰQΰ\Q\Q\���������������������������P^Pծ\ծܮPPΰ\Pͱ^^ٴ^\^�������ѫT֯T˴شT������~~˴~������ҭ~~~������~~ٴ~�����ͱ^^�����ͱPP������ɱQɱͱ~Q�������Pó_ߵ_�������ӳ^^ߵ^�������ӳVVߵV�����^ߵ^�����^ߵ^�����PߵP�����U̺U���������T^T̺^���������������\^~^P^̺\�������������öVöv]]ƹv̺v�����öǶv� $ &3$p"Ƕζv� $ &3$p�"��öǶ|�v� $ &3$p8���P�������¸]ƹ]]�������ܷ߷P߷VƹV���P�����кUU���������кTݼ]ݼT]���������������»\»ƻ|xƻ\VvټVVV\�����������V»v»߼^^v�����v� $ &3$p"v� $ &3$p�"��|�v� $ &3$p8���P���������ܻVPۼ\\�ܻܻ�����̽U̽U���������нTн]T]���������������ֽ\|x\VvVտV׿ܿVܿ\�����������Vv^ܿ^ܿv�����v� $ &3$p"v� $ &3$p�"��|�v� $ &3$p8���۽P���������VP\ܿ\������UU���������T_T_�����������VvxVPV�����������^~]]~�������~� $ &3$p"~� $ &3$p�"P�����\\���P�������___�����T���������XX�����UU���������T^T^��������������VvvxVp�VPV�������������]}\\\}�����}� $ &3$p"}� $ &3$p�"��v�}� $ &3$p8���P�����\\�����UU���������TVTV���������������\VvxVPV\���������������������^R~~~~R���������]]]]�����������__� __���P��������Pv�Tv����������RR���P�����UU���������T_T_���������������]\|\P\]�����������\|VV|�����|� $ &3$p"|� $ &3$p�"��}�|� $ &3$p8�����^^���P�����������VV�����UU���������T_T_�����������VvxVPV�����������^~\\~�������~� $ &3$p"~� $ &3$p�"P���������]]]]���P�������___�������]]]�����^^�����PP�������Q~Q�������^^^�����UU���������T_T_�����������VvxVPV�����������]}\\}�������}� $ &3$p"}� $ &3$p�"P���������^^^^���P�������___�����P�����]]������]]]�������]]]�������PPP�������Q}Q���P�����UU���������T^T^�����������������\VvVp�\VP\���������������Vv]VVVv���������v� $ &3$p"v� $ &3$p�"QQ��|�v� $ &3$p8���P���V�������������^P\^\^�����UU���������T^T^�����������������\VvVp�\VP\���������������Vv]VVVv���������v� $ &3$p"v� $ &3$p�"QQ��|�v� $ &3$p8���P���V�������������^P\^\^�����UU���������T^T^�����������������]Vvvxv]V]]�������������Vv\\\v���������v� $ &3$p"v� $ &3$p�"QQ��}�v� $ &3$p8���P���V�������^^^�����������P\v\\�����\\���\���\�����PP�����UU���������T]T]���\�������������^~V~V~�����~� $ &3$p"~� $ &3$p�"��|�~� $ &3$p8���P������\P\������PPP�\��1�����UU���������T]T]���\�������������^~V~V~�����~� $ &3$p"~� $ &3$p�"��|�~� $ &3$p8���P�����VV�����]]�����]]�����PP�������Q}Q��0�����UU���������T]T]���\�������������^~V~V~�����~� $ &3$p"~� $ &3$p�"��|�~� $ &3$p8���P�����VV�����]]�����]]�����PP�������Q}Q�����]]�����]]�����PP�������Q}Q�����]]�����]]�����PP�������Q}Q�����]]�����]]�����PP�������Q}Q�����VV�����VV�����PP�������QvQ��0�����UU���������T_T_�����������VvxVPV�����������^~\\~�������~� $ &3$p"~� $ &3$p�"P���������]]]]���P�����__���P�����UU���������T_T_�����������VvxVPV�����������^~\\~�������~� $ &3$p"~� $ &3$p�"P���������]]]]���P�����__�����UDŽU�������������T]ƒTƒ]TDŽ]������ހV^^�������������΀Ӏ^Ӏ~҂\ƒ\\DŽ~�����Ӏ׀~� $ &3$p"׀ۀ~� $ &3$p�"��Ӏ׀v�~� $ &3$p8���΀P�����҂\\�����VV�����‚ p}�"##‚Ƃ p�}�"##���� |�  |� ��ƒ1�����ЄUU�������������ЄT]T]T]������Vڇ^Ј^�������������^~\Ј\\~�����~� $ &3$p"~� $ &3$p�"��v�~� $ &3$p8���P�����\Ј\�����օVV����� p}�"## p�}�"##����\Ј\��އ1�����UU���������TĊ]ĊT]���؉\�������������ȉ͉^͉~V~V~�����͉щ~� $ &3$p"щՉ~� $ &3$p�"��͉щ|�~� $ &3$p8���ȉP�����VV��0�����UU�����������Tێ]ێОTОߞ]ߞT�������������\ו_ו\\_\�����������������ԍٍ_ٍSʏʏڏSžОSОߞ�������ٍ� $ &3$p"� $ &3$p�"P�������ޏ^^žߞ^���ԍP���������SSžSߞS���������������ڏ]]Ԙ]ؚ]Ǜ]]]�����]]�����TT��ő��Ƒ]������ut�"TőU���P�����q�3 p#"3���P�������Pq(ؚP�������P��������������֒SSԘSSǛSSS���������������������������������������������������TRTRʓTʓ[RTR[ܔTTԘTPTPT[Ǜ[T[TT[T���P�����q�3 p#"3���P�����P̖q(���������QRƘQƘȘqǛR����������TȘPPQǛP�����UįU���������������������������������������������������������������������TTPPPΡӡPPPPڢߢPPPãȣPPPP֤ۤPPPĥPPPP˦ЦPPPPקܧPPPǨ̨PP]Q���������̟ПPПVTįV���P���P���P���ӡޡP���P���P���ǢP���ߢP���P���P���ȣӣP���P���P���P���ۤP���P���P���ĥϥP���P���P���P���ЦۦP���P���P���ħP���ܧP���P���P���̨רP������������0p1p0p1p0p1p0p1p0įp1�� M���������P��̮ w���������ЮP���P���������__bp�x~����� �   �  �   �   �  �  �������  � � � � ��� !� !� ��� � �!"�!!�!"� "#� ""� "#�&'((((./�&&&&./�&&./�''..�''..�((((((,./11333334>>DDOOOOP�((((((/055788>>>@BDDDDEFIKLNNOOO�8>EFIKLMNOOO�99MM�;;LM�;;LM�;;LL�;;LL�<<II�<<II�<<JJ�<<JJ�<=IJ�<<IJ�==FF�====FF�==FF�=>JK�=>JK�>>>>@BDDFFIINN�@AII�13PP�455788>@BDDEFIKLNN�44DD�44DD�44???@GH�44???@GH�44??????GH�45CD�45CD�55BCKL�BCKL�BCKK�CCKL�5788>?CCDEFG�6667CC�67CC�67CC�7777CC�6688>?DEFG�66>?�66>?�66DE�66DE�66EE�88FG�88FG�88FG�88GG�77????HI�????HI�????HH�????HI�))++11�)+34�))34�++33�QQQQ[\�QQQQ[\�QQTVVVX[]aacdegh�QQUVVVX[]aacdegh�UUUUbb�XXabbbgh�abbbgh�aagg�abbbgh�YYZZZZ[[]]bb�ZZ]]�ZZZZZZ]]�YYYZ[[[[bcde�YY[[�YY[[�YYYZbc�YYbb�YYYZbc�[[[[de�[[de�[[dd�[[de�ZZ`a�ZZ`a�ZZZZ``�ZZ`a�QQ\\�QQ\\�RRWWWWfg�WWWWfg�WWff�WWWWfg�RRSSSTVVWW\]�TT\]�TTTTTT\]�SSVVVWWWaacd�SSWWWW�SSWWWW�SSaa�SSaa�SSaa�VVVWcd�VVcd�VVcc�VVcd�VVWWef�WWef�WWef�WWff�WWXXee�WWXXee�WWXXXXee�jjlllm�lllm�llll�lllm�nnnnnooppvwwww�nnnnnnnnopprrvwwww�nnnnrsuu�nnnnrsssuu�nnnnuu�pqsuuvvvwwww�tttuuuuvvvwwww�tttu�norr�norr�oooo�y{{{؃ݛԝáסߢãߤ�z{ԝ�؃ߢãߤ�ßߤ�џã�}}}؁؎ĜԜԝݞáס�}}}~~~�~~�~~~~�~~�~~~~~ȀĜԜݞס�~~ȀĜԜס�~~͚�~~͚�~~͚�͚�͚�ȀĜԜ�ܡ�ݞ�ݞ�ݞ�ʌ�ʌ�Ȁ߀�Ȁππ߀���ʁԝ�ʁԝ�ے̥ɩժ�“̥�̥ɩժ�צɩժ�ժ�؁߁؃ӑבڑ�؁߁���Р�ȂȂŃ̓Ѓ؃�Ȃ�Ȃ�Ȃ���؎ĜԜݞã�ƆɆ؎�ƆɆԆԆ؎�͆Ԇ����ˇĜ�ˇĜ��ã�Չ�Ƌɋ̋ۋ�����АӐݞ�АӐݞ�ū߫˴�˴�ҭҭ˴�ɭ˴����ͱ�ͱ�ӳߵ�ߵ�ߵ��޶�й�˷ٷ��»»»ƻü�λԻܻü�лԻܻܻܻ�۽۽ݽ�۾޾׿�۾޾׿�ʾ�������������������������������������������������΀�ȃ�ׂق��އ�ڇЈ�ȉ��ƍɍԍ�О��Ƒ���’’ؘǛȝž�’֒ԔؘؔǛ�ȘǛ����ΡΡСӡ����ڢڢܢߢ���ããţȣ����֤֤ؤۤ���ĥ����˦˦ͦЦ����קק٧ܧ���ǨǨɨ̨�į�ۭݭ�Ů̮���������������������������������������������������������������������������������������������������������������������( ����������������������������������������p��������������������������������������� �������������������� �� ������������������� � ������������������� ��%������������������� �)��������������������T�����������������������������������������0����������������������������������������8��������������������@��������������������H��������������������P��������������������@�������������������������������������������������������������������������������������P!�������������������������������������������������������������������������������������������������������������������������������� ����������������������!����������������������"������������������������������������� ���� �)������������������ ��*��������������!���� �@*��������������7�������������������C�����@��������������j���� �*��������������v�����8��������������������������������������� �*������6����������� �*������������������ ������������������H�����������������0������������������(����������������� �`+����������������� �+������ ���������� �.������{�����������@���������������� �.�������������"��� �P/������V�������.��� �/������������9��� �8������e������C��� � <������K�������O��� �p<������������[��� �=������f������e��� �R������ ������o����@��������������� �^������x������w��� �Pa������=��������� �f������c��������� ����������������� �������L��������� �������C��������� �0������K��������� �������o��������� ���������������� ���������������� �P������������.��� �0������u������M��� �������\������w��� ���������������� ���������������� �М��������������� ���������������� ���������������� �`������}��������� �������U��������� �@������U������#��� �������7������=��� �������7������V��� � ������������l��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������T��������������������������������������H������������������P�������������������0�������������������������������������@����������������� �� ��������������������������������������������������������#���������������������5���������������������F���������������������X���������������������h�� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+���������������������;���������������������L���������������������W���������������������d���������������������r������������������������������������������������������������������������������������������������������������������������������������������������������ �������D��������������������������� ��������������������� �� �������������������/���������������������C���������������������W���������������������e���������������������r������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������������������������������������������1���������������������E���������������������U���������������������f���������������������w���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-���������������������1���������������������@���������������������T���������������������k���������������������v����������������������������������������������������������������������������������������������������������������������������������������������������� ���������������������������������������� ��������������������� ���������������������' ���������������������5 ��"�������������������P ���������������������m ��������������������� ��������������������� ��������������������� ��������������������� ����������������������crtstuff.c�deregister_tm_clones�__do_global_dtors_aux�completed.0�__do_global_dtors_aux_fini_array_entry�frame_dummy�__frame_dummy_init_array_entry�get_bool�XS_JSON__XS_CLONE�json_stash�bool_stash�bool_false�bool_true�json_sv_grow�json_atof_scan1�decode_4hex�decode_hexdigit�ref_bool_type�json_nonref�encode_str�encode_hk�he_cmp_fast�he_cmp_slow�encode_sv�encode_hv�sv_json�decode_str�decode_sv�XS_JSON__XS_new�XS_JSON__XS_encode_json�XS_JSON__XS_decode_json�XS_JSON__XS_boolean_values�XS_JSON__XS_get_boolean_values�XS_JSON__XS_ascii�XS_JSON__XS_get_ascii�XS_JSON__XS_filter_json_object�XS_JSON__XS_filter_json_single_key_object�XS_JSON__XS_encode�XS_JSON__XS_decode�XS_JSON__XS_decode_prefix�XS_JSON__XS_incr_text�XS_JSON__XS_incr_reset�XS_JSON__XS_DESTROY�XS_JSON__XS_max_depth�XS_JSON__XS_max_size�XS_JSON__XS_get_max_depth�XS_JSON__XS_get_max_size�XS_JSON__XS_incr_skip�XS_JSON__XS_incr_parse�__FRAME_END__�_fini�XS.c.c0557bc0�__dso_handle�_DYNAMIC�__GNU_EH_FRAME_HDR�__TMC_END__�_GLOBAL_OFFSET_TABLE_�_init�Perl_sv_2iv_flags�__snprintf_chk@GLIBC_2.3.4�Perl_hv_iterkeysv�Perl_newRV_noinc�Perl_sv_2uv_flags�Perl_stack_grow�_ITM_deregisterTMCloneTable�qsort@GLIBC_2.2.5�Perl_sv_derived_from�pthread_getspecific@GLIBC_2.34�Perl_av_len�Perl_pop_scope�Perl_newSV�strlen@GLIBC_2.2.5�__stack_chk_fail@GLIBC_2.4�PL_WARN_NONE�Perl_sv_upgrade�Perl_sv_setiv_mg�PL_thr_key�Perl_newSVnv�Perl_sv_bless�memset@GLIBC_2.2.5�Perl_sv_chop�Perl_sv_2pv_flags�Perl_xs_boot_epilog�Perl_hv_iternext_flags�Perl_grok_number�Perl_get_cv�boot_JSON__XS�memcmp@GLIBC_2.2.5�strcmp@GLIBC_2.2.5�__gmon_start__�Perl_croak_xs_usage�Perl_newSVpvn_flags�Perl_savetmps�Perl_sv_grow�Perl_sv_utf8_upgrade_flags_grow�memcpy@GLIBC_2.14�Perl_gv_stashpv�Perl_av_push�Perl_sv_cmp_flags�Perl_newSVpv�Perl_gv_fetchmethod_autoload�Perl_get_sv�Perl_croak_nocontext�Perl_newXS_deffile�Perl_pv_uni_display�Perl_gv_stashsv�Perl_hv_iterinit�Perl_newXS_flags�Perl_sv_2mortal�Perl_mg_get�Perl_hv_common�Perl_newSVuv�Perl_safesysrealloc�memmove@GLIBC_2.2.5�Perl_xs_handshake�gcvt@GLIBC_2.2.5�Perl_av_fetch�Perl_utf8n_to_uvuni�Perl_utf8_length�pow�Perl_free_tmps�Perl_markstack_grow�Perl_hv_common_key_len�Perl_newRV�Perl_newSV_type�PL_hexdigit�Perl_save_vptr�Perl_call_sv�Perl_sv_setuv_mg�Perl_sv_free2�Perl_push_scope�_ITM_registerTMCloneTable�Perl_newSViv�Perl_hv_iterval�Perl_newSVsv_flags�Perl_newSVpvn�__cxa_finalize@GLIBC_2.2.5�Perl_sv_utf8_downgrade_flags�Perl_sv_newmortal�Perl_apply_attrs_string�PL_utf8skip�__sprintf_chk@GLIBC_2.3.4�Perl_hv_placeholders_get��.symtab�.strtab�.shstrtab�.note.gnu.property�.note.gnu.build-id�.gnu.hash�.dynsym�.dynstr�.gnu.version�.gnu.version_r�.rela.dyn�.rela.plt�.init�.plt.sec�.text�.fini�.rodata�.eh_frame_hdr�.eh_frame�.init_array�.fini_array�.data.rel.ro�.dynamic�.got�.bss�.comment�.annobin.notes�.gnu.build.attributes�.debug_aranges�.debug_info�.debug_abbrev�.debug_line�.debug_str�.debug_line_str�.debug_loclists�.debug_rnglists������������������������������������������������������������������������������������������������ ������������������������������.�������������������������$������������������������������A���o�������������������$�����������������������������K��� ������������������������������������������������S�������������( ������( �����������������������������������[���o�����������������������������������������������h���o�������p������p������`����������������������������w����������������������������������������������������������B�������������������8���������������������������������������� ������� ������������������������������������������������� ������ ������������������������������������������������%�������%�����������������������������������������������)������)������������������������������������������������T������T������ ���������������������������������������������������������0������������������������������������������0������0������T������������������������������������������������������x ������������������������������������������8������8������������������������������������������������@������@������������������������������������������������H������H�������������������������������������������������P������P����������������������������������������������@������@�������������������������������������������������������������P�������������� ���������������������0����������������������.����������������������������������0���������������.������������������������������������������������P!����������� �����������������������������,��������������������������0������������������������������;��������������������������V?����������������������������G���������������������ZA����������������������������������U���������������������6H�����_x�����������������������������a�����0�������������������� @����������������������������l�����0�������������������������������������������������|���������������������p�����;����������������������������������������������������������������������������������������������������������@����� +������$���w���������������� ����������������������`����� ���������������������������������������������������6��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������auto/Types/Serialiser/.packlist���������������������������������������������������������������������0000644�����������������00000000315�15130473315�0012536 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/usr/local/share/man/man3/Types::Serialiser.3pm /usr/local/share/man/man3/Types::Serialiser::Error.3pm /usr/local/share/perl5/5.32/Types/Serialiser.pm /usr/local/share/perl5/5.32/Types/Serialiser/Error.pm �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������common/sense.pm�������������������������������������������������������������������������������������0000444�����������������00000001005�15130473315�0007503 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package common::sense; our $VERSION = 3.75; # overload should be included sub import { local $^W; # work around perl 5.16 spewing out warnings for next statement # use warnings ${^WARNING_BITS} ^= ${^WARNING_BITS} ^ "\x0c\x3f\x33\x00\x03\xf0\x0f\xc0\xf0\xfc\x33\x00\x00\x00\x0c\x00\x00\x00\x00"; # use strict, use utf8; use feature; $^H |= 0x1c820fc0; @^H{qw(feature___SUB__ feature_evalbytes feature_fc feature_indirect feature_say feature_state feature_switch feature_unicode)} = (1) x 8; } 1 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������common/sense.pod������������������������������������������������������������������������������������0000444�����������������00000037071�15130473315�0007665 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=head1 NAME common::sense - save a tree AND a kitten, use common::sense! =head1 SYNOPSIS use common::sense; # Supposed to be mostly the same, with much lower memory usage, as: # use utf8; # use strict qw(vars subs); # use feature qw(say state switch); # use feature qw(unicode_strings unicode_eval current_sub fc evalbytes); # no feature qw(array_base); # no warnings; # use warnings qw(FATAL closed threads internal debugging pack # prototype inplace io pipe unpack malloc glob # digit printf layer reserved taint closure semicolon); # no warnings qw(exec newline unopened); =head1 DESCRIPTION “Nothing is more fairly distributed than common sense: no one thinks he needs more of it than he already has.” – René Descartes This module implements some sane defaults for Perl programs, as defined by two typical (or not so typical - use your common sense) specimens of Perl coders. In fact, after working out details on which warnings and strict modes to enable and make fatal, we found that we (and our code written so far, and others) fully agree on every option, even though we never used warnings before, so it seems this module indeed reflects a "common" sense among some long-time Perl coders. The basic philosophy behind the choices made in common::sense can be summarised as: "enforcing strict policies to catch as many bugs as possible, while at the same time, not limiting the expressive power available to the programmer". Two typical examples of how this philosophy is applied in practise is the handling of uninitialised and malloc warnings: =over 4 =item I<uninitialised> C<undef> is a well-defined feature of perl, and enabling warnings for using it rarely catches any bugs, but considerably limits you in what you can do, so uninitialised warnings are disabled. =item I<malloc> Freeing something twice on the C level is a serious bug, usually causing memory corruption. It often leads to side effects much later in the program and there are no advantages to not reporting this, so malloc warnings are fatal by default. =back Unfortunately, there is no fine-grained warning control in perl, so often whole groups of useful warnings had to be excluded because of a single useless warning (for example, perl puts an arbitrary limit on the length of text you can match with some regexes before emitting a warning, making the whole C<regexp> category useless). What follows is a more thorough discussion of what this module does, and why it does it, and what the advantages (and disadvantages) of this approach are. =head1 RATIONALE =over 4 =item use utf8 While it's not common sense to write your programs in UTF-8, it's quickly becoming the most common encoding, is the designated future default encoding for perl sources, and the most convenient encoding available (you can do really nice quoting tricks...). Experience has shown that our programs were either all pure ascii or utf-8, both of which will stay the same. There are few drawbacks to enabling UTF-8 source code by default (mainly some speed hits due to bugs in older versions of perl), so this module enables UTF-8 source code encoding by default. =item use strict qw(subs vars) Using C<use strict> is definitely common sense, but C<use strict 'refs'> definitely overshoots its usefulness. After almost two decades of Perl hacking, we decided that it does more harm than being useful. Specifically, constructs like these: @{ $var->[0] } Must be written like this (or similarly), when C<use strict 'refs'> is in scope, and C<$var> can legally be C<undef>: @{ $var->[0] || [] } This is annoying, and doesn't shield against obvious mistakes such as using C<"">, so one would even have to write (at least for the time being): @{ defined $var->[0] ? $var->[0] : [] } ... which nobody with a bit of common sense would consider writing: clear code is clearly something else. Curiously enough, sometimes perl is not so strict, as this works even with C<use strict> in scope: for (@{ $var->[0] }) { ... If that isn't hypocrisy! And all that from a mere program! =item use feature qw(say state given ...) We found it annoying that we always have to enable extra features. If something breaks because it didn't anticipate future changes, so be it. 5.10 broke almost all our XS modules and nobody cared either (or at least I know of nobody who really complained about gratuitous changes - as opposed to bugs). Few modules that are not actively maintained work with newer versions of Perl, regardless of use feature or not, so a new major perl release means changes to many modules - new keywords are just the tip of the iceberg. If your code isn't alive, it's dead, Jim - be an active maintainer. But nobody forces you to use those extra features in modules meant for older versions of perl - common::sense of course works there as well. There is also an important other mode where having additional features by default is useful: commandline hacks and internal use scripts: See "much reduced typing", below. There is one notable exception: C<unicode_eval> is not enabled by default. In our opinion, C<use feature> had one main effect - newer perl versions don't value backwards compatibility and the ability to write modules for multiple perl versions much, after all, you can use feature. C<unicode_eval> doesn't add a new feature, it breaks an existing function. =item no warnings, but a lot of new errors Ah, the dreaded warnings. Even worse, the horribly dreaded C<-w> switch: Even though we don't care if other people use warnings (and certainly there are useful ones), a lot of warnings simply go against the spirit of Perl. Most prominently, the warnings related to C<undef>. There is nothing wrong with C<undef>: it has well-defined semantics, it is useful, and spitting out warnings you never asked for is just evil. The result was that every one of our modules did C<no warnings> in the past, to avoid somebody accidentally using and forcing his bad standards on our code. Of course, this switched off all warnings, even the useful ones. Not a good situation. Really, the C<-w> switch should only enable warnings for the main program only. Funnily enough, L<perllexwarn> explicitly mentions C<-w> (and not in a favourable way, calling it outright "wrong"), but standard utilities, such as L<prove>, or MakeMaker when running C<make test>, still enable them blindly. For version 2 of common::sense, we finally sat down a few hours and went through I<every single warning message>, identifying - according to common sense - all the useful ones. This resulted in the rather impressive list in the SYNOPSIS. When we weren't sure, we didn't include the warning, so the list might grow in the future (we might have made a mistake, too, so the list might shrink as well). Note the presence of C<FATAL> in the list: we do not think that the conditions caught by these warnings are worthy of a warning, we I<insist> that they are worthy of I<stopping> your program, I<instantly>. They are I<bugs>! Therefore we consider C<common::sense> to be much stricter than C<use warnings>, which is good if you are into strict things (we are not, actually, but these things tend to be subjective). After deciding on the list, we ran the module against all of our code that uses C<common::sense> (that is almost all of our code), and found only one occurrence where one of them caused a problem: one of elmex's (unreleased) modules contained: $fmt =~ s/([^\s\[]*)\[( [^\]]* )\]/\x0$1\x1$2\x0/xgo; We quickly agreed that indeed the code should be changed, even though it happened to do the right thing when the warning was switched off. =item much reduced typing Especially with version 2.0 of common::sense, the amount of boilerplate code you need to add to get I<this> policy is daunting. Nobody would write this out in throwaway scripts, commandline hacks or in quick internal-use scripts. By using common::sense you get a defined set of policies (ours, but maybe yours, too, if you accept them), and they are easy to apply to your scripts: typing C<use common::sense;> is even shorter than C<use warnings; use strict; use feature ...>. And you can immediately use the features of your installed perl, which is more difficult in code you release, but not usually an issue for internal-use code (downgrades of your production perl should be rare, right?). =item mucho reduced memory usage Just using all those pragmas mentioned in the SYNOPSIS together wastes <blink>I<< B<776> kilobytes >></blink> of precious memory in my perl, for I<every single perl process using our code>, which on our machines, is a lot. In comparison, this module only uses I<< B<four> >> kilobytes (I even had to write it out so it looks like more) of memory on the same platform. The money/time/effort/electricity invested in these gigabytes (probably petabytes globally!) of wasted memory could easily save 42 trees, and a kitten! Unfortunately, until everybody applies more common sense, there will still often be modules that pull in the monster pragmas. But one can hope... =back =head1 THERE IS NO 'no common::sense'!!!! !!!! !! This module doesn't offer an unimport. First of all, it wastes even more memory, second, and more importantly, who with even a bit of common sense would want no common sense? =head1 STABILITY AND FUTURE VERSIONS Future versions might change just about everything in this module. We might test our modules and upload new ones working with newer versions of this module, and leave you standing in the rain because we didn't tell you. In fact, we did so when switching from 1.0 to 2.0, which enabled gobs of warnings, and made them FATAL on top. Maybe we will load some nifty modules that try to emulate C<say> or so with perls older than 5.10 (this module, of course, should work with older perl versions - supporting 5.8 for example is just common sense at this time. Maybe not in the future, but of course you can trust our common sense to be consistent with, uhm, our opinion). =head1 WHAT OTHER PEOPLE HAD TO SAY ABOUT THIS MODULE apeiron "... wow" "I hope common::sense is a joke." crab "i wonder how it would be if joerg schilling wrote perl modules." Adam Kennedy "Very interesting, efficient, and potentially something I'd use all the time." [...] "So no common::sense for me, alas." H.Merijn Brand "Just one more reason to drop JSON::XS from my distribution list" Pista Palo "Something in short supply these days..." Steffen Schwigon "This module is quite for sure *not* just a repetition of all the other 'use strict, use warnings'-approaches, and it's also not the opposite. [...] And for its chosen middle-way it's also not the worst name ever. And everything is documented." BKB "[Deleted - thanks to Steffen Schwigon for pointing out this review was in error.]" Somni "the arrogance of the guy" "I swear he tacked somenoe else's name onto the module just so he could use the royal 'we' in the documentation" Anonymous Monk "You just gotta love this thing, its got META.json!!!" dngor "Heh. '"<elmex at ta-sa.org>"' The quotes are semantic distancing from that e-mail address." Jerad Pierce "Awful name (not a proper pragma), and the SYNOPSIS doesn't tell you anything either. Nor is it clear what features have to do with "common sense" or discipline." acme "THERE IS NO 'no common::sense'!!!! !!!! !!" apeiron (meta-comment about us commenting^Wquoting his comment) "How about quoting this: get a clue, you fucktarded amoeba." quanth "common sense is beautiful, json::xs is fast, Anyevent, EV are fast and furious. I love mlehmannware ;)" apeiron "... it's mlehmann's view of what common sense is. His view of common sense is certainly uncommon, insofar as anyone with a clue disagrees with him." apeiron (another meta-comment) "apeiron wonders if his little informant is here to steal more quotes" ew73 "... I never got past the SYNOPSIS before calling it shit." [...] How come no one ever quotes me. :(" chip (not willing to explain his cryptic questions about links in Changes files) "I'm willing to ask the question I've asked. I'm not willing to go through the whole dance you apparently have choreographed. Either answer the completely obvious question, or tell me to fuck off again." =head1 FREQUENTLY ASKED QUESTIONS Or frequently-come-up confusions. =over 4 =item Is this module meant to be serious? Yes, we would have put it under the C<Acme::> namespace otherwise. =item But the manpage is written in a funny/stupid/... way? This was meant to make it clear that our common sense is a subjective thing and other people can use their own notions, taking the steam out of anybody who might be offended (as some people are always offended no matter what you do). This was a failure. But we hope the manpage still is somewhat entertaining even though it explains boring rationale. =item Why do you impose your conventions on my code? For some reason people keep thinking that C<common::sense> imposes process-wide limits, even though the SYNOPSIS makes it clear that it works like other similar modules - i.e. only within the scope that C<use>s them. So, no, we don't - nobody is forced to use this module, and using a module that relies on common::sense does not impose anything on you. =item Why do you think only your notion of common::sense is valid? Well, we don't, and have clearly written this in the documentation to every single release. We were just faster than anybody else w.r.t. to grabbing the namespace. =item But everybody knows that you have to use strict and use warnings, why do you disable them? Well, we don't do this either - we selectively disagree with the usefulness of some warnings over others. This module is aimed at experienced Perl programmers, not people migrating from other languages who might be surprised about stuff such as C<undef>. On the other hand, this does not exclude the usefulness of this module for total newbies, due to its strictness in enforcing policy, while at the same time not limiting the expressive power of perl. This module is considerably I<more> strict than the canonical C<use strict; use warnings>, as it makes all its warnings fatal in nature, so you can not get away with as many things as with the canonical approach. This was not implemented in version 1.0 because of the daunting number of warning categories and the difficulty in getting exactly the set of warnings you wish (i.e. look at the SYNOPSIS in how complicated it is to get a specific set of warnings - it is not reasonable to put this into every module, the maintenance effort would be enormous). =item But many modules C<use strict> or C<use warnings>, so the memory savings do not apply? I suddenly feel sad... But yes, that's true. Fortunately C<common::sense> still uses only a miniscule amount of RAM. =item But it adds another dependency to your modules! It's a fact, yeah. But it's trivial to install, most popular modules have many more dependencies. And we consider dependencies a good thing - it leads to better APIs, more thought about interworking of modules and so on. =item Why do you use JSON and not YAML for your META.yml? This is not true - YAML supports a large subset of JSON, and this subset is what META.yml is written in, so it would be correct to say "the META.yml is written in a common subset of YAML and JSON". The META.yml follows the YAML, JSON and META.yml specifications, and is correctly parsed by CPAN, so if you have trouble with it, the problem is likely on your side. =item But! But! Yeah, we know. =back =head1 AUTHOR Marc Lehmann <schmorp@schmorp.de> http://home.schmorp.de/ Robin Redeker, "<elmex at ta-sa.org>". =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������JSON/XS.pm������������������������������������������������������������������������������������������0000444�����������������00000210056�15130473315�0006211 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=head1 NAME JSON::XS - JSON serialising/deserialising, done correctly and fast =encoding utf-8 JSON::XS - 正しくて高速な JSON シリアライザ/デシリアライザ (http://fleur.hio.jp/perldoc/mix/lib/JSON/XS.html) =head1 SYNOPSIS use JSON::XS; # exported functions, they croak on error # and expect/generate UTF-8 $utf8_encoded_json_text = encode_json $perl_hash_or_arrayref; $perl_hash_or_arrayref = decode_json $utf8_encoded_json_text; # OO-interface $coder = JSON::XS->new->ascii->pretty->allow_nonref; $pretty_printed_unencoded = $coder->encode ($perl_scalar); $perl_scalar = $coder->decode ($unicode_json_text); # Note that JSON version 2.0 and above will automatically use JSON::XS # if available, at virtually no speed overhead either, so you should # be able to just: use JSON; # and do the same things, except that you have a pure-perl fallback now. =head1 DESCRIPTION This module converts Perl data structures to JSON and vice versa. Its primary goal is to be I<correct> and its secondary goal is to be I<fast>. To reach the latter goal it was written in C. See MAPPING, below, on how JSON::XS maps perl values to JSON values and vice versa. =head2 FEATURES =over =item * correct Unicode handling This module knows how to handle Unicode, documents how and when it does so, and even documents what "correct" means. =item * round-trip integrity When you serialise a perl data structure using only data types supported by JSON and Perl, the deserialised data structure is identical on the Perl level. (e.g. the string "2.0" doesn't suddenly become "2" just because it looks like a number). There I<are> minor exceptions to this, read the MAPPING section below to learn about those. =item * strict checking of JSON correctness There is no guessing, no generating of illegal JSON texts by default, and only JSON is accepted as input by default (the latter is a security feature). =item * fast Compared to other JSON modules and other serialisers such as Storable, this module usually compares favourably in terms of speed, too. =item * simple to use This module has both a simple functional interface as well as an object oriented interface. =item * reasonably versatile output formats You can choose between the most compact guaranteed-single-line format possible (nice for simple line-based protocols), a pure-ASCII format (for when your transport is not 8-bit clean, still supports the whole Unicode range), or a pretty-printed format (for when you want to read that stuff). Or you can combine those features in whatever way you like. =back =cut package JSON::XS; use common::sense; our $VERSION = '4.03'; our @ISA = qw(Exporter); our @EXPORT = qw(encode_json decode_json); use Exporter; use XSLoader; use Types::Serialiser (); =head1 FUNCTIONAL INTERFACE The following convenience methods are provided by this module. They are exported by default: =over =item $json_text = encode_json $perl_scalar Converts the given Perl data structure to a UTF-8 encoded, binary string (that is, the string contains octets only). Croaks on error. This function call is functionally identical to: $json_text = JSON::XS->new->utf8->encode ($perl_scalar) Except being faster. =item $perl_scalar = decode_json $json_text The opposite of C<encode_json>: expects a UTF-8 (binary) string and tries to parse that as a UTF-8 encoded JSON text, returning the resulting reference. Croaks on error. This function call is functionally identical to: $perl_scalar = JSON::XS->new->utf8->decode ($json_text) Except being faster. =back =head1 A FEW NOTES ON UNICODE AND PERL Since this often leads to confusion, here are a few very clear words on how Unicode works in Perl, modulo bugs. =over =item 1. Perl strings can store characters with ordinal values > 255. This enables you to store Unicode characters as single characters in a Perl string - very natural. =item 2. Perl does I<not> associate an encoding with your strings. ... until you force it to, e.g. when matching it against a regex, or printing the scalar to a file, in which case Perl either interprets your string as locale-encoded text, octets/binary, or as Unicode, depending on various settings. In no case is an encoding stored together with your data, it is I<use> that decides encoding, not any magical meta data. =item 3. The internal utf-8 flag has no meaning with regards to the encoding of your string. Just ignore that flag unless you debug a Perl bug, a module written in XS or want to dive into the internals of perl. Otherwise it will only confuse you, as, despite the name, it says nothing about how your string is encoded. You can have Unicode strings with that flag set, with that flag clear, and you can have binary data with that flag set and that flag clear. Other possibilities exist, too. If you didn't know about that flag, just the better, pretend it doesn't exist. =item 4. A "Unicode String" is simply a string where each character can be validly interpreted as a Unicode code point. If you have UTF-8 encoded data, it is no longer a Unicode string, but a Unicode string encoded in UTF-8, giving you a binary string. =item 5. A string containing "high" (> 255) character values is I<not> a UTF-8 string. It's a fact. Learn to live with it. =back I hope this helps :) =head1 OBJECT-ORIENTED INTERFACE The object oriented interface lets you configure your own encoding or decoding style, within the limits of supported formats. =over =item $json = new JSON::XS Creates a new JSON::XS object that can be used to de/encode JSON strings. All boolean flags described below are by default I<disabled> (with the exception of C<allow_nonref>, which defaults to I<enabled> since version C<4.0>). The mutators for flags all return the JSON object again and thus calls can be chained: my $json = JSON::XS->new->utf8->space_after->encode ({a => [1,2]}) => {"a": [1, 2]} =item $json = $json->ascii ([$enable]) =item $enabled = $json->get_ascii If C<$enable> is true (or missing), then the C<encode> method will not generate characters outside the code range C<0..127> (which is ASCII). Any Unicode characters outside that range will be escaped using either a single \uXXXX (BMP characters) or a double \uHHHH\uLLLLL escape sequence, as per RFC4627. The resulting encoded JSON text can be treated as a native Unicode string, an ascii-encoded, latin1-encoded or UTF-8 encoded string, or any other superset of ASCII. If C<$enable> is false, then the C<encode> method will not escape Unicode characters unless required by the JSON syntax or other flags. This results in a faster and more compact format. See also the section I<ENCODING/CODESET FLAG NOTES> later in this document. The main use for this flag is to produce JSON texts that can be transmitted over a 7-bit channel, as the encoded JSON texts will not contain any 8 bit characters. JSON::XS->new->ascii (1)->encode ([chr 0x10401]) => ["\ud801\udc01"] =item $json = $json->latin1 ([$enable]) =item $enabled = $json->get_latin1 If C<$enable> is true (or missing), then the C<encode> method will encode the resulting JSON text as latin1 (or iso-8859-1), escaping any characters outside the code range C<0..255>. The resulting string can be treated as a latin1-encoded JSON text or a native Unicode string. The C<decode> method will not be affected in any way by this flag, as C<decode> by default expects Unicode, which is a strict superset of latin1. If C<$enable> is false, then the C<encode> method will not escape Unicode characters unless required by the JSON syntax or other flags. See also the section I<ENCODING/CODESET FLAG NOTES> later in this document. The main use for this flag is efficiently encoding binary data as JSON text, as most octets will not be escaped, resulting in a smaller encoded size. The disadvantage is that the resulting JSON text is encoded in latin1 (and must correctly be treated as such when storing and transferring), a rare encoding for JSON. It is therefore most useful when you want to store data structures known to contain binary data efficiently in files or databases, not when talking to other JSON encoders/decoders. JSON::XS->new->latin1->encode (["\x{89}\x{abc}"] => ["\x{89}\\u0abc"] # (perl syntax, U+abc escaped, U+89 not) =item $json = $json->utf8 ([$enable]) =item $enabled = $json->get_utf8 If C<$enable> is true (or missing), then the C<encode> method will encode the JSON result into UTF-8, as required by many protocols, while the C<decode> method expects to be handed a UTF-8-encoded string. Please note that UTF-8-encoded strings do not contain any characters outside the range C<0..255>, they are thus useful for bytewise/binary I/O. In future versions, enabling this option might enable autodetection of the UTF-16 and UTF-32 encoding families, as described in RFC4627. If C<$enable> is false, then the C<encode> method will return the JSON string as a (non-encoded) Unicode string, while C<decode> expects thus a Unicode string. Any decoding or encoding (e.g. to UTF-8 or UTF-16) needs to be done yourself, e.g. using the Encode module. See also the section I<ENCODING/CODESET FLAG NOTES> later in this document. Example, output UTF-16BE-encoded JSON: use Encode; $jsontext = encode "UTF-16BE", JSON::XS->new->encode ($object); Example, decode UTF-32LE-encoded JSON: use Encode; $object = JSON::XS->new->decode (decode "UTF-32LE", $jsontext); =item $json = $json->pretty ([$enable]) This enables (or disables) all of the C<indent>, C<space_before> and C<space_after> (and in the future possibly more) flags in one call to generate the most readable (or most compact) form possible. Example, pretty-print some simple structure: my $json = JSON::XS->new->pretty(1)->encode ({a => [1,2]}) => { "a" : [ 1, 2 ] } =item $json = $json->indent ([$enable]) =item $enabled = $json->get_indent If C<$enable> is true (or missing), then the C<encode> method will use a multiline format as output, putting every array member or object/hash key-value pair into its own line, indenting them properly. If C<$enable> is false, no newlines or indenting will be produced, and the resulting JSON text is guaranteed not to contain any C<newlines>. This setting has no effect when decoding JSON texts. =item $json = $json->space_before ([$enable]) =item $enabled = $json->get_space_before If C<$enable> is true (or missing), then the C<encode> method will add an extra optional space before the C<:> separating keys from values in JSON objects. If C<$enable> is false, then the C<encode> method will not add any extra space at those places. This setting has no effect when decoding JSON texts. You will also most likely combine this setting with C<space_after>. Example, space_before enabled, space_after and indent disabled: {"key" :"value"} =item $json = $json->space_after ([$enable]) =item $enabled = $json->get_space_after If C<$enable> is true (or missing), then the C<encode> method will add an extra optional space after the C<:> separating keys from values in JSON objects and extra whitespace after the C<,> separating key-value pairs and array members. If C<$enable> is false, then the C<encode> method will not add any extra space at those places. This setting has no effect when decoding JSON texts. Example, space_before and indent disabled, space_after enabled: {"key": "value"} =item $json = $json->relaxed ([$enable]) =item $enabled = $json->get_relaxed If C<$enable> is true (or missing), then C<decode> will accept some extensions to normal JSON syntax (see below). C<encode> will not be affected in any way. I<Be aware that this option makes you accept invalid JSON texts as if they were valid!>. I suggest only to use this option to parse application-specific files written by humans (configuration files, resource files etc.) If C<$enable> is false (the default), then C<decode> will only accept valid JSON texts. Currently accepted extensions are: =over =item * list items can have an end-comma JSON I<separates> array elements and key-value pairs with commas. This can be annoying if you write JSON texts manually and want to be able to quickly append elements, so this extension accepts comma at the end of such items not just between them: [ 1, 2, <- this comma not normally allowed ] { "k1": "v1", "k2": "v2", <- this comma not normally allowed } =item * shell-style '#'-comments Whenever JSON allows whitespace, shell-style comments are additionally allowed. They are terminated by the first carriage-return or line-feed character, after which more white-space and comments are allowed. [ 1, # this comment not allowed in JSON # neither this one... ] =item * literal ASCII TAB characters in strings Literal ASCII TAB characters are now allowed in strings (and treated as C<\t>). [ "Hello\tWorld", "Hello<TAB>World", # literal <TAB> would not normally be allowed ] =back =item $json = $json->canonical ([$enable]) =item $enabled = $json->get_canonical If C<$enable> is true (or missing), then the C<encode> method will output JSON objects by sorting their keys. This is adding a comparatively high overhead. If C<$enable> is false, then the C<encode> method will output key-value pairs in the order Perl stores them (which will likely change between runs of the same script, and can change even within the same run from 5.18 onwards). This option is useful if you want the same data structure to be encoded as the same JSON text (given the same overall settings). If it is disabled, the same hash might be encoded differently even if contains the same data, as key-value pairs have no inherent ordering in Perl. This setting has no effect when decoding JSON texts. This setting has currently no effect on tied hashes. =item $json = $json->allow_nonref ([$enable]) =item $enabled = $json->get_allow_nonref Unlike other boolean options, this opotion is enabled by default beginning with version C<4.0>. See L<SECURITY CONSIDERATIONS> for the gory details. If C<$enable> is true (or missing), then the C<encode> method can convert a non-reference into its corresponding string, number or null JSON value, which is an extension to RFC4627. Likewise, C<decode> will accept those JSON values instead of croaking. If C<$enable> is false, then the C<encode> method will croak if it isn't passed an arrayref or hashref, as JSON texts must either be an object or array. Likewise, C<decode> will croak if given something that is not a JSON object or array. Example, encode a Perl scalar as JSON value without enabled C<allow_nonref>, resulting in an error: JSON::XS->new->allow_nonref (0)->encode ("Hello, World!") => hash- or arrayref expected... =item $json = $json->allow_unknown ([$enable]) =item $enabled = $json->get_allow_unknown If C<$enable> is true (or missing), then C<encode> will I<not> throw an exception when it encounters values it cannot represent in JSON (for example, filehandles) but instead will encode a JSON C<null> value. Note that blessed objects are not included here and are handled separately by c<allow_nonref>. If C<$enable> is false (the default), then C<encode> will throw an exception when it encounters anything it cannot encode as JSON. This option does not affect C<decode> in any way, and it is recommended to leave it off unless you know your communications partner. =item $json = $json->allow_blessed ([$enable]) =item $enabled = $json->get_allow_blessed See L<OBJECT SERIALISATION> for details. If C<$enable> is true (or missing), then the C<encode> method will not barf when it encounters a blessed reference that it cannot convert otherwise. Instead, a JSON C<null> value is encoded instead of the object. If C<$enable> is false (the default), then C<encode> will throw an exception when it encounters a blessed object that it cannot convert otherwise. This setting has no effect on C<decode>. =item $json = $json->convert_blessed ([$enable]) =item $enabled = $json->get_convert_blessed See L<OBJECT SERIALISATION> for details. If C<$enable> is true (or missing), then C<encode>, upon encountering a blessed object, will check for the availability of the C<TO_JSON> method on the object's class. If found, it will be called in scalar context and the resulting scalar will be encoded instead of the object. The C<TO_JSON> method may safely call die if it wants. If C<TO_JSON> returns other blessed objects, those will be handled in the same way. C<TO_JSON> must take care of not causing an endless recursion cycle (== crash) in this case. The name of C<TO_JSON> was chosen because other methods called by the Perl core (== not by the user of the object) are usually in upper case letters and to avoid collisions with any C<to_json> function or method. If C<$enable> is false (the default), then C<encode> will not consider this type of conversion. This setting has no effect on C<decode>. =item $json = $json->allow_tags ([$enable]) =item $enabled = $json->get_allow_tags See L<OBJECT SERIALISATION> for details. If C<$enable> is true (or missing), then C<encode>, upon encountering a blessed object, will check for the availability of the C<FREEZE> method on the object's class. If found, it will be used to serialise the object into a nonstandard tagged JSON value (that JSON decoders cannot decode). It also causes C<decode> to parse such tagged JSON values and deserialise them via a call to the C<THAW> method. If C<$enable> is false (the default), then C<encode> will not consider this type of conversion, and tagged JSON values will cause a parse error in C<decode>, as if tags were not part of the grammar. =item $json->boolean_values ([$false, $true]) =item ($false, $true) = $json->get_boolean_values By default, JSON booleans will be decoded as overloaded C<$Types::Serialiser::false> and C<$Types::Serialiser::true> objects. With this method you can specify your own boolean values for decoding - on decode, JSON C<false> will be decoded as a copy of C<$false>, and JSON C<true> will be decoded as C<$true> ("copy" here is the same thing as assigning a value to another variable, i.e. C<$copy = $false>). Calling this method without any arguments will reset the booleans to their default values. C<get_boolean_values> will return both C<$false> and C<$true> values, or the empty list when they are set to the default. =item $json = $json->filter_json_object ([$coderef->($hashref)]) When C<$coderef> is specified, it will be called from C<decode> each time it decodes a JSON object. The only argument is a reference to the newly-created hash. If the code reference returns a single scalar (which need not be a reference), this value (or rather a copy of it) is inserted into the deserialised data structure. If it returns an empty list (NOTE: I<not> C<undef>, which is a valid scalar), the original deserialised hash will be inserted. This setting can slow down decoding considerably. When C<$coderef> is omitted or undefined, any existing callback will be removed and C<decode> will not change the deserialised hash in any way. Example, convert all JSON objects into the integer 5: my $js = JSON::XS->new->filter_json_object (sub { 5 }); # returns [5] $js->decode ('[{}]') # throw an exception because allow_nonref is not enabled # so a lone 5 is not allowed. $js->decode ('{"a":1, "b":2}'); =item $json = $json->filter_json_single_key_object ($key [=> $coderef->($value)]) Works remotely similar to C<filter_json_object>, but is only called for JSON objects having a single key named C<$key>. This C<$coderef> is called before the one specified via C<filter_json_object>, if any. It gets passed the single value in the JSON object. If it returns a single value, it will be inserted into the data structure. If it returns nothing (not even C<undef> but the empty list), the callback from C<filter_json_object> will be called next, as if no single-key callback were specified. If C<$coderef> is omitted or undefined, the corresponding callback will be disabled. There can only ever be one callback for a given key. As this callback gets called less often then the C<filter_json_object> one, decoding speed will not usually suffer as much. Therefore, single-key objects make excellent targets to serialise Perl objects into, especially as single-key JSON objects are as close to the type-tagged value concept as JSON gets (it's basically an ID/VALUE tuple). Of course, JSON does not support this in any way, so you need to make sure your data never looks like a serialised Perl hash. Typical names for the single object key are C<__class_whatever__>, or C<$__dollars_are_rarely_used__$> or C<}ugly_brace_placement>, or even things like C<__class_md5sum(classname)__>, to reduce the risk of clashing with real hashes. Example, decode JSON objects of the form C<< { "__widget__" => <id> } >> into the corresponding C<< $WIDGET{<id>} >> object: # return whatever is in $WIDGET{5}: JSON::XS ->new ->filter_json_single_key_object (__widget__ => sub { $WIDGET{ $_[0] } }) ->decode ('{"__widget__": 5') # this can be used with a TO_JSON method in some "widget" class # for serialisation to json: sub WidgetBase::TO_JSON { my ($self) = @_; unless ($self->{id}) { $self->{id} = ..get..some..id..; $WIDGET{$self->{id}} = $self; } { __widget__ => $self->{id} } } =item $json = $json->shrink ([$enable]) =item $enabled = $json->get_shrink Perl usually over-allocates memory a bit when allocating space for strings. This flag optionally resizes strings generated by either C<encode> or C<decode> to their minimum size possible. This can save memory when your JSON texts are either very very long or you have many short strings. It will also try to downgrade any strings to octet-form if possible: perl stores strings internally either in an encoding called UTF-X or in octet-form. The latter cannot store everything but uses less space in general (and some buggy Perl or C code might even rely on that internal representation being used). The actual definition of what shrink does might change in future versions, but it will always try to save space at the expense of time. If C<$enable> is true (or missing), the string returned by C<encode> will be shrunk-to-fit, while all strings generated by C<decode> will also be shrunk-to-fit. If C<$enable> is false, then the normal perl allocation algorithms are used. If you work with your data, then this is likely to be faster. In the future, this setting might control other things, such as converting strings that look like integers or floats into integers or floats internally (there is no difference on the Perl level), saving space. =item $json = $json->max_depth ([$maximum_nesting_depth]) =item $max_depth = $json->get_max_depth Sets the maximum nesting level (default C<512>) accepted while encoding or decoding. If a higher nesting level is detected in JSON text or a Perl data structure, then the encoder and decoder will stop and croak at that point. Nesting level is defined by number of hash- or arrayrefs that the encoder needs to traverse to reach a given point or the number of C<{> or C<[> characters without their matching closing parenthesis crossed to reach a given character in a string. Setting the maximum depth to one disallows any nesting, so that ensures that the object is only a single hash/object or array. If no argument is given, the highest possible setting will be used, which is rarely useful. Note that nesting is implemented by recursion in C. The default value has been chosen to be as large as typical operating systems allow without crashing. See SECURITY CONSIDERATIONS, below, for more info on why this is useful. =item $json = $json->max_size ([$maximum_string_size]) =item $max_size = $json->get_max_size Set the maximum length a JSON text may have (in bytes) where decoding is being attempted. The default is C<0>, meaning no limit. When C<decode> is called on a string that is longer then this many bytes, it will not attempt to decode the string but throw an exception. This setting has no effect on C<encode> (yet). If no argument is given, the limit check will be deactivated (same as when C<0> is specified). See SECURITY CONSIDERATIONS, below, for more info on why this is useful. =item $json_text = $json->encode ($perl_scalar) Converts the given Perl value or data structure to its JSON representation. Croaks on error. =item $perl_scalar = $json->decode ($json_text) The opposite of C<encode>: expects a JSON text and tries to parse it, returning the resulting simple scalar or reference. Croaks on error. =item ($perl_scalar, $characters) = $json->decode_prefix ($json_text) This works like the C<decode> method, but instead of raising an exception when there is trailing garbage after the first JSON object, it will silently stop parsing there and return the number of characters consumed so far. This is useful if your JSON texts are not delimited by an outer protocol and you need to know where the JSON text ends. JSON::XS->new->decode_prefix ("[1] the tail") => ([1], 3) =back =head1 INCREMENTAL PARSING In some cases, there is the need for incremental parsing of JSON texts. While this module always has to keep both JSON text and resulting Perl data structure in memory at one time, it does allow you to parse a JSON stream incrementally. It does so by accumulating text until it has a full JSON object, which it then can decode. This process is similar to using C<decode_prefix> to see if a full JSON object is available, but is much more efficient (and can be implemented with a minimum of method calls). JSON::XS will only attempt to parse the JSON text once it is sure it has enough text to get a decisive result, using a very simple but truly incremental parser. This means that it sometimes won't stop as early as the full parser, for example, it doesn't detect mismatched parentheses. The only thing it guarantees is that it starts decoding as soon as a syntactically valid JSON text has been seen. This means you need to set resource limits (e.g. C<max_size>) to ensure the parser will stop parsing in the presence if syntax errors. The following methods implement this incremental parser. =over =item [void, scalar or list context] = $json->incr_parse ([$string]) This is the central parsing function. It can both append new text and extract objects from the stream accumulated so far (both of these functions are optional). If C<$string> is given, then this string is appended to the already existing JSON fragment stored in the C<$json> object. After that, if the function is called in void context, it will simply return without doing anything further. This can be used to add more text in as many chunks as you want. If the method is called in scalar context, then it will try to extract exactly I<one> JSON object. If that is successful, it will return this object, otherwise it will return C<undef>. If there is a parse error, this method will croak just as C<decode> would do (one can then use C<incr_skip> to skip the erroneous part). This is the most common way of using the method. And finally, in list context, it will try to extract as many objects from the stream as it can find and return them, or the empty list otherwise. For this to work, there must be no separators (other than whitespace) between the JSON objects or arrays, instead they must be concatenated back-to-back. If an error occurs, an exception will be raised as in the scalar context case. Note that in this case, any previously-parsed JSON texts will be lost. Example: Parse some JSON arrays/objects in a given string and return them. my @objs = JSON::XS->new->incr_parse ("[5][7][1,2]"); =item $lvalue_string = $json->incr_text This method returns the currently stored JSON fragment as an lvalue, that is, you can manipulate it. This I<only> works when a preceding call to C<incr_parse> in I<scalar context> successfully returned an object. Under all other circumstances you must not call this function (I mean it. although in simple tests it might actually work, it I<will> fail under real world conditions). As a special exception, you can also call this method before having parsed anything. That means you can only use this function to look at or manipulate text before or after complete JSON objects, not while the parser is in the middle of parsing a JSON object. This function is useful in two cases: a) finding the trailing text after a JSON object or b) parsing multiple JSON objects separated by non-JSON text (such as commas). =item $json->incr_skip This will reset the state of the incremental parser and will remove the parsed text from the input buffer so far. This is useful after C<incr_parse> died, in which case the input buffer and incremental parser state is left unchanged, to skip the text parsed so far and to reset the parse state. The difference to C<incr_reset> is that only text until the parse error occurred is removed. =item $json->incr_reset This completely resets the incremental parser, that is, after this call, it will be as if the parser had never parsed anything. This is useful if you want to repeatedly parse JSON objects and want to ignore any trailing data, which means you have to reset the parser after each successful decode. =back =head2 LIMITATIONS The incremental parser is a non-exact parser: it works by gathering as much text as possible that I<could> be a valid JSON text, followed by trying to decode it. That means it sometimes needs to read more data than strictly necessary to diagnose an invalid JSON text. For example, after parsing the following fragment, the parser I<could> stop with an error, as this fragment I<cannot> be the beginning of a valid JSON text: [, In reality, hopwever, the parser might continue to read data until a length limit is exceeded or it finds a closing bracket. =head2 EXAMPLES Some examples will make all this clearer. First, a simple example that works similarly to C<decode_prefix>: We want to decode the JSON object at the start of a string and identify the portion after the JSON object: my $text = "[1,2,3] hello"; my $json = new JSON::XS; my $obj = $json->incr_parse ($text) or die "expected JSON object or array at beginning of string"; my $tail = $json->incr_text; # $tail now contains " hello" Easy, isn't it? Now for a more complicated example: Imagine a hypothetical protocol where you read some requests from a TCP stream, and each request is a JSON array, without any separation between them (in fact, it is often useful to use newlines as "separators", as these get interpreted as whitespace at the start of the JSON text, which makes it possible to test said protocol with C<telnet>...). Here is how you'd do it (it is trivial to write this in an event-based manner): my $json = new JSON::XS; # read some data from the socket while (sysread $socket, my $buf, 4096) { # split and decode as many requests as possible for my $request ($json->incr_parse ($buf)) { # act on the $request } } Another complicated example: Assume you have a string with JSON objects or arrays, all separated by (optional) comma characters (e.g. C<[1],[2], [3]>). To parse them, we have to skip the commas between the JSON texts, and here is where the lvalue-ness of C<incr_text> comes in useful: my $text = "[1],[2], [3]"; my $json = new JSON::XS; # void context, so no parsing done $json->incr_parse ($text); # now extract as many objects as possible. note the # use of scalar context so incr_text can be called. while (my $obj = $json->incr_parse) { # do something with $obj # now skip the optional comma $json->incr_text =~ s/^ \s* , //x; } Now lets go for a very complex example: Assume that you have a gigantic JSON array-of-objects, many gigabytes in size, and you want to parse it, but you cannot load it into memory fully (this has actually happened in the real world :). Well, you lost, you have to implement your own JSON parser. But JSON::XS can still help you: You implement a (very simple) array parser and let JSON decode the array elements, which are all full JSON objects on their own (this wouldn't work if the array elements could be JSON numbers, for example): my $json = new JSON::XS; # open the monster open my $fh, "<bigfile.json" or die "bigfile: $!"; # first parse the initial "[" for (;;) { sysread $fh, my $buf, 65536 or die "read error: $!"; $json->incr_parse ($buf); # void context, so no parsing # Exit the loop once we found and removed(!) the initial "[". # In essence, we are (ab-)using the $json object as a simple scalar # we append data to. last if $json->incr_text =~ s/^ \s* \[ //x; } # now we have the skipped the initial "[", so continue # parsing all the elements. for (;;) { # in this loop we read data until we got a single JSON object for (;;) { if (my $obj = $json->incr_parse) { # do something with $obj last; } # add more data sysread $fh, my $buf, 65536 or die "read error: $!"; $json->incr_parse ($buf); # void context, so no parsing } # in this loop we read data until we either found and parsed the # separating "," between elements, or the final "]" for (;;) { # first skip whitespace $json->incr_text =~ s/^\s*//; # if we find "]", we are done if ($json->incr_text =~ s/^\]//) { print "finished.\n"; exit; } # if we find ",", we can continue with the next element if ($json->incr_text =~ s/^,//) { last; } # if we find anything else, we have a parse error! if (length $json->incr_text) { die "parse error near ", $json->incr_text; } # else add more data sysread $fh, my $buf, 65536 or die "read error: $!"; $json->incr_parse ($buf); # void context, so no parsing } This is a complex example, but most of the complexity comes from the fact that we are trying to be correct (bear with me if I am wrong, I never ran the above example :). =head1 MAPPING This section describes how JSON::XS maps Perl values to JSON values and vice versa. These mappings are designed to "do the right thing" in most circumstances automatically, preserving round-tripping characteristics (what you put in comes out as something equivalent). For the more enlightened: note that in the following descriptions, lowercase I<perl> refers to the Perl interpreter, while uppercase I<Perl> refers to the abstract Perl language itself. =head2 JSON -> PERL =over =item object A JSON object becomes a reference to a hash in Perl. No ordering of object keys is preserved (JSON does not preserve object key ordering itself). =item array A JSON array becomes a reference to an array in Perl. =item string A JSON string becomes a string scalar in Perl - Unicode codepoints in JSON are represented by the same codepoints in the Perl string, so no manual decoding is necessary. =item number A JSON number becomes either an integer, numeric (floating point) or string scalar in perl, depending on its range and any fractional parts. On the Perl level, there is no difference between those as Perl handles all the conversion details, but an integer may take slightly less memory and might represent more values exactly than floating point numbers. If the number consists of digits only, JSON::XS will try to represent it as an integer value. If that fails, it will try to represent it as a numeric (floating point) value if that is possible without loss of precision. Otherwise it will preserve the number as a string value (in which case you lose roundtripping ability, as the JSON number will be re-encoded to a JSON string). Numbers containing a fractional or exponential part will always be represented as numeric (floating point) values, possibly at a loss of precision (in which case you might lose perfect roundtripping ability, but the JSON number will still be re-encoded as a JSON number). Note that precision is not accuracy - binary floating point values cannot represent most decimal fractions exactly, and when converting from and to floating point, JSON::XS only guarantees precision up to but not including the least significant bit. =item true, false These JSON atoms become C<Types::Serialiser::true> and C<Types::Serialiser::false>, respectively. They are overloaded to act almost exactly like the numbers C<1> and C<0>. You can check whether a scalar is a JSON boolean by using the C<Types::Serialiser::is_bool> function (after C<use Types::Serialier>, of course). =item null A JSON null atom becomes C<undef> in Perl. =item shell-style comments (C<< # I<text> >>) As a nonstandard extension to the JSON syntax that is enabled by the C<relaxed> setting, shell-style comments are allowed. They can start anywhere outside strings and go till the end of the line. =item tagged values (C<< (I<tag>)I<value> >>). Another nonstandard extension to the JSON syntax, enabled with the C<allow_tags> setting, are tagged values. In this implementation, the I<tag> must be a perl package/class name encoded as a JSON string, and the I<value> must be a JSON array encoding optional constructor arguments. See L<OBJECT SERIALISATION>, below, for details. =back =head2 PERL -> JSON The mapping from Perl to JSON is slightly more difficult, as Perl is a truly typeless language, so we can only guess which JSON type is meant by a Perl value. =over =item hash references Perl hash references become JSON objects. As there is no inherent ordering in hash keys (or JSON objects), they will usually be encoded in a pseudo-random order. JSON::XS can optionally sort the hash keys (determined by the I<canonical> flag), so the same datastructure will serialise to the same JSON text (given same settings and version of JSON::XS), but this incurs a runtime overhead and is only rarely useful, e.g. when you want to compare some JSON text against another for equality. =item array references Perl array references become JSON arrays. =item other references Other unblessed references are generally not allowed and will cause an exception to be thrown, except for references to the integers C<0> and C<1>, which get turned into C<false> and C<true> atoms in JSON. Since C<JSON::XS> uses the boolean model from L<Types::Serialiser>, you can also C<use Types::Serialiser> and then use C<Types::Serialiser::false> and C<Types::Serialiser::true> to improve readability. use Types::Serialiser; encode_json [\0, Types::Serialiser::true] # yields [false,true] =item Types::Serialiser::true, Types::Serialiser::false These special values from the L<Types::Serialiser> module become JSON true and JSON false values, respectively. You can also use C<\1> and C<\0> directly if you want. =item blessed objects Blessed objects are not directly representable in JSON, but C<JSON::XS> allows various ways of handling objects. See L<OBJECT SERIALISATION>, below, for details. =item simple scalars Simple Perl scalars (any scalar that is not a reference) are the most difficult objects to encode: JSON::XS will encode undefined scalars as JSON C<null> values, scalars that have last been used in a string context before encoding as JSON strings, and anything else as number value: # dump as number encode_json [2] # yields [2] encode_json [-3.0e17] # yields [-3e+17] my $value = 5; encode_json [$value] # yields [5] # used as string, so dump as string print $value; encode_json [$value] # yields ["5"] # undef becomes null encode_json [undef] # yields [null] You can force the type to be a JSON string by stringifying it: my $x = 3.1; # some variable containing a number "$x"; # stringified $x .= ""; # another, more awkward way to stringify print $x; # perl does it for you, too, quite often You can force the type to be a JSON number by numifying it: my $x = "3"; # some variable containing a string $x += 0; # numify it, ensuring it will be dumped as a number $x *= 1; # same thing, the choice is yours. You can not currently force the type in other, less obscure, ways. Tell me if you need this capability (but don't forget to explain why it's needed :). Note that numerical precision has the same meaning as under Perl (so binary to decimal conversion follows the same rules as in Perl, which can differ to other languages). Also, your perl interpreter might expose extensions to the floating point numbers of your platform, such as infinities or NaN's - these cannot be represented in JSON, and it is an error to pass those in. =back =head2 OBJECT SERIALISATION As JSON cannot directly represent Perl objects, you have to choose between a pure JSON representation (without the ability to deserialise the object automatically again), and a nonstandard extension to the JSON syntax, tagged values. =head3 SERIALISATION What happens when C<JSON::XS> encounters a Perl object depends on the C<allow_blessed>, C<convert_blessed> and C<allow_tags> settings, which are used in this order: =over =item 1. C<allow_tags> is enabled and the object has a C<FREEZE> method. In this case, C<JSON::XS> uses the L<Types::Serialiser> object serialisation protocol to create a tagged JSON value, using a nonstandard extension to the JSON syntax. This works by invoking the C<FREEZE> method on the object, with the first argument being the object to serialise, and the second argument being the constant string C<JSON> to distinguish it from other serialisers. The C<FREEZE> method can return any number of values (i.e. zero or more). These values and the paclkage/classname of the object will then be encoded as a tagged JSON value in the following format: ("classname")[FREEZE return values...] e.g.: ("URI")["http://www.google.com/"] ("MyDate")[2013,10,29] ("ImageData::JPEG")["Z3...VlCg=="] For example, the hypothetical C<My::Object> C<FREEZE> method might use the objects C<type> and C<id> members to encode the object: sub My::Object::FREEZE { my ($self, $serialiser) = @_; ($self->{type}, $self->{id}) } =item 2. C<convert_blessed> is enabled and the object has a C<TO_JSON> method. In this case, the C<TO_JSON> method of the object is invoked in scalar context. It must return a single scalar that can be directly encoded into JSON. This scalar replaces the object in the JSON text. For example, the following C<TO_JSON> method will convert all L<URI> objects to JSON strings when serialised. The fatc that these values originally were L<URI> objects is lost. sub URI::TO_JSON { my ($uri) = @_; $uri->as_string } =item 3. C<allow_blessed> is enabled. The object will be serialised as a JSON null value. =item 4. none of the above If none of the settings are enabled or the respective methods are missing, C<JSON::XS> throws an exception. =back =head3 DESERIALISATION For deserialisation there are only two cases to consider: either nonstandard tagging was used, in which case C<allow_tags> decides, or objects cannot be automatically be deserialised, in which case you can use postprocessing or the C<filter_json_object> or C<filter_json_single_key_object> callbacks to get some real objects our of your JSON. This section only considers the tagged value case: I a tagged JSON object is encountered during decoding and C<allow_tags> is disabled, a parse error will result (as if tagged values were not part of the grammar). If C<allow_tags> is enabled, C<JSON::XS> will look up the C<THAW> method of the package/classname used during serialisation (it will not attempt to load the package as a Perl module). If there is no such method, the decoding will fail with an error. Otherwise, the C<THAW> method is invoked with the classname as first argument, the constant string C<JSON> as second argument, and all the values from the JSON array (the values originally returned by the C<FREEZE> method) as remaining arguments. The method must then return the object. While technically you can return any Perl scalar, you might have to enable the C<enable_nonref> setting to make that work in all cases, so better return an actual blessed reference. As an example, let's implement a C<THAW> function that regenerates the C<My::Object> from the C<FREEZE> example earlier: sub My::Object::THAW { my ($class, $serialiser, $type, $id) = @_; $class->new (type => $type, id => $id) } =head1 ENCODING/CODESET FLAG NOTES The interested reader might have seen a number of flags that signify encodings or codesets - C<utf8>, C<latin1> and C<ascii>. There seems to be some confusion on what these do, so here is a short comparison: C<utf8> controls whether the JSON text created by C<encode> (and expected by C<decode>) is UTF-8 encoded or not, while C<latin1> and C<ascii> only control whether C<encode> escapes character values outside their respective codeset range. Neither of these flags conflict with each other, although some combinations make less sense than others. Care has been taken to make all flags symmetrical with respect to C<encode> and C<decode>, that is, texts encoded with any combination of these flag values will be correctly decoded when the same flags are used - in general, if you use different flag settings while encoding vs. when decoding you likely have a bug somewhere. Below comes a verbose discussion of these flags. Note that a "codeset" is simply an abstract set of character-codepoint pairs, while an encoding takes those codepoint numbers and I<encodes> them, in our case into octets. Unicode is (among other things) a codeset, UTF-8 is an encoding, and ISO-8859-1 (= latin 1) and ASCII are both codesets I<and> encodings at the same time, which can be confusing. =over =item C<utf8> flag disabled When C<utf8> is disabled (the default), then C<encode>/C<decode> generate and expect Unicode strings, that is, characters with high ordinal Unicode values (> 255) will be encoded as such characters, and likewise such characters are decoded as-is, no changes to them will be done, except "(re-)interpreting" them as Unicode codepoints or Unicode characters, respectively (to Perl, these are the same thing in strings unless you do funny/weird/dumb stuff). This is useful when you want to do the encoding yourself (e.g. when you want to have UTF-16 encoded JSON texts) or when some other layer does the encoding for you (for example, when printing to a terminal using a filehandle that transparently encodes to UTF-8 you certainly do NOT want to UTF-8 encode your data first and have Perl encode it another time). =item C<utf8> flag enabled If the C<utf8>-flag is enabled, C<encode>/C<decode> will encode all characters using the corresponding UTF-8 multi-byte sequence, and will expect your input strings to be encoded as UTF-8, that is, no "character" of the input string must have any value > 255, as UTF-8 does not allow that. The C<utf8> flag therefore switches between two modes: disabled means you will get a Unicode string in Perl, enabled means you get a UTF-8 encoded octet/binary string in Perl. =item C<latin1> or C<ascii> flags enabled With C<latin1> (or C<ascii>) enabled, C<encode> will escape characters with ordinal values > 255 (> 127 with C<ascii>) and encode the remaining characters as specified by the C<utf8> flag. If C<utf8> is disabled, then the result is also correctly encoded in those character sets (as both are proper subsets of Unicode, meaning that a Unicode string with all character values < 256 is the same thing as a ISO-8859-1 string, and a Unicode string with all character values < 128 is the same thing as an ASCII string in Perl). If C<utf8> is enabled, you still get a correct UTF-8-encoded string, regardless of these flags, just some more characters will be escaped using C<\uXXXX> then before. Note that ISO-8859-1-I<encoded> strings are not compatible with UTF-8 encoding, while ASCII-encoded strings are. That is because the ISO-8859-1 encoding is NOT a subset of UTF-8 (despite the ISO-8859-1 I<codeset> being a subset of Unicode), while ASCII is. Surprisingly, C<decode> will ignore these flags and so treat all input values as governed by the C<utf8> flag. If it is disabled, this allows you to decode ISO-8859-1- and ASCII-encoded strings, as both strict subsets of Unicode. If it is enabled, you can correctly decode UTF-8 encoded strings. So neither C<latin1> nor C<ascii> are incompatible with the C<utf8> flag - they only govern when the JSON output engine escapes a character or not. The main use for C<latin1> is to relatively efficiently store binary data as JSON, at the expense of breaking compatibility with most JSON decoders. The main use for C<ascii> is to force the output to not contain characters with values > 127, which means you can interpret the resulting string as UTF-8, ISO-8859-1, ASCII, KOI8-R or most about any character set and 8-bit-encoding, and still get the same data structure back. This is useful when your channel for JSON transfer is not 8-bit clean or the encoding might be mangled in between (e.g. in mail), and works because ASCII is a proper subset of most 8-bit and multibyte encodings in use in the world. =back =head2 JSON and ECMAscript JSON syntax is based on how literals are represented in javascript (the not-standardised predecessor of ECMAscript) which is presumably why it is called "JavaScript Object Notation". However, JSON is not a subset (and also not a superset of course) of ECMAscript (the standard) or javascript (whatever browsers actually implement). If you want to use javascript's C<eval> function to "parse" JSON, you might run into parse errors for valid JSON texts, or the resulting data structure might not be queryable: One of the problems is that U+2028 and U+2029 are valid characters inside JSON strings, but are not allowed in ECMAscript string literals, so the following Perl fragment will not output something that can be guaranteed to be parsable by javascript's C<eval>: use JSON::XS; print encode_json [chr 0x2028]; The right fix for this is to use a proper JSON parser in your javascript programs, and not rely on C<eval> (see for example Douglas Crockford's F<json2.js> parser). If this is not an option, you can, as a stop-gap measure, simply encode to ASCII-only JSON: use JSON::XS; print JSON::XS->new->ascii->encode ([chr 0x2028]); Note that this will enlarge the resulting JSON text quite a bit if you have many non-ASCII characters. You might be tempted to run some regexes to only escape U+2028 and U+2029, e.g.: # DO NOT USE THIS! my $json = JSON::XS->new->utf8->encode ([chr 0x2028]); $json =~ s/\xe2\x80\xa8/\\u2028/g; # escape U+2028 $json =~ s/\xe2\x80\xa9/\\u2029/g; # escape U+2029 print $json; Note that I<this is a bad idea>: the above only works for U+2028 and U+2029 and thus only for fully ECMAscript-compliant parsers. Many existing javascript implementations, however, have issues with other characters as well - using C<eval> naively simply I<will> cause problems. Another problem is that some javascript implementations reserve some property names for their own purposes (which probably makes them non-ECMAscript-compliant). For example, Iceweasel reserves the C<__proto__> property name for its own purposes. If that is a problem, you could parse try to filter the resulting JSON output for these property strings, e.g.: $json =~ s/"__proto__"\s*:/"__proto__renamed":/g; This works because C<__proto__> is not valid outside of strings, so every occurrence of C<"__proto__"\s*:> must be a string used as property name. If you know of other incompatibilities, please let me know. =head2 JSON and YAML You often hear that JSON is a subset of YAML. This is, however, a mass hysteria(*) and very far from the truth (as of the time of this writing), so let me state it clearly: I<in general, there is no way to configure JSON::XS to output a data structure as valid YAML> that works in all cases. If you really must use JSON::XS to generate YAML, you should use this algorithm (subject to change in future versions): my $to_yaml = JSON::XS->new->utf8->space_after (1); my $yaml = $to_yaml->encode ($ref) . "\n"; This will I<usually> generate JSON texts that also parse as valid YAML. Please note that YAML has hardcoded limits on (simple) object key lengths that JSON doesn't have and also has different and incompatible unicode character escape syntax, so you should make sure that your hash keys are noticeably shorter than the 1024 "stream characters" YAML allows and that you do not have characters with codepoint values outside the Unicode BMP (basic multilingual page). YAML also does not allow C<\/> sequences in strings (which JSON::XS does not I<currently> generate, but other JSON generators might). There might be other incompatibilities that I am not aware of (or the YAML specification has been changed yet again - it does so quite often). In general you should not try to generate YAML with a JSON generator or vice versa, or try to parse JSON with a YAML parser or vice versa: chances are high that you will run into severe interoperability problems when you least expect it. =over =item (*) I have been pressured multiple times by Brian Ingerson (one of the authors of the YAML specification) to remove this paragraph, despite him acknowledging that the actual incompatibilities exist. As I was personally bitten by this "JSON is YAML" lie, I refused and said I will continue to educate people about these issues, so others do not run into the same problem again and again. After this, Brian called me a (quote)I<complete and worthless idiot>(unquote). In my opinion, instead of pressuring and insulting people who actually clarify issues with YAML and the wrong statements of some of its proponents, I would kindly suggest reading the JSON spec (which is not that difficult or long) and finally make YAML compatible to it, and educating users about the changes, instead of spreading lies about the real compatibility for many I<years> and trying to silence people who point out that it isn't true. Addendum/2009: the YAML 1.2 spec is still incompatible with JSON, even though the incompatibilities have been documented (and are known to Brian) for many years and the spec makes explicit claims that YAML is a superset of JSON. It would be so easy to fix, but apparently, bullying people and corrupting userdata is so much easier. =back =head2 SPEED It seems that JSON::XS is surprisingly fast, as shown in the following tables. They have been generated with the help of the C<eg/bench> program in the JSON::XS distribution, to make it easy to compare on your own system. First comes a comparison between various modules using a very short single-line JSON string (also available at L<http://dist.schmorp.de/misc/json/short.json>). {"method": "handleMessage", "params": ["user1", "we were just talking"], "id": null, "array":[1,11,234,-5,1e5,1e7, 1, 0]} It shows the number of encodes/decodes per second (JSON::XS uses the functional interface, while JSON::XS/2 uses the OO interface with pretty-printing and hashkey sorting enabled, JSON::XS/3 enables shrink. JSON::DWIW/DS uses the deserialise function, while JSON::DWIW::FJ uses the from_json method). Higher is better: module | encode | decode | --------------|------------|------------| JSON::DWIW/DS | 86302.551 | 102300.098 | JSON::DWIW/FJ | 86302.551 | 75983.768 | JSON::PP | 15827.562 | 6638.658 | JSON::Syck | 63358.066 | 47662.545 | JSON::XS | 511500.488 | 511500.488 | JSON::XS/2 | 291271.111 | 388361.481 | JSON::XS/3 | 361577.931 | 361577.931 | Storable | 66788.280 | 265462.278 | --------------+------------+------------+ That is, JSON::XS is almost six times faster than JSON::DWIW on encoding, about five times faster on decoding, and over thirty to seventy times faster than JSON's pure perl implementation. It also compares favourably to Storable for small amounts of data. Using a longer test string (roughly 18KB, generated from Yahoo! Locals search API (L<http://dist.schmorp.de/misc/json/long.json>). module | encode | decode | --------------|------------|------------| JSON::DWIW/DS | 1647.927 | 2673.916 | JSON::DWIW/FJ | 1630.249 | 2596.128 | JSON::PP | 400.640 | 62.311 | JSON::Syck | 1481.040 | 1524.869 | JSON::XS | 20661.596 | 9541.183 | JSON::XS/2 | 10683.403 | 9416.938 | JSON::XS/3 | 20661.596 | 9400.054 | Storable | 19765.806 | 10000.725 | --------------+------------+------------+ Again, JSON::XS leads by far (except for Storable which non-surprisingly decodes a bit faster). On large strings containing lots of high Unicode characters, some modules (such as JSON::PC) seem to decode faster than JSON::XS, but the result will be broken due to missing (or wrong) Unicode handling. Others refuse to decode or encode properly, so it was impossible to prepare a fair comparison table for that case. =head1 SECURITY CONSIDERATIONS When you are using JSON in a protocol, talking to untrusted potentially hostile creatures requires relatively few measures. First of all, your JSON decoder should be secure, that is, should not have any buffer overflows. Obviously, this module should ensure that and I am trying hard on making that true, but you never know. Second, you need to avoid resource-starving attacks. That means you should limit the size of JSON texts you accept, or make sure then when your resources run out, that's just fine (e.g. by using a separate process that can crash safely). The size of a JSON text in octets or characters is usually a good indication of the size of the resources required to decode it into a Perl structure. While JSON::XS can check the size of the JSON text, it might be too late when you already have it in memory, so you might want to check the size before you accept the string. Third, JSON::XS recurses using the C stack when decoding objects and arrays. The C stack is a limited resource: for instance, on my amd64 machine with 8MB of stack size I can decode around 180k nested arrays but only 14k nested JSON objects (due to perl itself recursing deeply on croak to free the temporary). If that is exceeded, the program crashes. To be conservative, the default nesting limit is set to 512. If your process has a smaller stack, you should adjust this setting accordingly with the C<max_depth> method. Something else could bomb you, too, that I forgot to think of. In that case, you get to keep the pieces. I am always open for hints, though... Also keep in mind that JSON::XS might leak contents of your Perl data structures in its error messages, so when you serialise sensitive information you might want to make sure that exceptions thrown by JSON::XS will not end up in front of untrusted eyes. If you are using JSON::XS to return packets to consumption by JavaScript scripts in a browser you should have a look at L<http://blog.archive.jpsykes.com/47/practical-csrf-and-json-security/> to see whether you are vulnerable to some common attack vectors (which really are browser design bugs, but it is still you who will have to deal with it, as major browser developers care only for features, not about getting security right). =head2 "OLD" VS. "NEW" JSON (RFC4627 VS. RFC7159) JSON originally required JSON texts to represent an array or object - scalar values were explicitly not allowed. This has changed, and versions of JSON::XS beginning with C<4.0> reflect this by allowing scalar values by default. One reason why one might not want this is that this removes a fundamental property of JSON texts, namely that they are self-delimited and self-contained, or in other words, you could take any number of "old" JSON texts and paste them together, and the result would be unambiguously parseable: [1,3]{"k":5}[][null] # four JSON texts, without doubt By allowing scalars, this property is lost: in the following example, is this one JSON text (the number 12) or two JSON texts (the numbers 1 and 2): 12 # could be 12, or 1 and 2 Another lost property of "old" JSON is that no lookahead is required to know the end of a JSON text, i.e. the JSON text definitely ended at the last C<]> or C<}> character, there was no need to read extra characters. For example, a viable network protocol with "old" JSON was to simply exchange JSON texts without delimiter. For "new" JSON, you have to use a suitable delimiter (such as a newline) after every JSON text or ensure you never encode/decode scalar values. Most protocols do work by only transferring arrays or objects, and the easiest way to avoid problems with the "new" JSON definition is to explicitly disallow scalar values in your encoder and decoder: $json_coder = JSON::XS->new->allow_nonref (0) This is a somewhat unhappy situation, and the blame can fully be put on JSON's inmventor, Douglas Crockford, who unilaterally changed the format in 2006 without consulting the IETF, forcing the IETF to either fork the format or go with it (as I was told, the IETF wasn't amused). =head1 RELATIONSHIP WITH I-JSON JSON is a somewhat sloppily-defined format - it carries around obvious Javascript baggage, such as not really defining number range, probably because Javascript only has one type of numbers: IEEE 64 bit floats ("binary64"). For this reaosn, RFC7493 defines "Internet JSON", which is a restricted subset of JSON that is supposedly more interoperable on the internet. While C<JSON::XS> does not offer specific support for I-JSON, it of course accepts valid I-JSON and by default implements some of the limitations of I-JSON, such as parsing numbers as perl numbers, which are usually a superset of binary64 numbers. To generate I-JSON, follow these rules: =over =item * always generate UTF-8 I-JSON must be encoded in UTF-8, the default for C<encode_json>. =item * numbers should be within IEEE 754 binary64 range Basically all existing perl installations use binary64 to represent floating point numbers, so all you need to do is to avoid large integers. =item * objects must not have duplicate keys This is trivially done, as C<JSON::XS> does not allow duplicate keys. =item * do not generate scalar JSON texts, use C<< ->allow_nonref (0) >> I-JSON strongly requests you to only encode arrays and objects into JSON. =item * times should be strings in ISO 8601 format There are a myriad of modules on CPAN dealing with ISO 8601 - search for C<ISO8601> on CPAN and use one. =item * encode binary data as base64 While it's tempting to just dump binary data as a string (and let C<JSON::XS> do the escaping), for I-JSON, it's I<recommended> to encode binary data as base64. =back There are some other considerations - read RFC7493 for the details if interested. =head1 INTEROPERABILITY WITH OTHER MODULES C<JSON::XS> uses the L<Types::Serialiser> module to provide boolean constants. That means that the JSON true and false values will be comaptible to true and false values of other modules that do the same, such as L<JSON::PP> and L<CBOR::XS>. =head1 INTEROPERABILITY WITH OTHER JSON DECODERS As long as you only serialise data that can be directly expressed in JSON, C<JSON::XS> is incapable of generating invalid JSON output (modulo bugs, but C<JSON::XS> has found more bugs in the official JSON testsuite (1) than the official JSON testsuite has found in C<JSON::XS> (0)). When you have trouble decoding JSON generated by this module using other decoders, then it is very likely that you have an encoding mismatch or the other decoder is broken. When decoding, C<JSON::XS> is strict by default and will likely catch all errors. There are currently two settings that change this: C<relaxed> makes C<JSON::XS> accept (but not generate) some non-standard extensions, and C<allow_tags> will allow you to encode and decode Perl objects, at the cost of not outputting valid JSON anymore. =head2 TAGGED VALUE SYNTAX AND STANDARD JSON EN/DECODERS When you use C<allow_tags> to use the extended (and also nonstandard and invalid) JSON syntax for serialised objects, and you still want to decode the generated When you want to serialise objects, you can run a regex to replace the tagged syntax by standard JSON arrays (it only works for "normal" package names without comma, newlines or single colons). First, the readable Perl version: # if your FREEZE methods return no values, you need this replace first: $json =~ s/\( \s* (" (?: [^\\":,]+|\\.|::)* ") \s* \) \s* \[\s*\]/[$1]/gx; # this works for non-empty constructor arg lists: $json =~ s/\( \s* (" (?: [^\\":,]+|\\.|::)* ") \s* \) \s* \[/[$1,/gx; And here is a less readable version that is easy to adapt to other languages: $json =~ s/\(\s*("([^\\":,]+|\\.|::)*")\s*\)\s*\[/[$1,/g; Here is an ECMAScript version (same regex): json = json.replace (/\(\s*("([^\\":,]+|\\.|::)*")\s*\)\s*\[/g, "[$1,"); Since this syntax converts to standard JSON arrays, it might be hard to distinguish serialised objects from normal arrays. You can prepend a "magic number" as first array element to reduce chances of a collision: $json =~ s/\(\s*("([^\\":,]+|\\.|::)*")\s*\)\s*\[/["XU1peReLzT4ggEllLanBYq4G9VzliwKF",$1,/g; And after decoding the JSON text, you could walk the data structure looking for arrays with a first element of C<XU1peReLzT4ggEllLanBYq4G9VzliwKF>. The same approach can be used to create the tagged format with another encoder. First, you create an array with the magic string as first member, the classname as second, and constructor arguments last, encode it as part of your JSON structure, and then: $json =~ s/\[\s*"XU1peReLzT4ggEllLanBYq4G9VzliwKF"\s*,\s*("([^\\":,]+|\\.|::)*")\s*,/($1)[/g; Again, this has some limitations - the magic string must not be encoded with character escapes, and the constructor arguments must be non-empty. =head1 (I-)THREADS This module is I<not> guaranteed to be ithread (or MULTIPLICITY-) safe and there are no plans to change this. Note that perl's builtin so-called threads/ithreads are officially deprecated and should not be used. =head1 THE PERILS OF SETLOCALE Sometimes people avoid the Perl locale support and directly call the system's setlocale function with C<LC_ALL>. This breaks both perl and modules such as JSON::XS, as stringification of numbers no longer works correctly (e.g. C<$x = 0.1; print "$x"+1> might print C<1>, and JSON::XS might output illegal JSON as JSON::XS relies on perl to stringify numbers). The solution is simple: don't call C<setlocale>, or use it for only those categories you need, such as C<LC_MESSAGES> or C<LC_CTYPE>. If you need C<LC_NUMERIC>, you should enable it only around the code that actually needs it (avoiding stringification of numbers), and restore it afterwards. =head1 SOME HISTORY At the time this module was created there already were a number of JSON modules available on CPAN, so what was the reason to write yet another JSON module? While it seems there are many JSON modules, none of them correctly handled all corner cases, and in most cases their maintainers are unresponsive, gone missing, or not listening to bug reports for other reasons. Beginning with version 2.0 of the JSON module, when both JSON and JSON::XS are installed, then JSON will fall back on JSON::XS (this can be overridden) with no overhead due to emulation (by inheriting constructor and methods). If JSON::XS is not available, it will fall back to the compatible JSON::PP module as backend, so using JSON instead of JSON::XS gives you a portable JSON API that can be fast when you need it and doesn't require a C compiler when that is a problem. Somewhere around version 3, this module was forked into C<Cpanel::JSON::XS>, because its maintainer had serious trouble understanding JSON and insisted on a fork with many bugs "fixed" that weren't actually bugs, while spreading FUD about this module without actually giving any details on his accusations. You be the judge, but in my personal opinion, if you want quality, you will stay away from dangerous forks like that. =head1 BUGS While the goal of this module is to be correct, that unfortunately does not mean it's bug-free, only that I think its design is bug-free. If you keep reporting bugs they will be fixed swiftly, though. Please refrain from using rt.cpan.org or any other bug reporting service. I put the contact address into my modules for a reason. =cut BEGIN { *true = \$Types::Serialiser::true; *true = \&Types::Serialiser::true; *false = \$Types::Serialiser::false; *false = \&Types::Serialiser::false; *is_bool = \&Types::Serialiser::is_bool; *JSON::XS::Boolean:: = *Types::Serialiser::Boolean::; } XSLoader::load "JSON::XS", $VERSION; =head1 SEE ALSO The F<json_xs> command line utility for quick experiments. =head1 AUTHOR Marc Lehmann <schmorp@schmorp.de> http://home.schmorp.de/ =cut 1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������JSON/XS/Boolean.pm����������������������������������������������������������������������������������0000444�����������������00000001122�15130473315�0007560 0����������������������������������������������������������������������������������������������������ustar�00�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=head1 NAME JSON::XS::Boolean - dummy module providing JSON::XS::Boolean =head1 SYNOPSIS # do not "use" yourself =head1 DESCRIPTION This module exists only to provide overload resolution for Storable and similar modules. It's only needed for compatibility with data serialised (by other modules such as Storable) that was decoded by JSON::XS versions before 3.0. Since 3.0, JSON::PP::Boolean has replaced it. Support for JSON::XS::Boolean will be removed in a future release. =cut use JSON::XS (); 1; =head1 AUTHOR Marc Lehmann <schmorp@schmorp.de> http://home.schmorp.de/ =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������