1<?php 2/** 3 * PEAR_Command_Package (package, package-validate, cvsdiff, cvstag, package-dependencies, 4 * sign, makerpm, convert commands) 5 * 6 * PHP versions 4 and 5 7 * 8 * @category pear 9 * @package PEAR 10 * @author Stig Bakken <ssb@php.net> 11 * @author Martin Jansen <mj@php.net> 12 * @author Greg Beaver <cellog@php.net> 13 * @copyright 1997-2009 The Authors 14 * @license http://opensource.org/licenses/bsd-license.php New BSD License 15 * @link http://pear.php.net/package/PEAR 16 * @since File available since Release 0.1 17 */ 18 19/** 20 * base class 21 */ 22require_once 'PEAR/Command/Common.php'; 23 24/** 25 * PEAR commands for login/logout 26 * 27 * @category pear 28 * @package PEAR 29 * @author Stig Bakken <ssb@php.net> 30 * @author Martin Jansen <mj@php.net> 31 * @author Greg Beaver <cellog@php.net> 32 * @copyright 1997-2009 The Authors 33 * @license http://opensource.org/licenses/bsd-license.php New BSD License 34 * @version Release: @package_version@ 35 * @link http://pear.php.net/package/PEAR 36 * @since Class available since Release 0.1 37 */ 38 39class PEAR_Command_Package extends PEAR_Command_Common 40{ 41 var $commands = array( 42 'package' => array( 43 'summary' => 'Build Package', 44 'function' => 'doPackage', 45 'shortcut' => 'p', 46 'options' => array( 47 'nocompress' => array( 48 'shortopt' => 'Z', 49 'doc' => 'Do not gzip the package file' 50 ), 51 'showname' => array( 52 'shortopt' => 'n', 53 'doc' => 'Print the name of the packaged file.', 54 ), 55 ), 56 'doc' => '[descfile] [descfile2] 57Creates a PEAR package from its description file (usually called 58package.xml). If a second packagefile is passed in, then 59the packager will check to make sure that one is a package.xml 60version 1.0, and the other is a package.xml version 2.0. The 61package.xml version 1.0 will be saved as "package.xml" in the archive, 62and the other as "package2.xml" in the archive" 63' 64 ), 65 'package-validate' => array( 66 'summary' => 'Validate Package Consistency', 67 'function' => 'doPackageValidate', 68 'shortcut' => 'pv', 69 'options' => array(), 70 'doc' => ' 71', 72 ), 73 'cvsdiff' => array( 74 'summary' => 'Run a "cvs diff" for all files in a package', 75 'function' => 'doCvsDiff', 76 'shortcut' => 'cd', 77 'options' => array( 78 'quiet' => array( 79 'shortopt' => 'q', 80 'doc' => 'Be quiet', 81 ), 82 'reallyquiet' => array( 83 'shortopt' => 'Q', 84 'doc' => 'Be really quiet', 85 ), 86 'date' => array( 87 'shortopt' => 'D', 88 'doc' => 'Diff against revision of DATE', 89 'arg' => 'DATE', 90 ), 91 'release' => array( 92 'shortopt' => 'R', 93 'doc' => 'Diff against tag for package release REL', 94 'arg' => 'REL', 95 ), 96 'revision' => array( 97 'shortopt' => 'r', 98 'doc' => 'Diff against revision REV', 99 'arg' => 'REV', 100 ), 101 'context' => array( 102 'shortopt' => 'c', 103 'doc' => 'Generate context diff', 104 ), 105 'unified' => array( 106 'shortopt' => 'u', 107 'doc' => 'Generate unified diff', 108 ), 109 'ignore-case' => array( 110 'shortopt' => 'i', 111 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent', 112 ), 113 'ignore-whitespace' => array( 114 'shortopt' => 'b', 115 'doc' => 'Ignore changes in amount of white space', 116 ), 117 'ignore-blank-lines' => array( 118 'shortopt' => 'B', 119 'doc' => 'Ignore changes that insert or delete blank lines', 120 ), 121 'brief' => array( 122 'doc' => 'Report only whether the files differ, no details', 123 ), 124 'dry-run' => array( 125 'shortopt' => 'n', 126 'doc' => 'Don\'t do anything, just pretend', 127 ), 128 ), 129 'doc' => '<package.xml> 130Compares all the files in a package. Without any options, this 131command will compare the current code with the last checked-in code. 132Using the -r or -R option you may compare the current code with that 133of a specific release. 134', 135 ), 136 'svntag' => array( 137 'summary' => 'Set SVN Release Tag', 138 'function' => 'doSvnTag', 139 'shortcut' => 'sv', 140 'options' => array( 141 'quiet' => array( 142 'shortopt' => 'q', 143 'doc' => 'Be quiet', 144 ), 145 'slide' => array( 146 'shortopt' => 'F', 147 'doc' => 'Move (slide) tag if it exists', 148 ), 149 'delete' => array( 150 'shortopt' => 'd', 151 'doc' => 'Remove tag', 152 ), 153 'dry-run' => array( 154 'shortopt' => 'n', 155 'doc' => 'Don\'t do anything, just pretend', 156 ), 157 ), 158 'doc' => '<package.xml> [files...] 159 Sets a SVN tag on all files in a package. Use this command after you have 160 packaged a distribution tarball with the "package" command to tag what 161 revisions of what files were in that release. If need to fix something 162 after running svntag once, but before the tarball is released to the public, 163 use the "slide" option to move the release tag. 164 165 to include files (such as a second package.xml, or tests not included in the 166 release), pass them as additional parameters. 167 ', 168 ), 169 'cvstag' => array( 170 'summary' => 'Set CVS Release Tag', 171 'function' => 'doCvsTag', 172 'shortcut' => 'ct', 173 'options' => array( 174 'quiet' => array( 175 'shortopt' => 'q', 176 'doc' => 'Be quiet', 177 ), 178 'reallyquiet' => array( 179 'shortopt' => 'Q', 180 'doc' => 'Be really quiet', 181 ), 182 'slide' => array( 183 'shortopt' => 'F', 184 'doc' => 'Move (slide) tag if it exists', 185 ), 186 'delete' => array( 187 'shortopt' => 'd', 188 'doc' => 'Remove tag', 189 ), 190 'dry-run' => array( 191 'shortopt' => 'n', 192 'doc' => 'Don\'t do anything, just pretend', 193 ), 194 ), 195 'doc' => '<package.xml> [files...] 196Sets a CVS tag on all files in a package. Use this command after you have 197packaged a distribution tarball with the "package" command to tag what 198revisions of what files were in that release. If need to fix something 199after running cvstag once, but before the tarball is released to the public, 200use the "slide" option to move the release tag. 201 202to include files (such as a second package.xml, or tests not included in the 203release), pass them as additional parameters. 204', 205 ), 206 'package-dependencies' => array( 207 'summary' => 'Show package dependencies', 208 'function' => 'doPackageDependencies', 209 'shortcut' => 'pd', 210 'options' => array(), 211 'doc' => '<package-file> or <package.xml> or <install-package-name> 212List all dependencies the package has. 213Can take a tgz / tar file, package.xml or a package name of an installed package.' 214 ), 215 'sign' => array( 216 'summary' => 'Sign a package distribution file', 217 'function' => 'doSign', 218 'shortcut' => 'si', 219 'options' => array( 220 'verbose' => array( 221 'shortopt' => 'v', 222 'doc' => 'Display GnuPG output', 223 ), 224 ), 225 'doc' => '<package-file> 226Signs a package distribution (.tar or .tgz) file with GnuPG.', 227 ), 228 'makerpm' => array( 229 'summary' => 'Builds an RPM spec file from a PEAR package', 230 'function' => 'doMakeRPM', 231 'shortcut' => 'rpm', 232 'options' => array( 233 'spec-template' => array( 234 'shortopt' => 't', 235 'arg' => 'FILE', 236 'doc' => 'Use FILE as RPM spec file template' 237 ), 238 'rpm-pkgname' => array( 239 'shortopt' => 'p', 240 'arg' => 'FORMAT', 241 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced 242by the PEAR package name, defaults to "PEAR::%s".', 243 ), 244 ), 245 'doc' => '<package-file> 246 247Creates an RPM .spec file for wrapping a PEAR package inside an RPM 248package. Intended to be used from the SPECS directory, with the PEAR 249package tarball in the SOURCES directory: 250 251$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz 252Wrote RPM spec file PEAR::Net_Geo-1.0.spec 253$ rpm -bb PEAR::Net_Socket-1.0.spec 254... 255Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm 256', 257 ), 258 'convert' => array( 259 'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format', 260 'function' => 'doConvert', 261 'shortcut' => 'c2', 262 'options' => array( 263 'flat' => array( 264 'shortopt' => 'f', 265 'doc' => 'do not beautify the filelist.', 266 ), 267 ), 268 'doc' => '[descfile] [descfile2] 269Converts a package.xml in 1.0 format into a package.xml 270in 2.0 format. The new file will be named package2.xml by default, 271and package.xml will be used as the old file by default. 272This is not the most intelligent conversion, and should only be 273used for automated conversion or learning the format. 274' 275 ), 276 ); 277 278 var $output; 279 280 /** 281 * PEAR_Command_Package constructor. 282 * 283 * @access public 284 */ 285 function __construct(&$ui, &$config) 286 { 287 parent::__construct($ui, $config); 288 } 289 290 function _displayValidationResults($err, $warn, $strict = false) 291 { 292 foreach ($err as $e) { 293 $this->output .= "Error: $e\n"; 294 } 295 foreach ($warn as $w) { 296 $this->output .= "Warning: $w\n"; 297 } 298 $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n", 299 sizeof($err), sizeof($warn)); 300 if ($strict && count($err) > 0) { 301 $this->output .= "Fix these errors and try again."; 302 return false; 303 } 304 return true; 305 } 306 307 function &getPackager() 308 { 309 if (!class_exists('PEAR_Packager')) { 310 require_once 'PEAR/Packager.php'; 311 } 312 $a = new PEAR_Packager; 313 return $a; 314 } 315 316 function &getPackageFile($config, $debug = false) 317 { 318 if (!class_exists('PEAR_Common')) { 319 require_once 'PEAR/Common.php'; 320 } 321 if (!class_exists('PEAR_PackageFile')) { 322 require_once 'PEAR/PackageFile.php'; 323 } 324 $a = new PEAR_PackageFile($config, $debug); 325 $common = new PEAR_Common; 326 $common->ui = $this->ui; 327 $a->setLogger($common); 328 return $a; 329 } 330 331 function doPackage($command, $options, $params) 332 { 333 $this->output = ''; 334 $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml'; 335 $pkg2 = isset($params[1]) ? $params[1] : null; 336 if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) { 337 $pkg2 = 'package2.xml'; 338 } 339 340 $packager = &$this->getPackager(); 341 $compress = empty($options['nocompress']) ? true : false; 342 $result = $packager->package($pkginfofile, $compress, $pkg2); 343 if (PEAR::isError($result)) { 344 return $this->raiseError($result); 345 } 346 347 // Don't want output, only the package file name just created 348 if (isset($options['showname'])) { 349 $this->output = $result; 350 } 351 352 if ($this->output) { 353 $this->ui->outputData($this->output, $command); 354 } 355 356 return true; 357 } 358 359 function doPackageValidate($command, $options, $params) 360 { 361 $this->output = ''; 362 if (count($params) < 1) { 363 $params[0] = 'package.xml'; 364 } 365 366 $obj = &$this->getPackageFile($this->config, $this->_debug); 367 $obj->rawReturn(); 368 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 369 $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); 370 if (PEAR::isError($info)) { 371 $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL); 372 } else { 373 $archive = $info->getArchiveFile(); 374 $tar = new Archive_Tar($archive); 375 $tar->extract(dirname($info->getPackageFile())); 376 $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR . 377 $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR . 378 basename($info->getPackageFile())); 379 } 380 381 PEAR::staticPopErrorHandling(); 382 if (PEAR::isError($info)) { 383 return $this->raiseError($info); 384 } 385 386 $valid = false; 387 if ($info->getPackagexmlVersion() == '2.0') { 388 if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) { 389 $info->flattenFileList(); 390 $valid = $info->validate(PEAR_VALIDATE_PACKAGING); 391 } 392 } else { 393 $valid = $info->validate(PEAR_VALIDATE_PACKAGING); 394 } 395 396 $err = $warn = array(); 397 if ($errors = $info->getValidationWarnings()) { 398 foreach ($errors as $error) { 399 if ($error['level'] == 'warning') { 400 $warn[] = $error['message']; 401 } else { 402 $err[] = $error['message']; 403 } 404 } 405 } 406 407 $this->_displayValidationResults($err, $warn); 408 $this->ui->outputData($this->output, $command); 409 return true; 410 } 411 412 function doSvnTag($command, $options, $params) 413 { 414 $this->output = ''; 415 $_cmd = $command; 416 if (count($params) < 1) { 417 $help = $this->getHelp($command); 418 return $this->raiseError("$command: missing parameter: $help[0]"); 419 } 420 421 $packageFile = realpath($params[0]); 422 $dir = dirname($packageFile); 423 $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); 424 $obj = &$this->getPackageFile($this->config, $this->_debug); 425 $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); 426 if (PEAR::isError($info)) { 427 return $this->raiseError($info); 428 } 429 430 $err = $warn = array(); 431 if (!$info->validate()) { 432 foreach ($info->getValidationWarnings() as $error) { 433 if ($error['level'] == 'warning') { 434 $warn[] = $error['message']; 435 } else { 436 $err[] = $error['message']; 437 } 438 } 439 } 440 441 if (!$this->_displayValidationResults($err, $warn, true)) { 442 $this->ui->outputData($this->output, $command); 443 return $this->raiseError('SVN tag failed'); 444 } 445 446 $version = $info->getVersion(); 447 $package = $info->getName(); 448 $svntag = "$package-$version"; 449 450 if (isset($options['delete'])) { 451 return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); 452 } 453 454 $path = $this->_svnFindPath($packageFile); 455 456 // Check if there are any modified files 457 $fp = popen('svn st --xml ' . dirname($packageFile), "r"); 458 $out = ''; 459 while ($line = fgets($fp, 1024)) { 460 $out .= rtrim($line)."\n"; 461 } 462 pclose($fp); 463 464 if (!isset($options['quiet']) && strpos($out, 'item="modified"')) { 465 $params = array(array( 466 'name' => 'modified', 467 'type' => 'yesno', 468 'default' => 'no', 469 'prompt' => 'You have files in your SVN checkout (' . $path['from'] . ') that have been modified but not committed, do you still want to tag ' . $version . '?', 470 )); 471 $answers = $this->ui->confirmDialog($params); 472 473 if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) { 474 return true; 475 } 476 } 477 478 if (isset($options['slide'])) { 479 $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); 480 } 481 482 // Check if tag already exists 483 $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag; 484 $existsCommand = 'svn ls ' . $path['base'] . 'tags/'; 485 486 $fp = popen($existsCommand, "r"); 487 $out = ''; 488 while ($line = fgets($fp, 1024)) { 489 $out .= rtrim($line)."\n"; 490 } 491 pclose($fp); 492 493 if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) { 494 $this->ui->outputData($this->output, $command); 495 return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.'); 496 } elseif (file_exists($path['local']['base'] . 'tags') === false) { 497 return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags'); 498 } elseif (is_writeable($path['local']['base'] . 'tags') === false) { 499 return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags'); 500 } else { 501 $makeCommand = 'svn mkdir ' . $releaseTag; 502 $this->output .= "+ $makeCommand\n"; 503 if (empty($options['dry-run'])) { 504 // We need to create the tag dir. 505 $fp = popen($makeCommand, "r"); 506 $out = ''; 507 while ($line = fgets($fp, 1024)) { 508 $out .= rtrim($line)."\n"; 509 } 510 pclose($fp); 511 $this->output .= "$out\n"; 512 } 513 } 514 515 $command = 'svn'; 516 if (isset($options['quiet'])) { 517 $command .= ' -q'; 518 } 519 520 $command .= ' copy --parents '; 521 522 $dir = dirname($packageFile); 523 $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); 524 $files = array_keys($info->getFilelist()); 525 if (!in_array(basename($packageFile), $files)) { 526 $files[] = basename($packageFile); 527 } 528 529 array_shift($params); 530 if (count($params)) { 531 // add in additional files to be tagged (package files and such) 532 $files = array_merge($files, $params); 533 } 534 535 $commands = array(); 536 foreach ($files as $file) { 537 if (!file_exists($file)) { 538 $file = $dir . DIRECTORY_SEPARATOR . $file; 539 } 540 $commands[] = $command . ' ' . escapeshellarg($file) . ' ' . 541 escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file); 542 } 543 544 $this->output .= implode("\n", $commands) . "\n"; 545 if (empty($options['dry-run'])) { 546 foreach ($commands as $command) { 547 $fp = popen($command, "r"); 548 while ($line = fgets($fp, 1024)) { 549 $this->output .= rtrim($line)."\n"; 550 } 551 pclose($fp); 552 } 553 } 554 555 $command = 'svn ci -m "Tagging the ' . $version . ' release" ' . $releaseTag . "\n"; 556 $this->output .= "+ $command\n"; 557 if (empty($options['dry-run'])) { 558 $fp = popen($command, "r"); 559 while ($line = fgets($fp, 1024)) { 560 $this->output .= rtrim($line)."\n"; 561 } 562 pclose($fp); 563 } 564 565 $this->ui->outputData($this->output, $_cmd); 566 return true; 567 } 568 569 function _svnFindPath($file) 570 { 571 $xml = ''; 572 $command = "svn info --xml $file"; 573 $fp = popen($command, "r"); 574 while ($line = fgets($fp, 1024)) { 575 $xml .= rtrim($line)."\n"; 576 } 577 pclose($fp); 578 $url_tag = strpos($xml, '<url>'); 579 $url = substr($xml, $url_tag + 5, strpos($xml, '</url>', $url_tag + 5) - ($url_tag + 5)); 580 581 $path = array(); 582 $path['from'] = substr($url, 0, strrpos($url, '/')); 583 $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1); 584 585 // Figure out the local paths - see http://pear.php.net/bugs/17463 586 $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR); 587 if ($pos === false) { 588 $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR); 589 } 590 $path['local']['base'] = substr($file, 0, $pos + 1); 591 592 return $path; 593 } 594 595 function _svnRemoveTag($version, $package, $tag, $packageFile, $options) 596 { 597 $command = 'svn'; 598 599 if (isset($options['quiet'])) { 600 $command .= ' -q'; 601 } 602 603 $command .= ' remove'; 604 $command .= ' -m "Removing tag for the ' . $version . ' release."'; 605 606 $path = $this->_svnFindPath($packageFile); 607 $command .= ' ' . $path['base'] . 'tags/' . $tag; 608 609 610 if ($this->config->get('verbose') > 1) { 611 $this->output .= "+ $command\n"; 612 } 613 614 $this->output .= "+ $command\n"; 615 if (empty($options['dry-run'])) { 616 $fp = popen($command, "r"); 617 while ($line = fgets($fp, 1024)) { 618 $this->output .= rtrim($line)."\n"; 619 } 620 pclose($fp); 621 } 622 623 $this->ui->outputData($this->output, $command); 624 return true; 625 } 626 627 function doCvsTag($command, $options, $params) 628 { 629 $this->output = ''; 630 $_cmd = $command; 631 if (count($params) < 1) { 632 $help = $this->getHelp($command); 633 return $this->raiseError("$command: missing parameter: $help[0]"); 634 } 635 636 $packageFile = realpath($params[0]); 637 $obj = &$this->getPackageFile($this->config, $this->_debug); 638 $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); 639 if (PEAR::isError($info)) { 640 return $this->raiseError($info); 641 } 642 643 $err = $warn = array(); 644 if (!$info->validate()) { 645 foreach ($info->getValidationWarnings() as $error) { 646 if ($error['level'] == 'warning') { 647 $warn[] = $error['message']; 648 } else { 649 $err[] = $error['message']; 650 } 651 } 652 } 653 654 if (!$this->_displayValidationResults($err, $warn, true)) { 655 $this->ui->outputData($this->output, $command); 656 return $this->raiseError('CVS tag failed'); 657 } 658 659 $version = $info->getVersion(); 660 $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version); 661 $cvstag = "RELEASE_$cvsversion"; 662 $files = array_keys($info->getFilelist()); 663 $command = 'cvs'; 664 if (isset($options['quiet'])) { 665 $command .= ' -q'; 666 } 667 668 if (isset($options['reallyquiet'])) { 669 $command .= ' -Q'; 670 } 671 672 $command .= ' tag'; 673 if (isset($options['slide'])) { 674 $command .= ' -F'; 675 } 676 677 if (isset($options['delete'])) { 678 $command .= ' -d'; 679 } 680 681 $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]); 682 array_shift($params); 683 if (count($params)) { 684 // add in additional files to be tagged 685 $files = array_merge($files, $params); 686 } 687 688 $dir = dirname($packageFile); 689 $dir = substr($dir, strrpos($dir, '/') + 1); 690 foreach ($files as $file) { 691 if (!file_exists($file)) { 692 $file = $dir . DIRECTORY_SEPARATOR . $file; 693 } 694 $command .= ' ' . escapeshellarg($file); 695 } 696 697 if ($this->config->get('verbose') > 1) { 698 $this->output .= "+ $command\n"; 699 } 700 701 $this->output .= "+ $command\n"; 702 if (empty($options['dry-run'])) { 703 $fp = popen($command, "r"); 704 while ($line = fgets($fp, 1024)) { 705 $this->output .= rtrim($line)."\n"; 706 } 707 pclose($fp); 708 } 709 710 $this->ui->outputData($this->output, $_cmd); 711 return true; 712 } 713 714 function doCvsDiff($command, $options, $params) 715 { 716 $this->output = ''; 717 if (sizeof($params) < 1) { 718 $help = $this->getHelp($command); 719 return $this->raiseError("$command: missing parameter: $help[0]"); 720 } 721 722 $file = realpath($params[0]); 723 $obj = &$this->getPackageFile($this->config, $this->_debug); 724 $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL); 725 if (PEAR::isError($info)) { 726 return $this->raiseError($info); 727 } 728 729 $err = $warn = array(); 730 if (!$info->validate()) { 731 foreach ($info->getValidationWarnings() as $error) { 732 if ($error['level'] == 'warning') { 733 $warn[] = $error['message']; 734 } else { 735 $err[] = $error['message']; 736 } 737 } 738 } 739 740 if (!$this->_displayValidationResults($err, $warn, true)) { 741 $this->ui->outputData($this->output, $command); 742 return $this->raiseError('CVS diff failed'); 743 } 744 745 $info1 = $info->getFilelist(); 746 $files = $info1; 747 $cmd = "cvs"; 748 if (isset($options['quiet'])) { 749 $cmd .= ' -q'; 750 unset($options['quiet']); 751 } 752 753 if (isset($options['reallyquiet'])) { 754 $cmd .= ' -Q'; 755 unset($options['reallyquiet']); 756 } 757 758 if (isset($options['release'])) { 759 $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']); 760 $cvstag = "RELEASE_$cvsversion"; 761 $options['revision'] = $cvstag; 762 unset($options['release']); 763 } 764 765 $execute = true; 766 if (isset($options['dry-run'])) { 767 $execute = false; 768 unset($options['dry-run']); 769 } 770 771 $cmd .= ' diff'; 772 // the rest of the options are passed right on to "cvs diff" 773 foreach ($options as $option => $optarg) { 774 $arg = $short = false; 775 if (isset($this->commands[$command]['options'][$option])) { 776 $arg = $this->commands[$command]['options'][$option]['arg']; 777 $short = $this->commands[$command]['options'][$option]['shortopt']; 778 } 779 $cmd .= $short ? " -$short" : " --$option"; 780 if ($arg && $optarg) { 781 $cmd .= ($short ? '' : '=') . escapeshellarg($optarg); 782 } 783 } 784 785 foreach ($files as $file) { 786 $cmd .= ' ' . escapeshellarg($file['name']); 787 } 788 789 if ($this->config->get('verbose') > 1) { 790 $this->output .= "+ $cmd\n"; 791 } 792 793 if ($execute) { 794 $fp = popen($cmd, "r"); 795 while ($line = fgets($fp, 1024)) { 796 $this->output .= rtrim($line)."\n"; 797 } 798 pclose($fp); 799 } 800 801 $this->ui->outputData($this->output, $command); 802 return true; 803 } 804 805 function doPackageDependencies($command, $options, $params) 806 { 807 // $params[0] -> the PEAR package to list its information 808 if (count($params) !== 1) { 809 return $this->raiseError("bad parameter(s), try \"help $command\""); 810 } 811 812 $obj = &$this->getPackageFile($this->config, $this->_debug); 813 if (is_file($params[0]) || strpos($params[0], '.xml') > 0) { 814 $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); 815 } else { 816 $reg = $this->config->getRegistry(); 817 $info = $obj->fromArray($reg->packageInfo($params[0])); 818 } 819 820 if (PEAR::isError($info)) { 821 return $this->raiseError($info); 822 } 823 824 $deps = $info->getDeps(); 825 if (is_array($deps)) { 826 if ($info->getPackagexmlVersion() == '1.0') { 827 $data = array( 828 'caption' => 'Dependencies for pear/' . $info->getPackage(), 829 'border' => true, 830 'headline' => array("Required?", "Type", "Name", "Relation", "Version"), 831 ); 832 833 foreach ($deps as $d) { 834 if (isset($d['optional'])) { 835 if ($d['optional'] == 'yes') { 836 $req = 'No'; 837 } else { 838 $req = 'Yes'; 839 } 840 } else { 841 $req = 'Yes'; 842 } 843 844 if (isset($this->_deps_rel_trans[$d['rel']])) { 845 $rel = $this->_deps_rel_trans[$d['rel']]; 846 } else { 847 $rel = $d['rel']; 848 } 849 850 if (isset($this->_deps_type_trans[$d['type']])) { 851 $type = ucfirst($this->_deps_type_trans[$d['type']]); 852 } else { 853 $type = $d['type']; 854 } 855 856 if (isset($d['name'])) { 857 $name = $d['name']; 858 } else { 859 $name = ''; 860 } 861 862 if (isset($d['version'])) { 863 $version = $d['version']; 864 } else { 865 $version = ''; 866 } 867 868 $data['data'][] = array($req, $type, $name, $rel, $version); 869 } 870 } else { // package.xml 2.0 dependencies display 871 require_once 'PEAR/Dependency2.php'; 872 $deps = $info->getDependencies(); 873 $reg = &$this->config->getRegistry(); 874 if (is_array($deps)) { 875 $data = array( 876 'caption' => 'Dependencies for ' . $info->getPackage(), 877 'border' => true, 878 'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'), 879 ); 880 foreach ($deps as $type => $subd) { 881 $req = ($type == 'required') ? 'Yes' : 'No'; 882 if ($type == 'group' && isset($subd['attribs']['name'])) { 883 $group = $subd['attribs']['name']; 884 } else { 885 $group = ''; 886 } 887 888 if (!isset($subd[0])) { 889 $subd = array($subd); 890 } 891 892 foreach ($subd as $groupa) { 893 foreach ($groupa as $deptype => $depinfo) { 894 if ($deptype == 'attribs') { 895 continue; 896 } 897 898 if ($deptype == 'pearinstaller') { 899 $deptype = 'pear Installer'; 900 } 901 902 if (!isset($depinfo[0])) { 903 $depinfo = array($depinfo); 904 } 905 906 foreach ($depinfo as $inf) { 907 $name = ''; 908 if (isset($inf['channel'])) { 909 $alias = $reg->channelAlias($inf['channel']); 910 if (!$alias) { 911 $alias = '(channel?) ' .$inf['channel']; 912 } 913 $name = $alias . '/'; 914 915 } 916 if (isset($inf['name'])) { 917 $name .= $inf['name']; 918 } elseif (isset($inf['pattern'])) { 919 $name .= $inf['pattern']; 920 } else { 921 $name .= ''; 922 } 923 924 if (isset($inf['uri'])) { 925 $name .= ' [' . $inf['uri'] . ']'; 926 } 927 928 if (isset($inf['conflicts'])) { 929 $ver = 'conflicts'; 930 } else { 931 $ver = PEAR_Dependency2::_getExtraString($inf); 932 } 933 934 $data['data'][] = array($req, ucfirst($deptype), $name, 935 $ver, $group); 936 } 937 } 938 } 939 } 940 } 941 } 942 943 $this->ui->outputData($data, $command); 944 return true; 945 } 946 947 // Fallback 948 $this->ui->outputData("This package does not have any dependencies.", $command); 949 } 950 951 function doSign($command, $options, $params) 952 { 953 // should move most of this code into PEAR_Packager 954 // so it'll be easy to implement "pear package --sign" 955 if (count($params) !== 1) { 956 return $this->raiseError("bad parameter(s), try \"help $command\""); 957 } 958 959 require_once 'System.php'; 960 require_once 'Archive/Tar.php'; 961 962 if (!file_exists($params[0])) { 963 return $this->raiseError("file does not exist: $params[0]"); 964 } 965 966 $obj = $this->getPackageFile($this->config, $this->_debug); 967 $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); 968 if (PEAR::isError($info)) { 969 return $this->raiseError($info); 970 } 971 972 $tar = new Archive_Tar($params[0]); 973 974 $tmpdir = $this->config->get('temp_dir'); 975 $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign'); 976 if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) { 977 return $this->raiseError("failed to extract tar file"); 978 } 979 980 if (file_exists("$tmpdir/package.sig")) { 981 return $this->raiseError("package already signed"); 982 } 983 984 $packagexml = 'package.xml'; 985 if (file_exists("$tmpdir/package2.xml")) { 986 $packagexml = 'package2.xml'; 987 } 988 989 if (file_exists("$tmpdir/package.sig")) { 990 unlink("$tmpdir/package.sig"); 991 } 992 993 if (!file_exists("$tmpdir/$packagexml")) { 994 return $this->raiseError("Extracted file $tmpdir/$packagexml not found."); 995 } 996 997 $input = $this->ui->userDialog($command, 998 array('GnuPG Passphrase'), 999 array('password')); 1000 if (!isset($input[0])) { 1001 //use empty passphrase 1002 $input[0] = ''; 1003 } 1004 1005 $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null'; 1006 $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w"); 1007 if (!$gpg) { 1008 return $this->raiseError("gpg command failed"); 1009 } 1010 1011 fwrite($gpg, "$input[0]\n"); 1012 if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) { 1013 return $this->raiseError("gpg sign failed"); 1014 } 1015 1016 if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) { 1017 return $this->raiseError('failed adding signature to file'); 1018 } 1019 1020 $this->ui->outputData("Package signed.", $command); 1021 return true; 1022 } 1023 1024 /** 1025 * For unit testing purposes 1026 */ 1027 function &getInstaller(&$ui) 1028 { 1029 if (!class_exists('PEAR_Installer')) { 1030 require_once 'PEAR/Installer.php'; 1031 } 1032 $a = new PEAR_Installer($ui); 1033 return $a; 1034 } 1035 1036 /** 1037 * For unit testing purposes 1038 */ 1039 function &getCommandPackaging(&$ui, &$config) 1040 { 1041 if (!class_exists('PEAR_Command_Packaging')) { 1042 if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) { 1043 fclose($fp); 1044 include_once 'PEAR/Command/Packaging.php'; 1045 } 1046 } 1047 1048 if (class_exists('PEAR_Command_Packaging')) { 1049 $a = new PEAR_Command_Packaging($ui, $config); 1050 } else { 1051 $a = null; 1052 } 1053 1054 return $a; 1055 } 1056 1057 function doMakeRPM($command, $options, $params) 1058 { 1059 1060 // Check to see if PEAR_Command_Packaging is installed, and 1061 // transparently switch to use the "make-rpm-spec" command from it 1062 // instead, if it does. Otherwise, continue to use the old version 1063 // of "makerpm" supplied with this package (PEAR). 1064 $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config); 1065 if ($packaging_cmd !== null) { 1066 $this->ui->outputData('PEAR_Command_Packaging is installed; using '. 1067 'newer "make-rpm-spec" command instead'); 1068 return $packaging_cmd->run('make-rpm-spec', $options, $params); 1069 } 1070 1071 $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '. 1072 'improved version is available via "pear make-rpm-spec", which '. 1073 'is available by installing PEAR_Command_Packaging'); 1074 return true; 1075 } 1076 1077 function doConvert($command, $options, $params) 1078 { 1079 $packagexml = isset($params[0]) ? $params[0] : 'package.xml'; 1080 $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) . 1081 DIRECTORY_SEPARATOR . 'package2.xml'; 1082 $pkg = &$this->getPackageFile($this->config, $this->_debug); 1083 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 1084 $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL); 1085 PEAR::staticPopErrorHandling(); 1086 if (PEAR::isError($pf)) { 1087 if (is_array($pf->getUserInfo())) { 1088 foreach ($pf->getUserInfo() as $warning) { 1089 $this->ui->outputData($warning['message']); 1090 } 1091 } 1092 return $this->raiseError($pf); 1093 } 1094 1095 if (is_a($pf, 'PEAR_PackageFile_v2')) { 1096 $this->ui->outputData($packagexml . ' is already a package.xml version 2.0'); 1097 return true; 1098 } 1099 1100 $gen = &$pf->getDefaultGenerator(); 1101 $newpf = &$gen->toV2(); 1102 $newpf->setPackagefile($newpackagexml); 1103 $gen = &$newpf->getDefaultGenerator(); 1104 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 1105 $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL); 1106 $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml)); 1107 PEAR::staticPopErrorHandling(); 1108 if (PEAR::isError($saved)) { 1109 if (is_array($saved->getUserInfo())) { 1110 foreach ($saved->getUserInfo() as $warning) { 1111 $this->ui->outputData($warning['message']); 1112 } 1113 } 1114 1115 $this->ui->outputData($saved->getMessage()); 1116 return true; 1117 } 1118 1119 $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"'); 1120 return true; 1121 } 1122} 1123