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