From 76cde0e16ee356486ffec389b0a1686fba265e53 Mon Sep 17 00:00:00 2001 From: Stig Bakken Date: Tue, 9 Apr 2002 19:04:25 +0000 Subject: [PATCH] * PEAR MFH # using rsync this time, does it work? --- pear/DB.php | 20 +-- pear/PEAR.php | 82 ++++++--- pear/PEAR/Command.php | 15 ++ pear/PEAR/Command/Auth.php | 29 +-- pear/PEAR/Command/Common.php | 22 +-- pear/PEAR/Command/Config.php | 101 ++++++++--- pear/PEAR/Command/Install.php | 63 +++++-- pear/PEAR/Command/Package.php | 184 ++++++++++++++++++- pear/PEAR/Command/Registry.php | 49 +++++- pear/PEAR/CommandResponse.php | 123 ------------- pear/PEAR/Common.php | 312 ++++++++++++++++++++++++++++----- pear/PEAR/Config.php | 50 ++++-- pear/PEAR/Dependency.php | 8 +- pear/PEAR/Frontend/CLI.php | 40 +++-- pear/PEAR/Installer.php | 173 ++++++++++++------ pear/PEAR/Packager.php | 30 +++- pear/PEAR/Registry.php | 52 +++--- pear/PEAR/Remote.php | 4 +- pear/PEAR/Uploader.php | 60 ------- pear/package.dtd | 21 ++- pear/pear.m4 | 1 + pear/scripts/pear.bat | 6 +- pear/scripts/pear.in | 98 ++++------- 23 files changed, 1014 insertions(+), 529 deletions(-) delete mode 100644 pear/PEAR/CommandResponse.php delete mode 100644 pear/PEAR/Uploader.php diff --git a/pear/DB.php b/pear/DB.php index 22cff1f81c..75b12e7944 100644 --- a/pear/DB.php +++ b/pear/DB.php @@ -127,8 +127,8 @@ define('DB_FETCHMODE_ORDERED', 1); define('DB_FETCHMODE_ASSOC', 2); /** -* Column data as object properties -*/ + * Column data as object properties + */ define('DB_FETCHMODE_OBJECT', 3); @@ -180,7 +180,6 @@ define('DB_TABLEINFO_FULL', 3); * class. * * @package DB - * @version 2 * @author Stig Bakken * @since PHP 4.0 */ @@ -261,9 +260,6 @@ class DB @$obj =& new $classname; - if (isset($options['connect_ondemand']) && !extension_loaded("overload")) { - unset($options['connect_ondemand']); - } if (is_array($options)) { foreach ($options as $option => $value) { $test = $obj->setOption($option, $value); @@ -274,14 +270,10 @@ class DB } else { $obj->setOption('persistent', $options); } - if (!$obj->getOption('connect_ondemand')) { - $err = $obj->connect($dsninfo, $obj->getOption('persistent')); - if (DB::isError($err)) { - $err->addUserInfo($dsn); - return $err; - } - } else { - $obj->dsn = $dsninfo; + $err = $obj->connect($dsninfo, $obj->getOption('persistent')); + if (DB::isError($err)) { + $err->addUserInfo($dsn); + return $err; } return $obj; diff --git a/pear/PEAR.php b/pear/PEAR.php index 7c5198b6b1..4d013d76c2 100644 --- a/pear/PEAR.php +++ b/pear/PEAR.php @@ -39,8 +39,13 @@ if (substr(PHP_OS, 0, 3) == 'WIN') { $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; $GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_PEAR_default_error_callback'] = ''; $GLOBALS['_PEAR_destructor_object_list'] = array(); +// +// Tests needed: - PEAR inheritance +// + /** * Base class for other PEAR classes. Provides rudimentary * emulation of destructors. @@ -55,12 +60,8 @@ $GLOBALS['_PEAR_destructor_object_list'] = array(); * discarded. If you need to get any debug information from your * destructor, use error_log(), syslog() or something similar. * - * IMPORTANT! To use the emulated destructors you need to create the - * objects by reference, ej: $obj =& new PEAR_child; - * * @since PHP 4.0.2 * @author Stig Bakken - * @see http://pear.php.net/manual/ */ class PEAR { @@ -125,8 +126,8 @@ class PEAR * $_PEAR_destructor_object_list for destructor emulation if a * destructor object exists. * - * @param string $error_class (optional) which class to use for error - * objects, defaults to PEAR_Error. + * @param string (optional) which class to use for error objects, + * defaults to PEAR_Error. * @access public * @return void */ @@ -177,7 +178,7 @@ class PEAR /** * Tell whether a value is a PEAR error. * - * @param mixed $data the value to test + * @param mixed the value to test * @access public * @return bool true if parameter is an error */ @@ -233,9 +234,11 @@ class PEAR if (isset($this)) { $setmode = &$this->_default_error_mode; $setoptions = &$this->_default_error_options; + //$setcallback = &$this->_default_error_callback; } else { $setmode = &$GLOBALS['_PEAR_default_error_mode']; $setoptions = &$GLOBALS['_PEAR_default_error_options']; + //$setcallback = &$GLOBALS['_PEAR_default_error_callback']; } switch ($mode) { @@ -276,15 +279,12 @@ class PEAR * expected errors are in effect until they are popped off the * stack with the popExpect() method. * - * Note that this method can not be called statically - * - * @param mixed $code a single error code or an array of error codes - * to expect + * @param mixed a single error code or an array of error codes + * to expect * * @return int the new depth of the "expected errors" stack - * @access public */ - function expectError($code = '*') + function expectError($code = "*") { if (is_array($code)) { array_push($this->_expected_errors, $code); @@ -371,19 +371,47 @@ class PEAR $mode = PEAR_ERROR_RETURN; } } - // No mode given, try global ones + if ($mode === null) { - // Class error handler if (isset($this) && isset($this->_default_error_mode)) { - $mode = $this->_default_error_mode; - $options = $this->_default_error_options; - // Global error handler - } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { - $mode = $GLOBALS['_PEAR_default_error_mode']; + $mode = $this->_default_error_mode; + } else { + $mode = $GLOBALS['_PEAR_default_error_mode']; + } + } + + if ($mode == PEAR_ERROR_TRIGGER && $options === null) { + if (isset($this)) { + if (isset($this->_default_error_options)) { + $options = $this->_default_error_options; + } + } else { $options = $GLOBALS['_PEAR_default_error_options']; } } + if ($mode == PEAR_ERROR_CALLBACK) { + if (!is_string($options) && + !(is_array($options) && sizeof($options) == 2 && + is_object($options[0]) && is_string($options[1]))) + { + if (isset($this) && isset($this->_default_error_options)) { + $options = $this->_default_error_options; + } else { + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + } else { + if ($options === null) { + if (isset($this)) { + if (isset($this->_default_error_options)) { + $options = $this->_default_error_options; + } + } else { + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + } if ($error_class !== null) { $ec = $error_class; } elseif (isset($this) && isset($this->_error_class)) { @@ -406,7 +434,7 @@ class PEAR * you can easily override the actual error handler for some code and restore * it later with popErrorHandling. * - * @param $mode mixed (same as setErrorHandling) + * @param $mode mixed (same as setErrorHandling) * @param $options mixed (same as setErrorHandling) * * @return bool Always true @@ -420,9 +448,13 @@ class PEAR if (isset($this)) { $def_mode = &$this->_default_error_mode; $def_options = &$this->_default_error_options; + // XXX Used anywhere? + //$def_callback = &$this->_default_error_callback; } else { $def_mode = &$GLOBALS['_PEAR_default_error_mode']; $def_options = &$GLOBALS['_PEAR_default_error_options']; + // XXX Used anywhere? + //$def_callback = &$GLOBALS['_PEAR_default_error_callback']; } $stack = array(); $stack[] = array($def_mode, $def_options); @@ -622,7 +654,7 @@ class PEAR_Error * @return string full error message * @access public */ - function getMessage() + function getMessage () { return ($this->error_message_prefix . $this->message); } @@ -651,7 +683,7 @@ class PEAR_Error * @return string error/exception name (type) * @access public */ - function getType() + function getType () { return get_class($this); } @@ -665,7 +697,7 @@ class PEAR_Error * @return string user-supplied information * @access public */ - function getUserInfo() + function getUserInfo () { return $this->userinfo; } @@ -679,7 +711,7 @@ class PEAR_Error * @return string debug information * @access public */ - function getDebugInfo() + function getDebugInfo () { return $this->getUserInfo(); } diff --git a/pear/PEAR/Command.php b/pear/PEAR/Command.php index 851099e66b..50b8c7b42c 100644 --- a/pear/PEAR/Command.php +++ b/pear/PEAR/Command.php @@ -33,6 +33,12 @@ $GLOBALS['_PEAR_Command_commandlist'] = array(); */ $GLOBALS['_PEAR_Command_uiclass'] = 'PEAR_Frontend_CLI'; +/** + * Instance of $_PEAR_Command_uiclass. + * @var object + */ +$GLOBALS['_PEAR_Command_uiobject'] = null; + /** * The options accepted by the commands * @var string the options @@ -205,6 +211,15 @@ class PEAR_Command } return $GLOBALS['_PEAR_Command_commandopts']; } + + function getHelp($command) + { + $cmds = PEAR_Command::getCommands(); + if (isset($cmds[$command])) { + return call_user_func(array($cmds[$command], 'getHelp'), $command); + } + return false; + } } ?> \ No newline at end of file diff --git a/pear/PEAR/Command/Auth.php b/pear/PEAR/Command/Auth.php index 81c393e8d6..f59f730b83 100644 --- a/pear/PEAR/Command/Auth.php +++ b/pear/PEAR/Command/Auth.php @@ -55,6 +55,16 @@ class PEAR_Command_Auth extends PEAR_Command_Common } // }}} + + function getHelp($command) + { + switch ($command) { + case 'login': + return array(null, 'Connects to the remote server'); + case 'logout': + return array(null, 'Disconnects from the remote server'); + } + } // {{{ run() /** @@ -73,28 +83,27 @@ class PEAR_Command_Auth extends PEAR_Command_Common */ function run($command, $options, $params) { - $cf = $this->config; $failmsg = ''; - $server = $cf->get('master_server'); + $server = $this->config->get('master_server'); switch ($command) { case 'login': { - $remote = new PEAR_Remote($cf); - $username = $cf->get('username'); + $remote = new PEAR_Remote($this->config); + $username = $this->config->get('username'); if (empty($username)) { $username = @$_ENV['USER']; } $this->ui->displayLine("Logging in to $server."); $username = trim($this->ui->userDialog('Username', 'text', $username)); - $cf->set('username', $username); + $this->config->set('username', $username); $password = trim($this->ui->userDialog('Password', 'password')); - $cf->set('password', $password); + $this->config->set('password', $password); $remote->expectError(401); $ok = $remote->call('logintest'); $remote->popExpect(); if ($ok === true) { $this->ui->displayLine("Logged in."); - $cf->store(); + $this->config->store(); } else { $this->ui->displayLine("Login failed!"); } @@ -102,9 +111,9 @@ class PEAR_Command_Auth extends PEAR_Command_Common } case 'logout': { $this->ui->displayLine("Logging out from $server."); - $cf->remove('username'); - $cf->remove('password'); - $cf->store(); + $this->config->remove('username'); + $this->config->remove('password'); + $this->config->store(); break; } default: { diff --git a/pear/PEAR/Command/Common.php b/pear/PEAR/Command/Common.php index 7ea724a999..c31c23680d 100644 --- a/pear/PEAR/Command/Common.php +++ b/pear/PEAR/Command/Common.php @@ -19,7 +19,6 @@ // $Id$ require_once "PEAR.php"; -//require_once "PEAR/CommandResponse.php"; class PEAR_Command_Common extends PEAR { @@ -54,27 +53,10 @@ class PEAR_Command_Common extends PEAR return array(); } - /** - * Return a PEAR_CommandResponse object with parameters - * filled in. - * - * @param int status code - * @param string message text - * @param string (optional) message character encoding - * - * @return object a PEAR_CommandResponse object - * - * @access public - * - * @see PEAR_CommandResponse - */ -/* - function &makeResponse($status, $message, $encoding = null) + function getHelp($command) { - $obj = &new PEAR_CommandResponse($status, $message, $encoding); - return $obj; + return array(null, 'No help avaible yet'); } -*/ } ?> \ No newline at end of file diff --git a/pear/PEAR/Command/Config.php b/pear/PEAR/Command/Config.php index 1fdcf32747..009c2d243e 100644 --- a/pear/PEAR/Command/Config.php +++ b/pear/PEAR/Command/Config.php @@ -14,6 +14,8 @@ // | license@php.net so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // | Author: Stig Bakken | +// | Tomas V.V.Cox | +// | | // +----------------------------------------------------------------------+ // // $Id$ @@ -57,44 +59,66 @@ class PEAR_Command_Config extends PEAR_Command_Common } // }}} + + function getHelp($command) + { + switch ($command) { + case 'config-show': + $ret = array('[]', 'Displays the configuration'); + break; + case 'config-get': + $ret = array(' []', + 'Displays the value of the given parameter'); + break; + case 'config-set': + $ret = array(' []', + 'Sets the value of a parameter in the config'); + break; + } + $ret[1] .= "\n". + " Where to store/get the configuration. The installer\n". + " supports 'user' (per user conf) and 'system' (global conf)"; + return $ret; + } // {{{ run() function run($command, $options, $params) { - $cf =& $this->config; + $cf = &$this->config; $failmsg = ''; - $params[0] = (isset($params[0])) ? $params[0] : null; - $params[1] = (isset($params[1])) ? $params[1] : null; switch ($command) { case 'config-show': { + // $params[0] -> the layer + if ($error = $this->_checkLayer(@$params[0])) { + $failmsg .= $error; + break; + } $keys = $cf->getKeys(); sort($keys); $this->ui->startTable(array('caption' => 'Configuration:')); - if (isset($params[0]) && $cf->isDefined($params[0])) { - foreach ($keys as $key) { - $type = $cf->getType($key); - $value = $cf->get($key, $params[0]); - if ($type == 'password' && $value) { - $value = '********'; - } - $this->ui->tableRow(array($key, $value)); + foreach ($keys as $key) { + $type = $cf->getType($key); + $value = $cf->get($key, @$params[0]); + if ($type == 'password' && $value) { + $value = '********'; } - } else { - foreach ($keys as $key) { - $type = $cf->getType($key); - $value = $cf->get($key, $params[0]); - if ($type == 'password' && $value) { - $value = '********'; - } - $this->ui->tableRow(array($key, $value)); + if (empty($value)) { + $value = ''; } + $this->ui->tableRow(array($key, $value)); } $this->ui->endTable(); break; } case 'config-get': { + // $params[0] -> the parameter + // $params[1] -> the layer + if ($error = $this->_checkLayer(@$params[1])) { + $failmsg .= $error; + break; + } if (sizeof($params) < 1 || sizeof($params) > 2) { - $failmsg .= "config-get expects 1 or 2 parameters"; + $failmsg .= "config-get expects 1 or 2 parameters. Try \"help config-get\" for help"; } elseif (sizeof($params) == 1) { $this->ui->displayLine("$params[0] = " . $cf->get($params[0])); } else { @@ -104,15 +128,22 @@ class PEAR_Command_Config extends PEAR_Command_Common break; } case 'config-set': { + // $param[0] -> a parameter to set + // $param[1] -> the value for the parameter + // $param[2] -> the layer if (sizeof($params) < 2 || sizeof($params) > 3) { - $failmsg .= "config-set expects 2 or 3 parameters"; + $failmsg .= "config-set expects 2 or 3 parameters. Try \"help config-set\" for help"; + break; + } + if ($error = $this->_checkLayer(@$params[2])) { + $failmsg .= $error; break; + } + if (!call_user_func_array(array(&$cf, 'set'), $params)) + { + $failmsg = "config-set (" . implode(", ", $params) . ") failed"; } else { - if (!call_user_func_array(array($cf, 'set'), $params)) - { - $failmsg = "config-set (" . - implode(", ", $params) . ") failed"; - } + $cf->store(); } break; } @@ -127,6 +158,24 @@ class PEAR_Command_Config extends PEAR_Command_Common } // }}} + + /** + * Checks if a layer is defined or not + * + * @param string $layer The layer to search for + * @return mixed False on no error or the error message + */ + function _checkLayer($layer = null) + { + if (!empty($layer)) { + $layers = $this->config->getLayers(); + if (!in_array($layer, $layers)) { + return " only the layers: \"" . implode('" or "', $layers) . "\" are supported"; + } + } + return false; + } + } ?> \ No newline at end of file diff --git a/pear/PEAR/Command/Install.php b/pear/PEAR/Command/Install.php index 7204c309bf..e037c0a1bb 100644 --- a/pear/PEAR/Command/Install.php +++ b/pear/PEAR/Command/Install.php @@ -28,9 +28,6 @@ require_once "PEAR/Installer.php"; */ class PEAR_Command_Install extends PEAR_Command_Common { - // {{{ properties - // }}} - // {{{ constructor /** @@ -57,27 +54,68 @@ class PEAR_Command_Install extends PEAR_Command_Common return array('install', 'uninstall', 'upgrade'); } + function getHelp($command) + { + switch ($command) { + case 'install': + $ret = array('', + 'Installs a PEAR package created by the "package" command'); + break; + case 'uninstall': + $ret = array('', + 'Uninstalls a previously installed PEAR package'); + break; + case 'upgrade': + $ret = array('', + 'Upgrades a PEAR package installed in the system'); + break; + } + $ret[0] = "[-n] [-f] [-s] [-Z] {$ret[0]}"; + $ret[1] = "{$ret[1]}\n" . + " -f forces the installation of the package\n". + " when it is already installed\n". + " -n do not take care of package dependencies\n". + " -s soft update: install or upgrade only if needed\n". + " -Z no compression: download plain .tar files"; + return $ret; + } + + // }}} + // {{{ getOptions() + + function getOptions() + { + return array('f', 'n', 's', 'Z'); + } + // }}} // {{{ run() function run($command, $options, $params) { - $installer = &new PEAR_Installer($this->config->get('php_dir'), - $this->config->get('ext_dir'), - $this->config->get('doc_dir')); + $installer = &new PEAR_Installer($this->config); + $installer->setFrontend($this->ui); $installer->debug = $this->config->get('verbose'); $failmsg = ''; $opts = array(); + if (isset($options['f'])) { + $opts['force'] = true; + } + if (isset($options['n'])) { + $opts['nodeps'] = true; + } + if (isset($options['s'])) { + $opts['soft'] = true; + } + if (isset($options['Z'])) { + $opts['nocompress'] = true; + } switch ($command) { case 'upgrade': $opts['upgrade'] = true; // fall through case 'install': { - if (isset($options['f'])) { - $opts['force'] = true; - } - // XXX The ['nodeps'] option is still missing if ($installer->install(@$params[0], $opts, $this->config)) { $this->ui->displayLine("install ok"); } else { @@ -104,11 +142,6 @@ class PEAR_Command_Install extends PEAR_Command_Common } // }}} - - function getOptions() - { - return array('f'); - } } ?> \ No newline at end of file diff --git a/pear/PEAR/Command/Package.php b/pear/PEAR/Command/Package.php index a95f45a1fd..eb2d9771b2 100644 --- a/pear/PEAR/Command/Package.php +++ b/pear/PEAR/Command/Package.php @@ -1,4 +1,22 @@ | +// +----------------------------------------------------------------------+ +// +// $Id$ require_once 'PEAR/Command/Common.php'; require_once 'PEAR/Packager.php'; @@ -20,6 +38,26 @@ class PEAR_Command_Package extends PEAR_Command_Common // }}} + // {{{ _displayValidationResults() + + function _displayValidationResults($err, $warn, $strict = false) + { + foreach ($err as $e) { + $this->ui->displayLine("Error: $e"); + } + foreach ($warn as $w) { + $this->ui->displayLine("Warning: $w"); + } + $this->ui->displayLine(sprintf('Validation: %d error(s), %d warning(s)', + sizeof($err), sizeof($warn))); + if ($strict && sizeof($err) > 0) { + $this->ui->displayLine("Fix these errors and try again."); + return false; + } + return true; + } + + // }}} // {{{ getCommands() /** @@ -30,8 +68,43 @@ class PEAR_Command_Package extends PEAR_Command_Common function getCommands() { return array('package', + 'package-info', 'package-list', - 'package-info'); + 'package-validate', + 'cvstag'); + } + + // }}} + // {{{ getOptions() + + function getOptions() + { + return array('Z', 'n' /*, 'f', 'd', 'q', 'Q'*/); + } + + // }}} + // {{{ getHelp() + + function getHelp($command) + { + switch ($command) { + case 'package': + return array('[]', + 'Creates a PEAR package from its description file (usually '. + 'named as package.xml)'); + case 'package-list': + return array('', + 'List the contents of a PEAR package'); + case 'packge-info': + return array('', + 'Shows information about a PEAR package'); + case 'package-validate': + return array('', + 'Verifies a package or description file'); + case 'cvstag': + return array('', + 'Runs "cvs tag" on files contained in a release'); + } } // }}} @@ -58,13 +131,19 @@ class PEAR_Command_Package extends PEAR_Command_Common // {{{ package case 'package': { - $pkginfofile = isset($params[0]) ? $params[0] : null; + $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml'; ob_start(); $packager =& new PEAR_Packager($this->config->get('php_dir'), $this->config->get('ext_dir'), $this->config->get('doc_dir')); $packager->debug = $this->config->get('verbose'); - $result = $packager->Package($pkginfofile); + $err = $warn = array(); + $packager->validatePackageInfo($pkginfofile, $err, $warn); + if (!$this->_displayValidationResults($err, $warn, true)) { + break; + } + $compress = empty($options['Z']) ? true : false; + $result = $packager->Package($pkginfofile, $compress); $output = ob_get_contents(); ob_end_clean(); $lines = explode("\n", $output); @@ -72,9 +151,7 @@ class PEAR_Command_Package extends PEAR_Command_Common $this->ui->displayLine($line); } if (PEAR::isError($result)) { - $this->ui->displayLine("Package failed!"); - } else { - $this->ui->displayLine("Package ok."); + $this->ui->displayLine("Package failed: ".$result->getMessage()); } break; } @@ -83,7 +160,14 @@ class PEAR_Command_Package extends PEAR_Command_Common // {{{ package-list case 'package-list': { + // $params[0] -> the PEAR package to list its contents + if (sizeof($params) != 1) { + $failmsg = "Command package-list requires a valid PEAR package filename ". + " as the first argument. Try the command \"help package-list\""; + break; + } $obj = new PEAR_Common(); + if (PEAR::isError($info = $obj->infoFromTgzFile($params[0]))) { return $info; } @@ -96,9 +180,9 @@ class PEAR_Command_Package extends PEAR_Command_Common foreach ($list as $file => $att) { if (isset($att['baseinstalldir'])) { $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR . - basename($file); + $file; } else { - $dest = basename($file); + $dest = $file; } switch ($att['role']) { case 'test': @@ -204,6 +288,89 @@ class PEAR_Command_Package extends PEAR_Command_Common break; } + // }}} + // {{{ package-validate + + case 'package-validate': { + if (sizeof($params) < 1) { + $params[0] = "package.xml"; + } + $obj = new PEAR_Common; + $info = null; + if (file_exists($params[0])) { + $fp = fopen($params[0], "r"); + $test = fread($fp, 5); + fclose($fp); + if ($test == "infoFromDescriptionFile($params[0]); + } + } + if (empty($info)) { + $info = $obj->infoFromTgzFile($params[0]); + } + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + $obj->validatePackageInfo($info, $err, $warn); + $this->_displayValidationResults($err, $warn); + break; + } + + // }}} + // {{{ cvstag + + case 'cvstag': { + if (sizeof($params) < 1) { + $help = $this->getHelp($command); + return $this->raiseError("$command: missing parameter: $help[0]"); + } + $obj = new PEAR_Common; + $info = $obj->infoFromDescriptionFile($params[0]); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + $err = $warn = array(); + $obj->validatePackageInfo($info, $err, $warn); + if (!$this->_displayValidationResults($err, $warn, true)) { + break; + } + $version = $info['version']; + $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version); + $cvstag = "RELEASE_$cvsversion"; + $files = array_keys($info['filelist']); + $command = "cvs"; + /* until the getopt bug is fixed, these won't work: + if (isset($options['q'])) { + $command .= ' -q'; + } + if (isset($options['Q'])) { + $command .= ' -Q'; + } + */ + $command .= ' tag'; + if (isset($options['f'])) { + $command .= ' -f'; + } + /* neither will this one: + if (isset($options['d'])) { + $command .= ' -d'; + } + */ + $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]); + foreach ($files as $file) { + $command .= ' ' . escapeshellarg($file); + } + $this->ui->displayLine("+ $command"); + if (empty($options['n'])) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->ui->displayLine(rtrim($line)); + } + pclose($fp); + } + break; + } + // }}} default: { return false; @@ -217,7 +384,6 @@ class PEAR_Command_Package extends PEAR_Command_Common // }}} - } ?> \ No newline at end of file diff --git a/pear/PEAR/Command/Registry.php b/pear/PEAR/Command/Registry.php index 25e0c576b4..92777094ca 100644 --- a/pear/PEAR/Command/Registry.php +++ b/pear/PEAR/Command/Registry.php @@ -29,7 +29,15 @@ class PEAR_Command_Registry extends PEAR_Command_Common */ function getCommands() { - return array('list-installed'); + return array('list-installed', 'shell-test'); + } + + function getHelp($command) + { + switch ($command) { + case 'list-installed': + return array(null, 'List the installed PEAR packages in the system'); + } } // }}} @@ -54,6 +62,8 @@ class PEAR_Command_Registry extends PEAR_Command_Common $failmsg = ''; $cf = &PEAR_Config::singleton(); switch ($command) { + // {{{ list-installed + case 'list-installed': { $reg = new PEAR_Registry($cf->get('php_dir')); $installed = $reg->packageInfo(); @@ -63,16 +73,47 @@ class PEAR_Command_Registry extends PEAR_Command_Common 'border' => true)); foreach ($installed as $package) { if ($i++ % 20 == 0) { - $this->ui->tableRow(array('Package', 'Version', 'State'), - array('bold' => true)); + $this->ui->tableRow( + array('Package', 'Version', 'State'), + array('bold' => true)); } $this->ui->tableRow(array($package['package'], $package['version'], - $package['release_state'])); + @$package['release_state'])); } $this->ui->endTable(); break; } + + // }}} + case 'shell-test': { + // silence error messages for the rest of the execution + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $reg = &new PEAR_Registry($this->config->get('php_dir')); + // "pear shell-test Foo" + if (sizeof($params) == 1) { + if (!$reg->packageExists($params[0])) { + exit(1); + } + // "pear shell-test Foo 1.0" + } elseif (sizeof($params) == 2) { + $v = $reg->packageInfo($params[0], 'version'); + if (!$v || !version_compare($v, $params[1], "ge")) { + exit(1); + } + // "pear shell-test Foo ge 1.0" + } elseif (sizeof($params) == 3) { + $v = $reg->packageInfo($params[0], 'version'); + if (!$v || !version_compare($v, $params[2], $params[1])) { + exit(1); + } + } else { + PEAR::popErrorHandling(); + PEAR::raiseError("$command: expects 1 to 3 parameters"); + exit(1); + } + break; + } default: { return false; } diff --git a/pear/PEAR/CommandResponse.php b/pear/PEAR/CommandResponse.php deleted file mode 100644 index 27715505b9..0000000000 --- a/pear/PEAR/CommandResponse.php +++ /dev/null @@ -1,123 +0,0 @@ - | -// +----------------------------------------------------------------------+ -// -// $Id$ - -require_once "PEAR.php"; - -define('PEAR_COMMAND_FAILURE', 0); -define('PEAR_COMMAND_SUCCESS', 1); -define('PEAR_COMMAND_PARTIAL', 2); - -/** - * PEAR_CommandResponse is for returning an "environment-neutral" - * response to the application when a PEAR command is done. This - * means that there should be no HTML markup etc. in the message. The - * application should try rendering the message "as-is" with - * linebreaks and preferably a fixed-width font. - */ -class PEAR_CommandResponse extends PEAR -{ - /** Status code (one of the PEAR_COMMAND_* constants - * @var int - */ - var $status = null; - - /** Message for user, in plain text. - * @var string - */ - var $message = ''; - - /** Character set/encoding of $message. - * @var string - */ - var $encoding = 'ISO-8859-1'; - - /** - * Constructor. Not very exciting. - * - * @param int Command status, one of: - * PEAR_COMMAND_SUCCESS : the command was successful - * PEAR_COMMAND_FAILURE : the command failed - * PEAR_COMMAND_PARTIAL : the command was successful, - * but requires some other command to complete the - * operation (for example if the user must confirm - * something). - * @param string Message for the user. - * @param string (optional) What character encoding the message - * is in, defaults to ISO-8859-1. - * @access public - */ - function PEAR_CommandRepsonse($status, $message, $encoding = null) - { - if ($encoding !== null) { - $this->setEncoding($encoding); - } - $this->status = $status; - $this->message = $message; - } - - /** - * Get the response status code. - * @return int response status code - * @access public - */ - function getStatus() - { - return $this->status; - } - - /** - * Get the response message. - * @return string response message - * @access public - */ - function getMessage() - { - return $this->message; - } - - /** - * Get the response message charset encoding. - * @return string response message charset encoding - * @access public - */ - function getEncoding() - { - return $this->encoding; - } - - /** - * Set the response message charset encoding. - * @param string Which encoding to use, valid charsets are currently - * ISO-8859-{1-15} and UTF-8. - * @return bool true if the encoding was valid, false if not - * @access public - */ - function setEncoding($encoding) - { - if (preg_match('/^ISO-8859-([1-9]|1[1-5])$/i', $encoding) || - strcasecmp('UTF-8', $encoding) == 0) { - $this->encoding = $encoding; - return true; - } - return false; - } -} - -?> \ No newline at end of file diff --git a/pear/PEAR/Common.php b/pear/PEAR/Common.php index efbaaac2a7..63696ed630 100644 --- a/pear/PEAR/Common.php +++ b/pear/PEAR/Common.php @@ -57,16 +57,49 @@ class PEAR_Common extends PEAR var $pkginfo = array(); /** - * Permitted maintainer roles + * Valid maintainer roles * @var array */ var $maintainer_roles = array('lead','developer','contributor','helper'); /** - * Permitted release states + * Valid release states * @var array */ - var $releases_states = array('alpha','beta','stable','snapshot','devel'); + var $release_states = array('alpha','beta','stable','snapshot','devel'); + + /** + * Valid dependency types + * @var array + */ + var $dependency_types = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi'); + + /** + * Valid dependency relations + * @var array + */ + var $dependency_relations = array('has','eq','lt','le','gt','ge'); + + /** + * Valid file roles + * @var array + */ + var $file_roles = array('php','ext','test','doc','data','extsrc','script'); + + /** + * Valid replacement types + * @var array + */ + var $replacement_types = array('php-const', 'pear-config'); + + /** + * User Interface object (PEAR_Frontend_* class). If null, + * log() uses print. + * @var object + */ + var $ui = null; + + var $current_path = null; // }}} @@ -159,7 +192,11 @@ class PEAR_Common extends PEAR function log($level, $msg) { if ($this->debug >= $level) { - print "$msg\n"; + if (is_object($this->ui)) { + $this->ui->displayLine($msg); + } else { + print "$msg\n"; + } } } @@ -168,13 +205,13 @@ class PEAR_Common extends PEAR /** * Create and register a temporary directory. - * + * * @param string (optional) Directory to use as tmpdir. Will use * system defaults (for example /tmp or c:\windows\temp) if not * specified * * @return string name of created directory - * + * * @access public */ function mkTempDir($tmpdir = '') @@ -192,6 +229,15 @@ class PEAR_Common extends PEAR } // }}} + // {{{ setFrontend() + + function setFrontend(&$ui) + { + $this->ui = &$ui; + } + + // }}} + // {{{ _element_start() /** @@ -247,7 +293,7 @@ class PEAR_Common extends PEAR function _element_end($xp, $name) { } - + // }}} // Support for package DTD v1.0: @@ -284,6 +330,34 @@ class PEAR_Common extends PEAR $this->dir_role = $attribs['role']; } break; + case 'file': + if (isset($attribs['name'])) { + $path = ''; + if (count($this->dir_names)) { + foreach ($this->dir_names as $dir) { + $path .= $dir . DIRECTORY_SEPARATOR; + } + } + $path .= $attribs['name']; + unset($attribs['name']); + $this->current_path = $path; + $this->filelist[$path] = $attribs; + // Set the baseinstalldir only if the file don't have this attrib + if (!isset($this->filelist[$path]['baseinstalldir']) && + isset($this->dir_install)) + { + $this->filelist[$path]['baseinstalldir'] = $this->dir_install; + } + // Set the Role + if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { + $this->filelist[$path]['role'] = $this->dir_role; + } + } + break; + case 'replace': + $this->filelist[$this->current_path]['replacements'][] = $attribs; + break; + case 'libfile': $this->lib_atts = $attribs; $this->lib_atts['role'] = 'extsrc'; @@ -368,11 +442,7 @@ class PEAR_Common extends PEAR $this->current_maintainer['email'] = $data; break; case 'role': - if (!in_array($data, $this->maintainer_roles)) { - trigger_error("The maintainer role: '$data' is not valid", E_USER_WARNING); - } else { - $this->current_maintainer['role'] = $data; - } + $this->current_maintainer['role'] = $data; break; case 'version': $data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data); @@ -397,9 +467,7 @@ class PEAR_Common extends PEAR } break; case 'state': - if (!in_array($data, $this->releases_states)) { - trigger_error("The release state: '$data' is not valid", E_USER_WARNING); - } elseif ($this->in_changelog) { + if ($this->in_changelog) { $this->current_release['release_state'] = $data; } else { $this->pkginfo['release_state'] = $data; @@ -420,24 +488,25 @@ class PEAR_Common extends PEAR array_pop($this->dir_names); break; case 'file': - $this->current_file = $data; - $path = ''; - if (count($this->dir_names)) { - foreach ($this->dir_names as $dir) { - $path .= $dir . DIRECTORY_SEPARATOR; + if ($data) { + $path = ''; + if (count($this->dir_names)) { + foreach ($this->dir_names as $dir) { + $path .= $dir . DIRECTORY_SEPARATOR; + } + } + $path .= $data; + $this->filelist[$path] = $this->current_attributes; + // Set the baseinstalldir only if the file don't have this attrib + if (!isset($this->filelist[$path]['baseinstalldir']) && + isset($this->dir_install)) + { + $this->filelist[$path]['baseinstalldir'] = $this->dir_install; + } + // Set the Role + if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { + $this->filelist[$path]['role'] = $this->dir_role; } - } - $path .= $this->current_file; - $this->filelist[$path] = $this->current_attributes; - // Set the baseinstalldir only if the file don't have this attrib - if (!isset($this->filelist[$path]['baseinstalldir']) && - isset($this->dir_install)) - { - $this->filelist[$path]['baseinstalldir'] = $this->dir_install; - } - // Set the Role - if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { - $this->filelist[$path]['role'] = $this->dir_role; } break; case 'libfile': @@ -504,7 +573,9 @@ class PEAR_Common extends PEAR */ function _pkginfo_cdata_1_0($xp, $data) { - $this->cdata .= $data; + if (isset($this->cdata)) { + $this->cdata .= $data; + } } // }}} @@ -527,7 +598,12 @@ class PEAR_Common extends PEAR if (!@is_file($file)) { return $this->raiseError('tgz :: could not open file'); } - $tar = new Archive_Tar($file, true); + if (substr($file, -4) == '.tar') { + $compress = false; + } else { + $compress = true; + } + $tar = new Archive_Tar($file, $compress); $content = $tar->listContent(); if (!is_array($content)) { return $this->raiseError('tgz :: could not get contents of package'); @@ -694,6 +770,7 @@ class PEAR_Common extends PEAR */ function _makeReleaseXml($pkginfo, $changelog = false) { + // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!! $indent = $changelog ? " " : ""; $ret = "$indent \n"; if (!empty($pkginfo['version'])) { @@ -709,9 +786,9 @@ class PEAR_Common extends PEAR $ret .= "$indent $pkginfo[release_state]\n"; } if (!empty($pkginfo['release_notes'])) { - $ret .= "$indent $pkginfo[release_notes]\n"; + $ret .= "$indent ".htmlspecialchars($pkginfo['release_notes'])."\n"; } - if (sizeof($pkginfo['release_deps']) > 0) { + if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) { $ret .= "$indent \n"; foreach ($pkginfo['release_deps'] as $dep) { $ret .= "$indent $fa) { - if ($fa['role'] == 'extsrc') { + if (@$fa['role'] == 'extsrc') { $ret .= "$indent \n"; $ret .= "$indent $file\n"; $ret .= "$indent $fa[sources]\n"; $ret .= "$indent \n"; } else { - $ret .= "$indent $v) { + $ret .= " $k=\"" . htmlspecialchars($v) .'"'; + } + $ret .= "/>\n"; + } + @$ret .= "$indent \n"; + } } } $ret .= "$indent \n"; @@ -752,5 +847,142 @@ class PEAR_Common extends PEAR } // }}} + // {{{ validatePackageInfo() + + function validatePackageInfo($info, &$errors, &$warnings) + { + if (is_string($info) && file_exists($info)) { + $tmp = substr($info, -4); + if ($tmp == '.xml') { + $info = $this->infoFromDescriptionFile($info); + } elseif ($tmp == '.tar' || $tmp == '.tgz') { + $info = $this->infoFromTgzFile($info); + } else { + $fp = fopen($params[0], "r"); + $test = fread($fp, 5); + fclose($fp); + if ($test == "infoFromDescriptionFile($params[0]); + } else { + $info = $obj->infoFromTgzFile($params[0]); + } + } + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + } + if (!is_array($info)) { + return false; + } + $errors = array(); + $warnings = array(); + if (empty($info['package'])) { + $errors[] = 'missing package name'; + } + if (empty($info['summary'])) { + $errors[] = 'missing summary'; + } elseif (strpos(trim($info['summary']), "\n") !== false) { + $warnings[] = 'summary should be on a single line'; + } + if (empty($info['description'])) { + $errors[] = 'missing description'; + } + if (empty($info['release_license'])) { + $errors[] = 'missing license'; + } + if (empty($info['version'])) { + $errors[] = 'missing version'; + } + if (empty($info['release_state'])) { + $errors[] = 'missing release state'; + } elseif (!in_array($info['release_state'], $this->release_states)) { + $errors[] = "invalid release state `$info[release_state]', should be one of: ".implode(' ', $this->release_states); + } + if (empty($info['release_date'])) { + $errors[] = 'missing release date'; + } elseif (!preg_match('/^\d{4}-\d\d-\d\d$/', $info['release_date'])) { + $errors[] = "invalid release date `$info[release_date]', format is YYYY-MM-DD"; + } + if (empty($info['release_notes'])) { + $errors[] = "missing release notes"; + } + if (empty($info['maintainers'])) { + $errors[] = 'no maintainer(s)'; + } else { + $i = 1; + foreach ($info['maintainers'] as $m) { + if (empty($m['handle'])) { + $errors[] = "maintainer $i: missing handle"; + } + if (empty($m['role'])) { + $errors[] = "maintainer $i: missing role"; + } elseif (!in_array($m['role'], $this->maintainer_roles)) { + $errors[] = "maintainer $i: invalid role `$m[role]', should be one of: ".implode(' ', $this->maintainer_roles); + } + if (empty($m['name'])) { + $errors[] = "maintainer $i: missing name"; + } + if (empty($m['email'])) { + $errors[] = "maintainer $i: missing email"; + } + $i++; + } + } + if (!empty($info['deps'])) { + $i = 1; + foreach ($info['deps'] as $d) { + if (empty($d['type'])) { + $errors[] = "depenency $i: missing type"; + } elseif (!in_array($d['type'], $this->dependency_types)) { + $errors[] = "depenency $i: invalid type, should be one of: ".implode(' ', $this->depenency_types); + } + if (empty($d['rel'])) { + $errors[] = "dependency $i: missing relation"; + } elseif (!in_array($d['rel'], $this->dependency_relations)) { + $errors[] = "dependency $i: invalid relation, should be one of: ".implode(' ', $this->dependency_relations); + } + if ($d['rel'] != 'has' && empty($d['version'])) { + $warnings[] = "dependency $i: missing version"; + } elseif ($d['rel'] == 'has' && !empty($d['version'])) { + $warnings[] = "dependency $i: version ignored for `has' dependencies"; + } + if ($d['type'] == 'php' && !empty($d['name'])) { + $warnings[] = "dependency $i: name ignored for php type dependencies"; + } elseif ($d['type'] != 'php' && empty($d['name'])) { + $errors[] = "dependency $i: missing name"; + } + $i++; + } + } + if (empty($info['filelist'])) { + $errors[] = 'no files'; + } else { + foreach ($info['filelist'] as $file => $fa) { + if (empty($fa['role'])) { + $errors[] = "$file: missing role"; + } elseif (!in_array($fa['role'], $this->file_roles)) { + $errors[] = "$file: invalid role, should be one of: ".implode(' ', $this->file_roles); + } elseif ($fa['role'] == 'extsrc' && empty($fa['sources'])) { + $errors[] = "$file: no source files"; + } + // (ssb) Any checks we can do for baseinstalldir? + // (cox) Perhaps checks that either the target dir and baseInstall + // doesn't cointain "../../" + } + } + return true; + } + + // }}} + /** + * Get the valid roles for a PEAR package maintainer + * + * @static + */ + function getUserRoles() + { + $common = &new PEAR_Common; + return $common->maintainer_roles; + } } ?> \ No newline at end of file diff --git a/pear/PEAR/Config.php b/pear/PEAR/Config.php index 62ad1cdea3..3efe113235 100644 --- a/pear/PEAR/Config.php +++ b/pear/PEAR/Config.php @@ -27,23 +27,19 @@ require_once 'PEAR.php'; $GLOBALS['_PEAR_Config_instance'] = null; define('PEAR_CONFIG_DEFAULT_DOCDIR', - PHP_DATADIR.DIRECTORY_SEPARATOR.'pear'.DIRECTORY_SEPARATOR.'doc'); + PHP_DATADIR.DIRECTORY_SEPARATOR.'doc'.DIRECTORY_SEPARATOR.'pear'); + +// in case a --without-pear PHP installation is used +if (!defined('PEAR_INSTALL_DIR')) { + define('PEAR_INSTALL_DIR', PHP_LIBDIR); +} +if (!defined('PEAR_EXTENSION_DIR')) { + define('PEAR_EXTENSION_DIR', PHP_EXTENSION_DIR); +} + /** - * This is a class for storing simple configuration values keeping - * track of which are system-defined, user-defined or defaulted. By - * default, only user-defined settings are stored back to the user's - * configuration file. - * - * Configuration member is a simple associative array. Used keys: - * - * master_server which server to query for mirror lists etc. - * server which server/mirror we're currently using - * username PEAR username - * password PEAR password (stored base64-encoded) - * php_dir Where to install .php files - * ext_dir Directory to install compiled libs in - * doc_dir Directory to install documentation in - * + * This is a class for storing configuration data, keeping track of + * which are system-defined, user-defined or defaulted. */ class PEAR_Config extends PEAR { @@ -105,7 +101,12 @@ class PEAR_Config extends PEAR 'doc_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_DOCDIR, - 'doc' => 'directory where documentation is located', + 'doc' => 'directory where documentation is installed', + ), + 'bin_dir' => array( + 'type' => 'directory', + 'default' => PHP_BINDIR, + 'doc' => 'directory where executables are installed', ), 'username' => array( 'type' => 'string', @@ -765,6 +766,21 @@ when installing packages without a version or state specified', return isset($this->configuration[$layer]); } + // }}} + // {{{ getLayers() + + /** + * Returns the layers defined (except the 'default' one) + * + * @return array of the defined layers + */ + function getLayers() + { + $cf = $this->configuration; + unset($cf['default']); + return array_keys($cf); + } + // }}} } diff --git a/pear/PEAR/Dependency.php b/pear/PEAR/Dependency.php index c6af633f66..76327df0ef 100644 --- a/pear/PEAR/Dependency.php +++ b/pear/PEAR/Dependency.php @@ -29,7 +29,10 @@ require_once "PEAR.php"; class PEAR_Dependency { - + function PEAR_Dependency(&$registry) + { + $this->registry = &$registry; + } /** * This method maps the xml dependency definition to the * PEAR_dependecy one @@ -87,9 +90,6 @@ class PEAR_Dependency */ function checkPackage($name, $req = null, $relation = 'has') { - if (empty($this->registry)) { - $this->registry = new PEAR_Registry; - } if (!$this->registry->packageExists($name)) { return "'$name' PEAR package is not installed"; } diff --git a/pear/PEAR/Frontend/CLI.php b/pear/PEAR/Frontend/CLI.php index ae6d9c0436..e8cc5e49e4 100644 --- a/pear/PEAR/Frontend/CLI.php +++ b/pear/PEAR/Frontend/CLI.php @@ -25,6 +25,14 @@ class PEAR_Frontend_CLI extends PEAR { // {{{ properties + /** + * What type of user interface this frontend is for. + * @var string + * @access public + */ + var $type = 'CLI'; + var $lp = ''; // line prefix + var $omode = 'plain'; var $params = array(); var $term = array( @@ -56,16 +64,28 @@ class PEAR_Frontend_CLI extends PEAR // }}} - // For now, all the display functions print a "| " at the - // beginning of the line. This is just a temporary thing, it - // is for discovering commands that use print instead of - // the UI layer. - // {{{ displayLine(text) function displayLine($text) { - print "| $text\n"; + print "$this->lp$text\n"; + } + + // }}} + // {{{ displayError(eobj) + + function displayError($eobj) + { + return $this->displayLine($eobj->getMessage()); + } + + // }}} + // {{{ displayFatalError(eobj) + + function displayFatalError($eobj) + { + $this->displayError($eobj); + exit(1); } // }}} @@ -73,8 +93,8 @@ class PEAR_Frontend_CLI extends PEAR function displayHeading($title) { - print "| ".$this->bold($title)."\n"; - print "| ".str_repeat("=", strlen($title))."\n"; + print $this->lp.$this->bold($title)."\n"; + print $this->lp.str_repeat("=", strlen($title))."\n"; } // }}} @@ -85,7 +105,7 @@ class PEAR_Frontend_CLI extends PEAR if ($type == 'password') { system('stty -echo'); } - print "| $prompt "; + print "$this->lp$prompt "; if ($default) { print "[$default] "; } @@ -110,7 +130,7 @@ class PEAR_Frontend_CLI extends PEAR { static $positives = array('y', 'yes', 'on', '1'); static $negatives = array('n', 'no', 'off', '0'); - print "| $prompt [$default] : "; + print "$this->lp$prompt [$default] : "; $fp = fopen("php://stdin", "r"); $line = fgets($fp, 2048); fclose($fp); diff --git a/pear/PEAR/Installer.php b/pear/PEAR/Installer.php index 0e9dbdaf19..95e679c689 100644 --- a/pear/PEAR/Installer.php +++ b/pear/PEAR/Installer.php @@ -38,48 +38,60 @@ class PEAR_Installer extends PEAR_Common { // {{{ properties - /** name of the package directory, for example Foo-1.0 */ + /** name of the package directory, for example Foo-1.0 + * @var string + */ var $pkgdir; - /** directory where PHP code files go */ + /** directory where PHP code files go + * @var string + */ var $phpdir; - /** directory where PHP extension files go */ + /** directory where PHP extension files go + * @var string + */ var $extdir; - /** directory where documentation goes */ + /** directory where documentation goes + * @var string + */ var $docdir; /** directory where the package wants to put files, relative - * to one of the three previous dirs + * to one of the previous dirs + * @var string */ var $destdir = ''; - /** debug level (integer) */ + /** debug level + * @var int + */ var $debug = 1; - /** temporary directory */ + /** temporary directory + * @var string + */ var $tmpdir; - /** PEAR_Registry object used by the installer */ + /** PEAR_Registry object used by the installer + * @var object + */ var $registry; + /** PEAR_Config object used by the installer + * @var object + */ + var $config; + // }}} // {{{ constructor - function PEAR_Installer($phpdir = PEAR_INSTALL_DIR, - $extdir = PEAR_EXTENSION_DIR, - $docdir = null) + function PEAR_Installer(&$config) { $this->PEAR(); - $this->phpdir = $phpdir; - $this->extdir = $extdir; - if ($docdir === null) { - $docdir = PHP_DATADIR . DIRECTORY_SEPARATOR . 'pear' . - DIRECTORY_SEPARATOR . 'doc'; - } - $this->docdir = $docdir; + $this->config = &$config; } // }}} @@ -88,11 +100,11 @@ class PEAR_Installer extends PEAR_Common function _deletePackageFiles($package) { - $info = $this->registry->packageInfo($package); - if ($info == null) { + $filelist = $this->registry->packageInfo($package, 'filelist'); + if ($filelist == null) { return $this->raiseError("$package not installed"); } - foreach ($info['filelist'] as $file => $props) { + foreach ($filelist as $file => $props) { $path = $props['installed_as']; // XXX TODO: do a "rmdir -p dirname($path)" to maintain clean the fs if (!@unlink($path)) { @@ -108,30 +120,40 @@ class PEAR_Installer extends PEAR_Common function _installFile($file, $atts, $tmp_path) { - $type = strtolower($atts['role']); - switch ($type) { - case 'test': + switch ($atts['role']) { + case 'test': case 'data': case 'ext': // don't install test files for now - $this->log(2, "+ Test file $file won't be installed yet"); + $this->log(2, "+ $file: $atts[role] file not installed yet"); return true; - break; case 'doc': - $dest_dir = $this->docdir . DIRECTORY_SEPARATOR . - $this->pkginfo['package']; + $dest_dir = $this->config->get('doc_dir') . + DIRECTORY_SEPARATOR . $this->pkginfo['package']; break; + case 'extsrc': + // don't install test files for now + $this->log(2, "+ $file: no support for building extensions yet"); + return true; case 'php': - default: { - $dest_dir = $this->phpdir; - if (isset($atts['baseinstalldir'])) { - $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; - } - if (dirname($file) != '.') { - $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); - } + $dest_dir = $this->config->get('php_dir'); + break; + case 'script': { + $dest_dir = $this->config->get('bin_dir'); break; } + default: + break; + } + if (isset($atts['baseinstalldir'])) { + $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; + } + if (dirname($file) != '.') { + $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); + } + if (empty($atts['install-as'])) { + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); + } else { + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; } - $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); if (!@is_dir($dest_dir)) { if (!$this->mkDirHier($dest_dir)) { $this->log(0, "failed to mkdir $dest_dir"); @@ -140,13 +162,55 @@ class PEAR_Installer extends PEAR_Common $this->log(2, "+ created dir $dest_dir"); } $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; - $orig_perms = fileperms($orig_file); - if (!@copy($orig_file, $dest_file)) { - $this->log(0, "failed to copy $orig_file to $dest_file"); - return false; + if (empty($atts['replacements'])) { + if (!@copy($orig_file, $dest_file)) { + $this->log(0, "failed to copy $orig_file to $dest_file"); + return false; + } + $this->log(2, "+ copy $orig_file to $dest_file"); + } else { + $fp = fopen($orig_file, "r"); + $contents = fread($fp, filesize($orig_file)); + fclose($fp); + $subst_from = $subst_to = array(); + foreach ($atts['replacements'] as $a) { + $to = ''; + if ($a['type'] == 'php-const') { + if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) { + eval("\$to = $a[to];"); + } else { + $this->log(0, "invalid php-const replacement: $a[to]"); + continue; + } + } elseif ($a['type'] == 'pear-config') { + $to = $this->config->get($a['to']); + } + if ($to) { + $subst_from = $a['from']; + $subst_to = $to; + } + } + if (sizeof($subst_from)) { + $contents = str_replace($subst_from, $subst_to, $contents); + } + $wp = @fopen($dest_file, "w"); + if (!is_resource($wp)) { + $this->log(0, "failed to create $dest_file"); + return false; + } + fwrite($wp, $contents); + fclose($wp); + } + if (!OS_WINDOWS) { + if ($atts['role'] == 'script') { + $mode = 0755; + } else { + $mode = 0644; + } + if (!@chmod($dest_file, $mode)) { + $this->log(0, "failed to change mode of $dest_file"); + } } - chmod($dest_file, $orig_perms); - $this->log(2, "+ copy $orig_file to $dest_file"); // Store the full path where the file was installed for easy unistall $this->pkginfo['filelist'][$file]['installed_as'] = $dest_file; @@ -171,9 +235,10 @@ class PEAR_Installer extends PEAR_Common // recognized options: // - register_only : update registry but don't install files // - upgrade : upgrade existing install + // - soft : fail silently // if (empty($this->registry)) { - $this->registry = new PEAR_Registry($this->phpdir); + $this->registry = &new PEAR_Registry($this->config->get('php_dir')); } $oldcwd = getcwd(); $need_download = false; @@ -182,12 +247,18 @@ class PEAR_Installer extends PEAR_Common } elseif (!@is_file($pkgfile)) { if (preg_match('/^[A-Z][A-Za-z0-9_]+$/', $pkgfile)) { // valid package name + if ($this->registry->packageExists($pkgfile)) { + return $this->raiseError("$pkgfile already installed"); + } if ($config === null) { $pkgfile = "http://pear.php.net/get/$pkgfile"; } else { $pkgfile = "http://" . $config->get('master_server') . "/get/$pkgfile"; } + if (!extension_loaded("zlib")) { + $pkgfile .= '?uncompress=yes'; + } $need_download = true; } else { return $this->raiseError("could not open the package file: $pkgfile"); @@ -260,7 +331,7 @@ class PEAR_Installer extends PEAR_Common // ==> XXX This part should be removed later on $flag_old_format = false; if (!is_file($descfile)) { - // ----- Look for old package .tgz archive format + // ----- Look for old package archive format // In this format the package.xml file was inside the package directory name $dp = opendir($tmpdir); do { @@ -291,7 +362,9 @@ class PEAR_Installer extends PEAR_Common if (isset($pkginfo['release_deps']) && !isset($options['nodeps'])) { $error = $this->checkDeps($pkginfo); if ($error) { - $this->log(0, $error); + if (empty($options['soft'])) { + $this->log(0, $error); + } return $this->raiseError('Dependencies failed'); } } @@ -314,7 +387,7 @@ class PEAR_Installer extends PEAR_Common } if (empty($options['register_only'])) { // when upgrading, remove old release's files first: - $this->_deletePackageFiles($package); + $this->_deletePackageFiles($pkgname); } } @@ -323,7 +396,7 @@ class PEAR_Installer extends PEAR_Common // info from the package it self we want to access from _installFile $this->pkginfo = $pkginfo; if (empty($options['register_only'])) { - if (!is_dir($this->phpdir)) { + if (!is_dir($this->config->get('php_dir'))) { chdir($oldcwd); return $this->raiseError("no script destination directory\n", null, PEAR_ERROR_DIE); @@ -365,7 +438,7 @@ class PEAR_Installer extends PEAR_Common function uninstall($package) { if (empty($this->registry)) { - $this->registry = new PEAR_Registry($this->phpdir); + $this->registry = new PEAR_Registry($this->config->get('php_dir')); } // Delete the files @@ -380,7 +453,7 @@ class PEAR_Installer extends PEAR_Common function checkDeps(&$pkginfo) { - $deps = new PEAR_Dependency; + $deps = &new PEAR_Dependency($this->registry); $errors = null; if (is_array($pkginfo['release_deps'])) { foreach($pkginfo['release_deps'] as $dep) { diff --git a/pear/PEAR/Packager.php b/pear/PEAR/Packager.php index c314e55f8d..7930203f70 100644 --- a/pear/PEAR/Packager.php +++ b/pear/PEAR/Packager.php @@ -51,7 +51,6 @@ class PEAR_Packager extends PEAR_Common function _PEAR_Packager() { - chdir($this->orig_pwd); $this->_PEAR_Common(); } @@ -59,7 +58,7 @@ class PEAR_Packager extends PEAR_Common // {{{ package() - function package($pkgfile = null) + function package($pkgfile = null, $compress = true) { $this->orig_pwd = getcwd(); if (empty($pkgfile)) { @@ -67,7 +66,7 @@ class PEAR_Packager extends PEAR_Common } $pkginfo = $this->infoFromDescriptionFile($pkgfile); if (PEAR::isError($pkginfo)) { - return $pkginfo; + return $this->raiseError($pkginfo); } // XXX This needs to be checked in infoFromDescriptionFile // or at least a helper method to do the proper checks @@ -108,10 +107,10 @@ class PEAR_Packager extends PEAR_Common } $new_xml = $this->xmlFromInfo($pkginfo); if (PEAR::isError($new_xml)) { - return $new_xml; + return $this->raiseError($new_xml); } $tmpdir = $this->mkTempDir(getcwd()); - $newpkgfile = $tmpdir . DIRECTORY_SEPARATOR . $pkgfile; + $newpkgfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml'; $np = @fopen($newpkgfile, "w"); if (!$np) { return $this->raiseError("PEAR_Packager: unable to rewrite $pkgfile"); @@ -120,13 +119,14 @@ class PEAR_Packager extends PEAR_Common fclose($np); // TAR the Package ------------------------------------------- - $dest_package = $this->orig_pwd . DIRECTORY_SEPARATOR . "{$pkgver}.tgz"; - $tar =& new Archive_Tar($dest_package, true); + $ext = $compress ? '.tgz' : '.tar'; + $dest_package = $this->orig_pwd . DIRECTORY_SEPARATOR . $pkgver . $ext; + $tar =& new Archive_Tar($dest_package, $compress); $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors // ----- Creates with the package.xml file $ok = $tar->createModify($newpkgfile, '', $tmpdir); if (PEAR::isError($ok)) { - return $ok; + return $this->raiseError($ok); } elseif (!$ok) { return $this->raiseError('PEAR_Packager: tarball creation failed'); } @@ -137,11 +137,23 @@ class PEAR_Packager extends PEAR_Common $this->log(1, "Package $dest_package done"); $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pkgversion); $cvstag = "RELEASE_$cvsversion"; - $this->log(1, "CVS release tag: $cvstag"); + $this->log(0, "Tag the released code with `pear cvstag $pkgfile'"); + $this->log(0, "(or set the CVS tag $cvstag by hand)"); return $dest_package; } // }}} } +if (!function_exists('md5_file')) { + function md5_file($file) { + if (!$fd = @fopen($file, 'r')) { + return false; + } + $md5 = md5(fread($fd, filesize($file))); + fclose($fd); + return $md5; + } +} + ?> \ No newline at end of file diff --git a/pear/PEAR/Registry.php b/pear/PEAR/Registry.php index c09e3abddb..3d84120bcc 100644 --- a/pear/PEAR/Registry.php +++ b/pear/PEAR/Registry.php @@ -57,7 +57,7 @@ class PEAR_Registry extends PEAR // }}} - // {{{ PEAR_Registry + // {{{ constructor /** * PEAR_Registry constructor. @@ -79,7 +79,7 @@ class PEAR_Registry extends PEAR } // }}} - // {{{ _PEAR_Registry + // {{{ destructor /** * PEAR_Registry destructor. Makes sure no locks are forgotten. @@ -208,26 +208,36 @@ class PEAR_Registry extends PEAR */ function _lock($mode = LOCK_EX) { - if ($mode != LOCK_UN && is_resource($this->lock_fp)) { - // XXX does not check type of lock (LOCK_SH/LOCK_EX) - return true; - } - if (PEAR::isError($err = $this->_assertStateDir())) { - return $err; - } - $this->lock_fp = @fopen($this->lockfile, 'w'); - if (!is_resource($this->lock_fp)) { - return $this->raiseError("could not create lock file: $php_errormsg"); - } - if (!(int)flock($this->lock_fp, $mode)) { - switch ($mode) { - case LOCK_SH: $str = 'shared'; break; - case LOCK_EX: $str = 'exclusive'; break; - case LOCK_UN: $str = 'unlock'; break; - default: $str = 'unknown'; break; + if(!strstr(php_uname(), 'Windows 95/98')) { + if ($mode != LOCK_UN && is_resource($this->lock_fp)) { + // XXX does not check type of lock (LOCK_SH/LOCK_EX) + return true; + } + if (PEAR::isError($err = $this->_assertStateDir())) { + return $err; + } + $open_mode = 'w'; + // XXX People reported problems with LOCK_SH and 'w' + if ($mode === LOCK_SH) { + if (@!is_file($this->lockfile)) { + touch($this->lockfile); + } + $open_mode = 'r'; + } + $this->lock_fp = @fopen($this->lockfile, $open_mode); + if (!is_resource($this->lock_fp)) { + return $this->raiseError("could not create lock file: $php_errormsg"); + } + if (!(int)flock($this->lock_fp, $mode)) { + switch ($mode) { + case LOCK_SH: $str = 'shared'; break; + case LOCK_EX: $str = 'exclusive'; break; + case LOCK_UN: $str = 'unlock'; break; + default: $str = 'unknown'; break; + } + return $this->raiseError("could not acquire $str lock ($this->lockfile)", + PEAR_REGISTRY_ERROR_LOCK); } - return $this->raiseError("could not acquire $str lock ($this->lockfile)", - PEAR_REGISTRY_ERROR_LOCK); } return true; } diff --git a/pear/PEAR/Remote.php b/pear/PEAR/Remote.php index 1f20db6508..482ff88dde 100644 --- a/pear/PEAR/Remote.php +++ b/pear/PEAR/Remote.php @@ -38,7 +38,7 @@ class PEAR_Remote extends PEAR function PEAR_Remote(&$config) { $this->PEAR(); - $this->config = $config; + $this->config = &$config; } // }}} @@ -50,6 +50,8 @@ class PEAR_Remote extends PEAR if (!extension_loaded("xmlrpc")) { return $this->raiseError("xmlrpc support not loaded"); } + $params = func_get_args(); + array_shift($params); $method = str_replace("_", ".", $method); $request = xmlrpc_encode_request($method, $params); $server_host = $this->config->get("master_server"); diff --git a/pear/PEAR/Uploader.php b/pear/PEAR/Uploader.php deleted file mode 100644 index 99212f6813..0000000000 --- a/pear/PEAR/Uploader.php +++ /dev/null @@ -1,60 +0,0 @@ - | -// +----------------------------------------------------------------------+ -// - -require_once "PEAR/Common.php"; - -/** - * Administration class used to install PEAR packages and maintain the - * installed package database. - * - * @since PHP 4.0.2 - * @author Stig Bakken - */ -class PEAR_Uploader extends PEAR_Common -{ - // {{{ properties - - var $_tempfiles = array(); - - // }}} - - // {{{ constructor - - function PEAR_Uploader() - { - $this->PEAR_Common(); - } - - // }}} - - function upload($pkgfile, $infofile = null) - { - if ($infofile === null) { - $info = $this->infoFromTarBall($pkgfile); - } else { - $info = $this->infoFromDescriptionFile($infofile); - } - if (PEAR::isError($info)) { - return $info; - } - - } -} - -?> diff --git a/pear/package.dtd b/pear/package.dtd index a0cb045559..a77af2d393 100644 --- a/pear/package.dtd +++ b/pear/package.dtd @@ -1,10 +1,10 @@