PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/php/PEAR/Frontend/CLI.php

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