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