Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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: 1.9.4
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
}