[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 package CPANPLUS::Internals::Utils; 2 3 use strict; 4 5 use CPANPLUS::Error; 6 use CPANPLUS::Internals::Constants; 7 8 use Cwd; 9 use File::Copy; 10 use Params::Check qw[check]; 11 use Module::Load::Conditional qw[can_load]; 12 use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext'; 13 14 local $Params::Check::VERBOSE = 1; 15 16 =pod 17 18 =head1 NAME 19 20 CPANPLUS::Internals::Utils 21 22 =head1 SYNOPSIS 23 24 my $bool = $cb->_mkdir( dir => 'blah' ); 25 my $bool = $cb->_chdir( dir => 'blah' ); 26 my $bool = $cb->_rmdir( dir => 'blah' ); 27 28 my $bool = $cb->_move( from => '/some/file', to => '/other/file' ); 29 my $bool = $cb->_move( from => '/some/dir', to => '/other/dir' ); 30 31 my $cont = $cb->_get_file_contents( file => '/path/to/file' ); 32 33 34 my $version = $cb->_perl_version( perl => $^X ); 35 36 =head1 DESCRIPTION 37 38 C<CPANPLUS::Internals::Utils> holds a few convenience functions for 39 CPANPLUS libraries. 40 41 =head1 METHODS 42 43 =head2 $cb->_mkdir( dir => '/some/dir' ) 44 45 C<_mkdir> creates a full path to a directory. 46 47 Returns true on success, false on failure. 48 49 =cut 50 51 sub _mkdir { 52 my $self = shift; 53 54 my %hash = @_; 55 56 my $tmpl = { 57 dir => { required => 1 }, 58 }; 59 60 my $args = check( $tmpl, \%hash ) or ( 61 error(loc( Params::Check->last_error ) ), return 62 ); 63 64 unless( can_load( modules => { 'File::Path' => 0.0 } ) ) { 65 error( loc("Could not use File::Path! This module should be core!") ); 66 return; 67 } 68 69 eval { File::Path::mkpath($args->{dir}) }; 70 71 if($@) { 72 chomp($@); 73 error(loc(qq[Could not create directory '%1': %2], $args->{dir}, $@ )); 74 return; 75 } 76 77 return 1; 78 } 79 80 =pod 81 82 =head2 $cb->_chdir( dir => '/some/dir' ) 83 84 C<_chdir> changes directory to a dir. 85 86 Returns true on success, false on failure. 87 88 =cut 89 90 sub _chdir { 91 my $self = shift; 92 my %hash = @_; 93 94 my $tmpl = { 95 dir => { required => 1, allow => DIR_EXISTS }, 96 }; 97 98 my $args = check( $tmpl, \%hash ) or return; 99 100 unless( chdir $args->{dir} ) { 101 error( loc(q[Could not chdir into '%1'], $args->{dir}) ); 102 return; 103 } 104 105 return 1; 106 } 107 108 =pod 109 110 =head2 $cb->_rmdir( dir => '/some/dir' ); 111 112 Removes a directory completely, even if it is non-empty. 113 114 Returns true on success, false on failure. 115 116 =cut 117 118 sub _rmdir { 119 my $self = shift; 120 my %hash = @_; 121 122 my $tmpl = { 123 dir => { required => 1, allow => IS_DIR }, 124 }; 125 126 my $args = check( $tmpl, \%hash ) or return; 127 128 unless( can_load( modules => { 'File::Path' => 0.0 } ) ) { 129 error( loc("Could not use File::Path! This module should be core!") ); 130 return; 131 } 132 133 eval { File::Path::rmtree($args->{dir}) }; 134 135 if($@) { 136 chomp($@); 137 error(loc(qq[Could not delete directory '%1': %2], $args->{dir}, $@ )); 138 return; 139 } 140 141 return 1; 142 } 143 144 =pod 145 146 =head2 $cb->_perl_version ( perl => 'some/perl/binary' ); 147 148 C<_perl_version> returns the version of a certain perl binary. 149 It does this by actually running a command. 150 151 Returns the perl version on success and false on failure. 152 153 =cut 154 155 sub _perl_version { 156 my $self = shift; 157 my %hash = @_; 158 159 my $perl; 160 my $tmpl = { 161 perl => { required => 1, store => \$perl }, 162 }; 163 164 check( $tmpl, \%hash ) or return; 165 166 my $perl_version; 167 ### special perl, or the one we are running under? 168 if( $perl eq $^X ) { 169 ### just load the config 170 require Config; 171 $perl_version = $Config::Config{version}; 172 173 } else { 174 my $cmd = $perl . 175 ' -MConfig -eprint+Config::config_vars+version'; 176 ($perl_version) = (`$cmd` =~ /version='(.*)'/); 177 } 178 179 return $perl_version if defined $perl_version; 180 return; 181 } 182 183 =pod 184 185 =head2 $cb->_version_to_number( version => $version ); 186 187 Returns a proper module version, or '0.0' if none was available. 188 189 =cut 190 191 sub _version_to_number { 192 my $self = shift; 193 my %hash = @_; 194 195 my $version; 196 my $tmpl = { 197 version => { default => '0.0', store => \$version }, 198 }; 199 200 check( $tmpl, \%hash ) or return; 201 202 return $version if $version =~ /^\.?\d/; 203 return '0.0'; 204 } 205 206 =pod 207 208 =head2 $cb->_whoami 209 210 Returns the name of the subroutine you're currently in. 211 212 =cut 213 214 sub _whoami { my $name = (caller 1)[3]; $name =~ s/.+:://; $name } 215 216 =pod 217 218 =head2 _get_file_contents( file => $file ); 219 220 Returns the contents of a file 221 222 =cut 223 224 sub _get_file_contents { 225 my $self = shift; 226 my %hash = @_; 227 228 my $file; 229 my $tmpl = { 230 file => { required => 1, store => \$file } 231 }; 232 233 check( $tmpl, \%hash ) or return; 234 235 my $fh = OPEN_FILE->($file) or return; 236 my $contents = do { local $/; <$fh> }; 237 238 return $contents; 239 } 240 241 =pod $cb->_move( from => $file|$dir, to => $target ); 242 243 Moves a file or directory to the target. 244 245 Returns true on success, false on failure. 246 247 =cut 248 249 sub _move { 250 my $self = shift; 251 my %hash = @_; 252 253 my $from; my $to; 254 my $tmpl = { 255 file => { required => 1, allow => [IS_FILE,IS_DIR], 256 store => \$from }, 257 to => { required => 1, store => \$to } 258 }; 259 260 check( $tmpl, \%hash ) or return; 261 262 if( File::Copy::move( $from, $to ) ) { 263 return 1; 264 } else { 265 error(loc("Failed to move '%1' to '%2': %3", $from, $to, $!)); 266 return; 267 } 268 } 269 270 =pod $cb->_copy( from => $file|$dir, to => $target ); 271 272 Moves a file or directory to the target. 273 274 Returns true on success, false on failure. 275 276 =cut 277 278 sub _copy { 279 my $self = shift; 280 my %hash = @_; 281 282 my($from,$to); 283 my $tmpl = { 284 file =>{ required => 1, allow => [IS_FILE,IS_DIR], 285 store => \$from }, 286 to => { required => 1, store => \$to } 287 }; 288 289 check( $tmpl, \%hash ) or return; 290 291 if( File::Copy::copy( $from, $to ) ) { 292 return 1; 293 } else { 294 error(loc("Failed to copy '%1' to '%2': %3", $from, $to, $!)); 295 return; 296 } 297 } 298 299 =head2 $cb->_mode_plus_w( file => '/path/to/file' ); 300 301 Sets the +w bit for the file. 302 303 Returns true on success, false on failure. 304 305 =cut 306 307 sub _mode_plus_w { 308 my $self = shift; 309 my %hash = @_; 310 311 require File::stat; 312 313 my $file; 314 my $tmpl = { 315 file => { required => 1, allow => IS_FILE, store => \$file }, 316 }; 317 318 check( $tmpl, \%hash ) or return; 319 320 ### set the mode to +w for a file and +wx for a dir 321 my $x = File::stat::stat( $file ); 322 my $mask = -d $file ? 0100 : 0200; 323 324 if( $x and chmod( $x->mode|$mask, $file ) ) { 325 return 1; 326 327 } else { 328 error(loc("Failed to '%1' '%2': '%3'", 'chmod +w', $file, $!)); 329 return; 330 } 331 } 332 333 =head2 $uri = $cb->_host_to_uri( scheme => SCHEME, host => HOST, path => PATH ); 334 335 Turns a CPANPLUS::Config style C<host> entry into an URI string. 336 337 Returns the uri on success, and false on failure 338 339 =cut 340 341 sub _host_to_uri { 342 my $self = shift; 343 my %hash = @_; 344 345 my($scheme, $host, $path); 346 my $tmpl = { 347 scheme => { required => 1, store => \$scheme }, 348 host => { default => 'localhost', store => \$host }, 349 path => { default => '', store => \$path }, 350 }; 351 352 check( $tmpl, \%hash ) or return; 353 354 ### it's an URI, so unixify the path. 355 ### VMS has a special method for just that 356 $path = ON_VMS 357 ? VMS::Filespec::unixify($path) 358 : File::Spec::Unix->catdir( File::Spec->splitdir( $path ) ); 359 360 return "$scheme://" . File::Spec::Unix->catdir( $host, $path ); 361 } 362 363 =head2 $cb->_vcmp( VERSION, VERSION ); 364 365 Normalizes the versions passed and does a '<=>' on them, returning the result. 366 367 =cut 368 369 sub _vcmp { 370 my $self = shift; 371 my ($x, $y) = @_; 372 373 s/_//g foreach $x, $y; 374 375 return $x <=> $y; 376 } 377 378 =head2 $cb->_home_dir 379 380 Returns the user's homedir, or C<cwd> if it could not be found 381 382 =cut 383 384 sub _home_dir { 385 my @os_home_envs = qw( APPDATA HOME USERPROFILE WINDIR SYS$LOGIN ); 386 387 for my $env ( @os_home_envs ) { 388 next unless exists $ENV{ $env }; 389 next unless defined $ENV{ $env } && length $ENV{ $env }; 390 return $ENV{ $env } if -d $ENV{ $env }; 391 } 392 393 return cwd(); 394 } 395 396 =head2 $path = $cb->_safe_path( path => $path ); 397 398 Returns a path that's safe to us on Win32 and VMS. 399 400 Only cleans up the path on Win32 if the path exists. 401 402 On VMS, it encodes dots to _ using C<VMS::Filespec::vmsify> 403 404 =cut 405 406 sub _safe_path { 407 my $self = shift; 408 409 my %hash = @_; 410 411 my $path; 412 my $tmpl = { 413 path => { required => 1, store => \$path }, 414 }; 415 416 check( $tmpl, \%hash ) or return; 417 418 if( ON_WIN32 ) { 419 ### only need to fix it up if there's spaces in the path 420 return $path unless $path =~ /\s+/; 421 422 ### clean up paths if we are on win32 423 return Win32::GetShortPathName( $path ) || $path; 424 425 } elsif ( ON_VMS ) { 426 ### XXX According to John Malmberg, there's an VMS issue: 427 ### catdir on VMS can not currently deal with directory components 428 ### with dots in them. 429 ### Fixing this is a a three step procedure, which will work for 430 ### VMS in its traditional ODS-2 mode, and it will also work if 431 ### VMS is in the ODS-5 mode that is being implemented. 432 ### If the path is already in VMS syntax, assume that we are done. 433 434 ### VMS format is a path with a trailing ']' or ':' 435 return $path if $path =~ /\:|\]$/; 436 437 ### 1. Make sure that the value to be converted, $path is 438 ### in UNIX directory syntax by appending a '/' to it. 439 $path .= '/' unless $path =~ m|/$|; 440 441 ### 2. Use VMS::Filespec::vmsify($path . '/') to convert the dots to 442 ### underscores if needed. The trailing '/' is needed as so that 443 ### C<vmsify> knows that it should use directory translation instead of 444 ### filename translation, as filename translation leaves one dot. 445 $path = VMS::Filespec::vmsify( $path ); 446 447 ### 3. Use $path = File::Spec->splitdir( VMS::Filespec::vmsify( 448 ### $path . '/') to remove the directory delimiters. 449 450 ### From John Malmberg: 451 ### File::Spec->catdir will put the path back together. 452 ### The '/' trick only works if the string is a directory name 453 ### with UNIX style directory delimiters or no directory delimiters. 454 ### It is to force vmsify to treat the input specification as UNIX. 455 ### 456 ### There is a VMS::Filespec::unixpath() to do the appending of the '/' 457 ### to the specification, which will do a VMS::Filespec::vmsify() 458 ### if needed. 459 ### However it is not a good idea to call vmsify() on a pathname 460 ### returned by unixify(), and it is not a good idea to call unixify() 461 ### on a pathname returned by vmsify(). Because of the nature of the 462 ### conversion, not all file specifications can make the round trip. 463 ### 464 ### I think that directory specifications can safely make the round 465 ### trip, but not ones containing filenames. 466 $path = File::Spec->catdir( File::Spec->splitdir( $path ) ) 467 } 468 469 return $path; 470 } 471 472 473 =head2 ($pkg, $version, $ext) = $cb->_split_package_string( package => PACKAGE_STRING ); 474 475 Splits the name of a CPAN package string up in it's package, version 476 and extension parts. 477 478 For example, C<Foo-Bar-1.2.tar.gz> would return the following parts: 479 480 Package: Foo-Bar 481 Version: 1.2 482 Extension: tar.gz 483 484 =cut 485 486 { my $del_re = qr/[-_\+]/i; # delimiter between elements 487 my $pkg_re = qr/[a-z] # any letters followed by 488 [a-z\d]* # any letters, numbers 489 (?i:\.pm)? # followed by '.pm'--authors do this :( 490 (?: # optionally repeating: 491 $del_re # followed by a delimiter 492 [a-z] # any letters followed by 493 [a-z\d]* # any letters, numbers 494 (?i:\.pm)? # followed by '.pm'--authors do this :( 495 )* 496 /xi; 497 498 my $ver_re = qr/[a-z]*\d+[a-z]* # contains a digit and possibly letters 499 (?: 500 [-._] # followed by a delimiter 501 [a-z\d]+ # and more digits and or letters 502 )*? 503 /xi; 504 505 my $ext_re = qr/[a-z] # a letter, followed by 506 [a-z\d]* # letters and or digits, optionally 507 (?: 508 \. # followed by a dot and letters 509 [a-z\d]+ # and or digits (like .tar.bz2) 510 )? # optionally 511 /xi; 512 513 my $ver_ext_re = qr/ 514 ($ver_re+) # version, optional 515 (?: 516 \. # a literal . 517 ($ext_re) # extension, 518 )? # optional, but requires version 519 /xi; 520 521 ### composed regex for CPAN packages 522 my $full_re = qr/ 523 ^ 524 ($pkg_re+) # package 525 (?: 526 $del_re # delimiter 527 $ver_ext_re # version + extension 528 )? 529 $ 530 /xi; 531 532 ### composed regex for perl packages 533 my $perl = PERL_CORE; 534 my $perl_re = qr/ 535 ^ 536 ($perl) # package name for 'perl' 537 (?: 538 $ver_ext_re # version + extension 539 )? 540 $ 541 /xi; 542 543 544 sub _split_package_string { 545 my $self = shift; 546 my %hash = @_; 547 548 my $str; 549 my $tmpl = { package => { required => 1, store => \$str } }; 550 check( $tmpl, \%hash ) or return; 551 552 553 ### 2 different regexes, one for the 'perl' package, 554 ### one for ordinary CPAN packages.. try them both, 555 ### first match wins. 556 for my $re ( $full_re, $perl_re ) { 557 558 ### try the next if the match fails 559 $str =~ $re or next; 560 561 my $pkg = $1 || ''; 562 my $ver = $2 || ''; 563 my $ext = $3 || ''; 564 565 ### this regex resets the capture markers! 566 ### strip the trailing delimiter 567 $pkg =~ s/$del_re$//; 568 569 ### strip the .pm package suffix some authors insist on adding 570 $pkg =~ s/\.pm$//i; 571 572 return ($pkg, $ver, $ext ); 573 } 574 575 return; 576 } 577 } 578 579 { my %escapes = map { 580 chr($_) => sprintf("%%%02X", $_) 581 } 0 .. 255; 582 583 sub _uri_encode { 584 my $self = shift; 585 my %hash = @_; 586 587 my $str; 588 my $tmpl = { 589 uri => { store => \$str, required => 1 } 590 }; 591 592 check( $tmpl, \%hash ) or return; 593 594 ### XXX taken straight from URI::Encode 595 ### Default unsafe characters. RFC 2732 ^(uric - reserved) 596 $str =~ s|([^A-Za-z0-9\-_.!~*'()])|$escapes{$1}|g; 597 598 return $str; 599 } 600 601 602 sub _uri_decode { 603 my $self = shift; 604 my %hash = @_; 605 606 my $str; 607 my $tmpl = { 608 uri => { store => \$str, required => 1 } 609 }; 610 611 check( $tmpl, \%hash ) or return; 612 613 ### XXX use unencode routine in utils? 614 $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; 615 616 return $str; 617 } 618 } 619 620 sub _update_timestamp { 621 my $self = shift; 622 my %hash = @_; 623 624 my $file; 625 my $tmpl = { 626 file => { required => 1, store => \$file, allow => FILE_EXISTS } 627 }; 628 629 check( $tmpl, \%hash ) or return; 630 631 ### `touch` the file, so windoze knows it's new -jmb 632 ### works on *nix too, good fix -Kane 633 ### make sure it is writable first, otherwise the `touch` will fail 634 635 my $now = time; 636 unless( chmod( 0644, $file) && utime ($now, $now, $file) ) { 637 error( loc("Couldn't touch %1", $file) ); 638 return; 639 } 640 641 return 1; 642 } 643 644 645 1; 646 647 # Local variables: 648 # c-indentation-style: bsd 649 # c-basic-offset: 4 650 # indent-tabs-mode: nil 651 # End: 652 # vim: expandtab shiftwidth=4:
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |