Updated PEAR and PEAR packages.
[timetracker.git] / WEB-INF / lib / pear / PEAR / Frontend / CLI.php
1 <?php
2 /**
3  * PEAR_Frontend_CLI
4  *
5  * PHP versions 4 and 5
6  *
7  * @category   pear
8  * @package    PEAR
9  * @author     Stig Bakken <ssb@php.net>
10  * @author     Greg Beaver <cellog@php.net>
11  * @copyright  1997-2009 The Authors
12  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
13  * @link       http://pear.php.net/package/PEAR
14  * @since      File available since Release 0.1
15  */
16 /**
17  * base class
18  */
19 require_once 'PEAR/Frontend.php';
20
21 /**
22  * Command-line Frontend for the PEAR Installer
23  * @category   pear
24  * @package    PEAR
25  * @author     Stig Bakken <ssb@php.net>
26  * @author     Greg Beaver <cellog@php.net>
27  * @copyright  1997-2009 The Authors
28  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
29  * @version    Release: 1.10.1
30  * @link       http://pear.php.net/package/PEAR
31  * @since      Class available since Release 0.1
32  */
33 class PEAR_Frontend_CLI extends PEAR_Frontend
34 {
35     /**
36      * What type of user interface this frontend is for.
37      * @var string
38      * @access public
39      */
40     var $type = 'CLI';
41     var $lp = ''; // line prefix
42
43     var $params = array();
44     var $term = array(
45         'bold'   => '',
46         'normal' => '',
47     );
48
49     function __construct()
50     {
51         parent::__construct();
52         $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
53         if (function_exists('posix_isatty') && !posix_isatty(1)) {
54             // output is being redirected to a file or through a pipe
55         } elseif ($term) {
56             if (preg_match('/^(xterm|vt220|linux)/', $term)) {
57                 $this->term['bold']   = sprintf("%c%c%c%c", 27, 91, 49, 109);
58                 $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109);
59             } elseif (preg_match('/^vt100/', $term)) {
60                 $this->term['bold']   = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
61                 $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
62             }
63         } elseif (OS_WINDOWS) {
64             // XXX add ANSI codes here
65         }
66     }
67
68     /**
69      * @param object PEAR_Error object
70      */
71     function displayError($e)
72     {
73         return $this->_displayLine($e->getMessage());
74     }
75
76     /**
77      * @param object PEAR_Error object
78      */
79     function displayFatalError($eobj)
80     {
81         $this->displayError($eobj);
82         if (class_exists('PEAR_Config')) {
83             $config = &PEAR_Config::singleton();
84             if ($config->get('verbose') > 5) {
85                 if (function_exists('debug_print_backtrace')) {
86                     debug_print_backtrace();
87                     exit(1);
88                 }
89
90                 $raised = false;
91                 foreach (debug_backtrace() as $i => $frame) {
92                     if (!$raised) {
93                         if (isset($frame['class'])
94                             && strtolower($frame['class']) == 'pear'
95                             && strtolower($frame['function']) == 'raiseerror'
96                         ) {
97                             $raised = true;
98                         } else {
99                             continue;
100                         }
101                     }
102
103                     $frame['class']    = !isset($frame['class'])    ? '' : $frame['class'];
104                     $frame['type']     = !isset($frame['type'])     ? '' : $frame['type'];
105                     $frame['function'] = !isset($frame['function']) ? '' : $frame['function'];
106                     $frame['line']     = !isset($frame['line'])     ? '' : $frame['line'];
107                     $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]");
108                 }
109             }
110         }
111
112         exit(1);
113     }
114
115     /**
116      * Instruct the runInstallScript method to skip a paramgroup that matches the
117      * id value passed in.
118      *
119      * This method is useful for dynamically configuring which sections of a post-install script
120      * will be run based on the user's setup, which is very useful for making flexible
121      * post-install scripts without losing the cross-Frontend ability to retrieve user input
122      * @param string
123      */
124     function skipParamgroup($id)
125     {
126         $this->_skipSections[$id] = true;
127     }
128
129     function runPostinstallScripts(&$scripts)
130     {
131         foreach ($scripts as $i => $script) {
132             $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj);
133         }
134     }
135
136     /**
137      * @param array $xml contents of postinstallscript tag
138      * @param object $script post-installation script
139      * @param string install|upgrade
140      */
141     function runInstallScript($xml, &$script)
142     {
143         $this->_skipSections = array();
144         if (!is_array($xml) || !isset($xml['paramgroup'])) {
145             $script->run(array(), '_default');
146             return;
147         }
148
149         $completedPhases = array();
150         if (!isset($xml['paramgroup'][0])) {
151             $xml['paramgroup'] = array($xml['paramgroup']);
152         }
153
154         foreach ($xml['paramgroup'] as $group) {
155             if (isset($this->_skipSections[$group['id']])) {
156                 // the post-install script chose to skip this section dynamically
157                 continue;
158             }
159
160             if (isset($group['name'])) {
161                 $paramname = explode('::', $group['name']);
162                 if ($lastgroup['id'] != $paramname[0]) {
163                     continue;
164                 }
165
166                 $group['name'] = $paramname[1];
167                 if (!isset($answers)) {
168                     return;
169                 }
170
171                 if (isset($answers[$group['name']])) {
172                     switch ($group['conditiontype']) {
173                         case '=' :
174                             if ($answers[$group['name']] != $group['value']) {
175                                 continue 2;
176                             }
177                         break;
178                         case '!=' :
179                             if ($answers[$group['name']] == $group['value']) {
180                                 continue 2;
181                             }
182                         break;
183                         case 'preg_match' :
184                             if (!@preg_match('/' . $group['value'] . '/',
185                                   $answers[$group['name']])) {
186                                 continue 2;
187                             }
188                         break;
189                         default :
190                         return;
191                     }
192                 }
193             }
194
195             $lastgroup = $group;
196             if (isset($group['instructions'])) {
197                 $this->_display($group['instructions']);
198             }
199
200             if (!isset($group['param'][0])) {
201                 $group['param'] = array($group['param']);
202             }
203
204             if (isset($group['param'])) {
205                 if (method_exists($script, 'postProcessPrompts')) {
206                     $prompts = $script->postProcessPrompts($group['param'], $group['id']);
207                     if (!is_array($prompts) || count($prompts) != count($group['param'])) {
208                         $this->outputData('postinstall', 'Error: post-install script did not ' .
209                             'return proper post-processed prompts');
210                         $prompts = $group['param'];
211                     } else {
212                         foreach ($prompts as $i => $var) {
213                             if (!is_array($var) || !isset($var['prompt']) ||
214                                   !isset($var['name']) ||
215                                   ($var['name'] != $group['param'][$i]['name']) ||
216                                   ($var['type'] != $group['param'][$i]['type'])
217                             ) {
218                                 $this->outputData('postinstall', 'Error: post-install script ' .
219                                     'modified the variables or prompts, severe security risk. ' .
220                                     'Will instead use the defaults from the package.xml');
221                                 $prompts = $group['param'];
222                             }
223                         }
224                     }
225
226                     $answers = $this->confirmDialog($prompts);
227                 } else {
228                     $answers = $this->confirmDialog($group['param']);
229                 }
230             }
231
232             if ((isset($answers) && $answers) || !isset($group['param'])) {
233                 if (!isset($answers)) {
234                     $answers = array();
235                 }
236
237                 array_unshift($completedPhases, $group['id']);
238                 if (!$script->run($answers, $group['id'])) {
239                     $script->run($completedPhases, '_undoOnError');
240                     return;
241                 }
242             } else {
243                 $script->run($completedPhases, '_undoOnError');
244                 return;
245             }
246         }
247     }
248
249     /**
250      * Ask for user input, confirm the answers and continue until the user is satisfied
251      * @param array an array of arrays, format array('name' => 'paramname', 'prompt' =>
252      *              'text to display', 'type' => 'string'[, default => 'default value'])
253      * @return array
254      */
255     function confirmDialog($params)
256     {
257         $answers = $prompts = $types = array();
258         foreach ($params as $param) {
259             $prompts[$param['name']] = $param['prompt'];
260             $types[$param['name']]   = $param['type'];
261             $answers[$param['name']] = isset($param['default']) ? $param['default'] : '';
262         }
263
264         $tried = false;
265         do {
266             if ($tried) {
267                 $i = 1;
268                 foreach ($answers as $var => $value) {
269                     if (!strlen($value)) {
270                         echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n");
271                     }
272                     $i++;
273                 }
274             }
275
276             $answers = $this->userDialog('', $prompts, $types, $answers);
277             $tried   = true;
278         } while (is_array($answers) && count(array_filter($answers)) != count($prompts));
279
280         return $answers;
281     }
282
283     function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20)
284     {
285         if (!is_array($prompts)) {
286             return array();
287         }
288
289         $testprompts = array_keys($prompts);
290         $result      = $defaults;
291
292         reset($prompts);
293         if (count($prompts) === 1) {
294             foreach ($prompts as $key => $prompt) {
295                 $type    = $types[$key];
296                 $default = @$defaults[$key];
297                 print "$prompt ";
298                 if ($default) {
299                     print "[$default] ";
300                 }
301                 print ": ";
302
303                 $line         = fgets(STDIN, 2048);
304                 $result[$key] =  ($default && trim($line) == '') ? $default : trim($line);
305             }
306
307             return $result;
308         }
309
310         $first_run = true;
311         while (true) {
312             $descLength = max(array_map('strlen', $prompts));
313             $descFormat = "%-{$descLength}s";
314             $last       = count($prompts);
315
316             $i = 0;
317             foreach ($prompts as $n => $var) {
318                 $res = isset($result[$n]) ? $result[$n] : null;
319                 printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res);
320             }
321             print "\n1-$last, 'all', 'abort', or Enter to continue: ";
322
323             $tmp = trim(fgets(STDIN, 1024));
324             if (empty($tmp)) {
325                 break;
326             }
327
328             if ($tmp == 'abort') {
329                 return false;
330             }
331
332             if (isset($testprompts[(int)$tmp - 1])) {
333                 $var     = $testprompts[(int)$tmp - 1];
334                 $desc    = $prompts[$var];
335                 $current = @$result[$var];
336                 print "$desc [$current] : ";
337                 $tmp = trim(fgets(STDIN, 1024));
338                 if ($tmp !== '') {
339                     $result[$var] = $tmp;
340                 }
341             } elseif ($tmp == 'all') {
342                 foreach ($prompts as $var => $desc) {
343                     $current = $result[$var];
344                     print "$desc [$current] : ";
345                     $tmp = trim(fgets(STDIN, 1024));
346                     if (trim($tmp) !== '') {
347                         $result[$var] = trim($tmp);
348                     }
349                 }
350             }
351
352             $first_run = false;
353         }
354
355         return $result;
356     }
357
358     function userConfirm($prompt, $default = 'yes')
359     {
360         trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
361         static $positives = array('y', 'yes', 'on', '1');
362         static $negatives = array('n', 'no', 'off', '0');
363         print "$this->lp$prompt [$default] : ";
364         $fp = fopen("php://stdin", "r");
365         $line = fgets($fp, 2048);
366         fclose($fp);
367         $answer = strtolower(trim($line));
368         if (empty($answer)) {
369             $answer = $default;
370         }
371         if (in_array($answer, $positives)) {
372             return true;
373         }
374         if (in_array($answer, $negatives)) {
375             return false;
376         }
377         if (in_array($default, $positives)) {
378             return true;
379         }
380         return false;
381     }
382
383     function outputData($data, $command = '_default')
384     {
385         switch ($command) {
386             case 'channel-info':
387                 foreach ($data as $type => $section) {
388                     if ($type == 'main') {
389                         $section['data'] = array_values($section['data']);
390                     }
391
392                     $this->outputData($section);
393                 }
394                 break;
395             case 'install':
396             case 'upgrade':
397             case 'upgrade-all':
398                 if (is_array($data) && isset($data['release_warnings'])) {
399                     $this->_displayLine('');
400                     $this->_startTable(array(
401                         'border' => false,
402                         'caption' => 'Release Warnings'
403                     ));
404                     $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
405                     $this->_endTable();
406                     $this->_displayLine('');
407                 }
408
409                 $this->_displayLine(is_array($data) ? $data['data'] : $data);
410                 break;
411             case 'search':
412                 $this->_startTable($data);
413                 if (isset($data['headline']) && is_array($data['headline'])) {
414                     $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
415                 }
416
417                 $packages = array();
418                 foreach($data['data'] as $category) {
419                     foreach($category as $name => $pkg) {
420                         $packages[$pkg[0]] = $pkg;
421                     }
422                 }
423
424                 $p = array_keys($packages);
425                 natcasesort($p);
426                 foreach ($p as $name) {
427                     $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55)));
428                 }
429
430                 $this->_endTable();
431                 break;
432             case 'list-all':
433                 if (!isset($data['data'])) {
434                       $this->_displayLine('No packages in channel');
435                       break;
436                 }
437
438                 $this->_startTable($data);
439                 if (isset($data['headline']) && is_array($data['headline'])) {
440                     $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
441                 }
442
443                 $packages = array();
444                 foreach($data['data'] as $category) {
445                     foreach($category as $name => $pkg) {
446                         $packages[$pkg[0]] = $pkg;
447                     }
448                 }
449
450                 $p = array_keys($packages);
451                 natcasesort($p);
452                 foreach ($p as $name) {
453                     $pkg = $packages[$name];
454                     unset($pkg[4], $pkg[5]);
455                     $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
456                 }
457
458                 $this->_endTable();
459                 break;
460             case 'config-show':
461                 $data['border'] = false;
462                 $opts = array(
463                     0 => array('wrap' => 30),
464                     1 => array('wrap' => 20),
465                     2 => array('wrap' => 35)
466                 );
467
468                 $this->_startTable($data);
469                 if (isset($data['headline']) && is_array($data['headline'])) {
470                     $this->_tableRow($data['headline'], array('bold' => true), $opts);
471                 }
472
473                 foreach ($data['data'] as $group) {
474                     foreach ($group as $value) {
475                         if ($value[2] == '') {
476                             $value[2] = "<not set>";
477                         }
478
479                         $this->_tableRow($value, null, $opts);
480                     }
481                 }
482
483                 $this->_endTable();
484                 break;
485             case 'remote-info':
486                 $d = $data;
487                 $data = array(
488                     'caption' => 'Package details:',
489                     'border'  => false,
490                     'data'    => array(
491                         array("Latest",      $data['stable']),
492                         array("Installed",   $data['installed']),
493                         array("Package",     $data['name']),
494                         array("License",     $data['license']),
495                         array("Category",    $data['category']),
496                         array("Summary",     $data['summary']),
497                         array("Description", $data['description']),
498                     ),
499                 );
500
501                 if (isset($d['deprecated']) && $d['deprecated']) {
502                     $conf = &PEAR_Config::singleton();
503                     $reg = $conf->getRegistry();
504                     $name = $reg->parsedPackageNameToString($d['deprecated'], true);
505                     $data['data'][] = array('Deprecated! use', $name);
506                 }
507             default: {
508                 if (is_array($data)) {
509                     $this->_startTable($data);
510                     $count = count($data['data'][0]);
511                     if ($count == 2) {
512                         $opts = array(0 => array('wrap' => 25),
513                                       1 => array('wrap' => 48)
514                         );
515                     } elseif ($count == 3) {
516                         $opts = array(0 => array('wrap' => 30),
517                                       1 => array('wrap' => 20),
518                                       2 => array('wrap' => 35)
519                         );
520                     } else {
521                         $opts = null;
522                     }
523                     if (isset($data['headline']) && is_array($data['headline'])) {
524                         $this->_tableRow($data['headline'],
525                                          array('bold' => true),
526                                          $opts);
527                     }
528
529                     if (is_array($data['data'])) {
530                         foreach($data['data'] as $row) {
531                             $this->_tableRow($row, null, $opts);
532                         }
533                     } else {
534                         $this->_tableRow(array($data['data']), null, $opts);
535                      }
536                     $this->_endTable();
537                 } else {
538                     $this->_displayLine($data);
539                 }
540             }
541         }
542     }
543
544     function log($text, $append_crlf = true)
545     {
546         if ($append_crlf) {
547             return $this->_displayLine($text);
548         }
549
550         return $this->_display($text);
551     }
552
553     function bold($text)
554     {
555         if (empty($this->term['bold'])) {
556             return strtoupper($text);
557         }
558
559         return $this->term['bold'] . $text . $this->term['normal'];
560     }
561
562     function _displayHeading($title)
563     {
564         print $this->lp.$this->bold($title)."\n";
565         print $this->lp.str_repeat("=", strlen($title))."\n";
566     }
567
568     function _startTable($params = array())
569     {
570         $params['table_data'] = array();
571         $params['widest']     = array();  // indexed by column
572         $params['highest']    = array(); // indexed by row
573         $params['ncols']      = 0;
574         $this->params         = $params;
575     }
576
577     function _tableRow($columns, $rowparams = array(), $colparams = array())
578     {
579         $highest = 1;
580         for ($i = 0; $i < count($columns); $i++) {
581             $col = &$columns[$i];
582             if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
583                 $col = wordwrap($col, $colparams[$i]['wrap']);
584             }
585
586             if (strpos($col, "\n") !== false) {
587                 $multiline = explode("\n", $col);
588                 $w = 0;
589                 foreach ($multiline as $n => $line) {
590                     $len = strlen($line);
591                     if ($len > $w) {
592                         $w = $len;
593                     }
594                 }
595                 $lines = count($multiline);
596             } else {
597                 $w = strlen($col);
598             }
599
600             if (isset($this->params['widest'][$i])) {
601                 if ($w > $this->params['widest'][$i]) {
602                     $this->params['widest'][$i] = $w;
603                 }
604             } else {
605                 $this->params['widest'][$i] = $w;
606             }
607
608             $tmp = count_chars($columns[$i], 1);
609             // handle unix, mac and windows formats
610             $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1;
611             if ($lines > $highest) {
612                 $highest = $lines;
613             }
614         }
615
616         if (count($columns) > $this->params['ncols']) {
617             $this->params['ncols'] = count($columns);
618         }
619
620         $new_row = array(
621             'data'      => $columns,
622             'height'    => $highest,
623             'rowparams' => $rowparams,
624             'colparams' => $colparams,
625         );
626         $this->params['table_data'][] = $new_row;
627     }
628
629     function _endTable()
630     {
631         extract($this->params);
632         if (!empty($caption)) {
633             $this->_displayHeading($caption);
634         }
635
636         if (count($table_data) === 0) {
637             return;
638         }
639
640         if (!isset($width)) {
641             $width = $widest;
642         } else {
643             for ($i = 0; $i < $ncols; $i++) {
644                 if (!isset($width[$i])) {
645                     $width[$i] = $widest[$i];
646                 }
647             }
648         }
649
650         $border = false;
651         if (empty($border)) {
652             $cellstart  = '';
653             $cellend    = ' ';
654             $rowend     = '';
655             $padrowend  = false;
656             $borderline = '';
657         } else {
658             $cellstart  = '| ';
659             $cellend    = ' ';
660             $rowend     = '|';
661             $padrowend  = true;
662             $borderline = '+';
663             foreach ($width as $w) {
664                 $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
665                 $borderline .= '+';
666             }
667         }
668
669         if ($borderline) {
670             $this->_displayLine($borderline);
671         }
672
673         for ($i = 0; $i < count($table_data); $i++) {
674             extract($table_data[$i]);
675             if (!is_array($rowparams)) {
676                 $rowparams = array();
677             }
678
679             if (!is_array($colparams)) {
680                 $colparams = array();
681             }
682
683             $rowlines = array();
684             if ($height > 1) {
685                 for ($c = 0; $c < count($data); $c++) {
686                     $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
687                     if (count($rowlines[$c]) < $height) {
688                         $rowlines[$c] = array_pad($rowlines[$c], $height, '');
689                     }
690                 }
691             } else {
692                 for ($c = 0; $c < count($data); $c++) {
693                     $rowlines[$c] = array($data[$c]);
694                 }
695             }
696
697             for ($r = 0; $r < $height; $r++) {
698                 $rowtext = '';
699                 for ($c = 0; $c < count($data); $c++) {
700                     if (isset($colparams[$c])) {
701                         $attribs = array_merge($rowparams, $colparams);
702                     } else {
703                         $attribs = $rowparams;
704                     }
705
706                     $w = isset($width[$c]) ? $width[$c] : 0;
707                     //$cell = $data[$c];
708                     $cell = $rowlines[$c][$r];
709                     $l = strlen($cell);
710                     if ($l > $w) {
711                         $cell = substr($cell, 0, $w);
712                     }
713
714                     if (isset($attribs['bold'])) {
715                         $cell = $this->bold($cell);
716                     }
717
718                     if ($l < $w) {
719                         // not using str_pad here because we may
720                         // add bold escape characters to $cell
721                         $cell .= str_repeat(' ', $w - $l);
722                     }
723
724                     $rowtext .= $cellstart . $cell . $cellend;
725                 }
726
727                 if (!$border) {
728                     $rowtext = rtrim($rowtext);
729                 }
730
731                 $rowtext .= $rowend;
732                 $this->_displayLine($rowtext);
733             }
734         }
735
736         if ($borderline) {
737             $this->_displayLine($borderline);
738         }
739     }
740
741     function _displayLine($text)
742     {
743         print "$this->lp$text\n";
744     }
745
746     function _display($text)
747     {
748         print $text;
749     }
750 }