summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Joye <pajoye@php.net>2003-09-29 14:06:44 +0000
committerPierre Joye <pajoye@php.net>2003-09-29 14:06:44 +0000
commitc2a66c59c620e3933248038e61f15248199ee836 (patch)
tree1e69e834f36648cad0d447e785899290444ca68a
parentcb9b99262e4ab7a67924c71fec17d5a1125fce67 (diff)
downloadphp-git-c2a66c59c620e3933248038e61f15248199ee836.tar.gz
MFH 4.3.4RC1
-rw-r--r--pear/OS/Guess.php2
-rw-r--r--pear/PEAR.php17
-rw-r--r--pear/PEAR/Command/Install.php59
-rw-r--r--pear/PEAR/Common.php219
-rw-r--r--pear/PEAR/Dependency.php47
-rw-r--r--pear/PEAR/Frontend/CLI.php5
-rw-r--r--pear/PEAR/Installer.php527
-rw-r--r--pear/PEAR/Registry.php2
-rw-r--r--pear/System.php86
-rw-r--r--pear/package-PEAR.xml318
-rw-r--r--pear/package.dtd2
-rwxr-xr-xpear/scripts/pear.bat18
12 files changed, 937 insertions, 365 deletions
diff --git a/pear/OS/Guess.php b/pear/OS/Guess.php
index 7cbd988af0..c6852fef25 100644
--- a/pear/OS/Guess.php
+++ b/pear/OS/Guess.php
@@ -167,7 +167,7 @@ class OS_Guess
$cpp = popen("/usr/bin/cpp $tmpfile", "r");
$major = $minor = 0;
while ($line = fgets($cpp, 1024)) {
- if ($line{0} == '#') {
+ if ($line{0} == '#' || trim($line) == '') {
continue;
}
if (list($major, $minor) = explode(' ', trim($line))) {
diff --git a/pear/PEAR.php b/pear/PEAR.php
index ec98d5576f..da40082604 100644
--- a/pear/PEAR.php
+++ b/pear/PEAR.php
@@ -289,7 +289,8 @@ class PEAR
function setErrorHandling($mode = null, $options = null)
{
- if (isset($this)) {
+ if (isset($this) &&
+ (get_class($this) == 'pear' || is_subclass_of($this, 'pear'))) {
$setmode = &$this->_default_error_mode;
$setoptions = &$this->_default_error_options;
} else {
@@ -310,9 +311,8 @@ class PEAR
case PEAR_ERROR_CALLBACK:
$setmode = $mode;
- if ((is_string($options) && function_exists($options)) ||
- (is_array($options) && method_exists(@$options[0], @$options[1])))
- {
+ // class/object method callback
+ if (is_callable($options)) {
$setoptions = $options;
} else {
trigger_error("invalid error callback", E_USER_WARNING);
@@ -566,7 +566,8 @@ class PEAR
function pushErrorHandling($mode, $options = null)
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
- if (isset($this)) {
+ if (isset($this) &&
+ (get_class($this) == 'pear' || is_subclass_of($this, 'pear'))) {
$def_mode = &$this->_default_error_mode;
$def_options = &$this->_default_error_options;
} else {
@@ -575,7 +576,8 @@ class PEAR
}
$stack[] = array($def_mode, $def_options);
- if (isset($this)) {
+ if (isset($this) &&
+ (get_class($this) == 'pear' || is_subclass_of($this, 'pear'))) {
$this->setErrorHandling($mode, $options);
} else {
PEAR::setErrorHandling($mode, $options);
@@ -600,7 +602,8 @@ class PEAR
array_pop($stack);
list($mode, $options) = $stack[sizeof($stack) - 1];
array_pop($stack);
- if (isset($this)) {
+ if (isset($this) &&
+ (get_class($this) == 'pear' || is_subclass_of($this, 'pear'))) {
$this->setErrorHandling($mode, $options);
} else {
PEAR::setErrorHandling($mode, $options);
diff --git a/pear/PEAR/Command/Install.php b/pear/PEAR/Command/Install.php
index 8026a7bfb1..9cac11a058 100644
--- a/pear/PEAR/Command/Install.php
+++ b/pear/PEAR/Command/Install.php
@@ -20,7 +20,6 @@
require_once "PEAR/Command/Common.php";
require_once "PEAR/Installer.php";
-require_once "Console/Getopt.php";
/**
* PEAR commands for installation or deinstallation/upgrading of
@@ -69,6 +68,14 @@ class PEAR_Command_Install extends PEAR_Command_Common
'ignore-errors' => array(
'doc' => 'force install even if there were errors',
),
+ 'alldeps' => array(
+ 'shortopt' => 'a',
+ 'doc' => 'install all required and optional dependencies',
+ ),
+ 'onlyreqdeps' => array(
+ 'shortopt' => 'o',
+ 'doc' => 'install all required dependencies',
+ ),
),
'doc' => '<package> ...
Installs one or more PEAR packages. You can specify a package to
@@ -123,6 +130,14 @@ four ways of specifying packages.
'ignore-errors' => array(
'doc' => 'force install even if there were errors',
),
+ 'alldeps' => array(
+ 'shortopt' => 'a',
+ 'doc' => 'install all required and optional dependencies',
+ ),
+ 'onlyreqdeps' => array(
+ 'shortopt' => 'o',
+ 'doc' => 'install all required dependencies',
+ ),
),
'doc' => '<package> ...
Upgrades one or more PEAR packages. See documentation for the
@@ -240,7 +255,7 @@ package if needed.
$this->installer = &new PEAR_Installer($this->ui);
}
if ($command == 'upgrade') {
- $options[$command] = true;
+ $options['upgrade'] = true;
}
if ($command == 'upgrade-all') {
include_once "PEAR/Remote.php";
@@ -273,9 +288,25 @@ package if needed.
$this->ui->outputData(array('data' => "Will upgrade $package"), $command);
}
}
- foreach ($params as $pkg) {
- $bn = basename($pkg);
- $info = $this->installer->install($pkg, $options, $this->config);
+ $errors = array();
+ $downloaded = array();
+ $this->installer->download($params, $options, $this->config, $downloaded,
+ $errors);
+ if ($command != 'upgrade-all') {
+ for ($i = 0; $i < count($params); $i++) {
+ $params[$i] = $this->installer->extractDownloadFileName($params[$i], $_tmp);
+ }
+ }
+ if (count($errors)) {
+ $err['data'] = array($errors);
+ $err['headline'] = 'Install Errors';
+ $this->ui->outputData($err);
+ return $this->raiseError("$command failed");
+ }
+ $this->installer->sortPkgDeps($downloaded);
+ foreach ($downloaded as $pkg) {
+ $bn = basename($pkg['file']);
+ $info = $this->installer->install($pkg['file'], $options, $this->config);
if (is_array($info)) {
if ($this->config->get('verbose') > 0) {
$label = "$info[package] $info[version]";
@@ -303,6 +334,24 @@ package if needed.
if (sizeof($params) < 1) {
return $this->raiseError("Please supply the package(s) you want to uninstall");
}
+ include_once 'PEAR/Registry.php';
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $newparams = array();
+ $badparams = array();
+ foreach ($params as $pkg) {
+ $info = $reg->packageInfo($pkg);
+ if ($info === null) {
+ $badparams[] = $pkg;
+ } else {
+ $newparams[] = $info;
+ }
+ }
+ PEAR_Common::sortPkgDeps($newparams, true);
+ $params = array();
+ foreach($newparams as $info) {
+ $params[] = $info['info']['package'];
+ }
+ $params = array_merge($params, $badparams);
foreach ($params as $pkg) {
if ($this->installer->uninstall($pkg, $options)) {
if ($this->config->get('verbose') > 0) {
diff --git a/pear/PEAR/Common.php b/pear/PEAR/Common.php
index 81f88b6643..e4cfa7515f 100644
--- a/pear/PEAR/Common.php
+++ b/pear/PEAR/Common.php
@@ -26,7 +26,10 @@ require_once 'PEAR/Config.php';
// {{{ constants and globals
-define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^([A-Z][a-zA-Z0-9_]+|[a-z][a-z0-9_]+)$/');
+define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^[A-Za-z][a-zA-Z0-9_]+$/');
+
+// XXX far from perfect :-)
+define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^([A-Za-z][a-zA-Z0-9_]+)(-([.0-9a-zA-Z]+))?$/');
/**
* List of temporary files and directories registered by
@@ -214,11 +217,11 @@ class PEAR_Common extends PEAR
*
* @access public
*/
- function log($level, $msg)
+ function log($level, $msg, $append_crlf = true)
{
if ($this->debug >= $level) {
if (is_object($this->ui)) {
- $this->ui->log($msg);
+ $this->ui->log($msg, $append_crlf);
} else {
print "$msg\n";
}
@@ -329,6 +332,11 @@ class PEAR_Common extends PEAR
$elem_start = '_element_start_'. $vs;
$elem_end = '_element_end_'. $vs;
$cdata = '_pkginfo_cdata_'. $vs;
+ if (!method_exists($this, $elem_start) ||
+ !method_exists($this, $elem_end) ||
+ !method_exists($this, $cdata)) {
+ $this->raiseError("No handlers for package.xml version $attribs[version]");
+ }
xml_set_element_handler($xp, $elem_start, $elem_end);
xml_set_character_data_handler($xp, $cdata);
break;
@@ -699,7 +707,7 @@ class PEAR_Common extends PEAR
break;
}
}
- $tmpdir = System::mkTemp('-d pear');
+ $tmpdir = System::mkTemp(array('-d', 'pear'));
$this->addTempFile($tmpdir);
if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
return $this->raiseError('could not extract the package.xml file');
@@ -924,6 +932,9 @@ class PEAR_Common extends PEAR
if (isset($dep['version'])) {
$ret .= " version=\"$dep[version]\"";
}
+ if (isset($dep['optional'])) {
+ $ret .= " optional=\"$dep[optional]\"";
+ }
if (isset($dep['name'])) {
$ret .= ">$dep[name]</dep>\n";
} else {
@@ -1081,7 +1092,7 @@ class PEAR_Common extends PEAR
$i = 1;
foreach ($info['deps'] as $d) {
if (empty($d['type'])) {
- $errors[] = "depenency $i: missing type";
+ $errors[] = "dependency $i: missing type";
} elseif (!in_array($d['type'], $_PEAR_Common_dependency_types)) {
$errors[] = "dependency $i: invalid type, should be one of: ".implode(' ', $_PEAR_Common_depenency_types);
}
@@ -1090,6 +1101,11 @@ class PEAR_Common extends PEAR
} elseif (!in_array($d['rel'], $_PEAR_Common_dependency_relations)) {
$errors[] = "dependency $i: invalid relation, should be one of: ".implode(' ', $_PEAR_Common_dependency_relations);
}
+ if (!empty($d['optional'])) {
+ if (!in_array($d['optional'], array('yes', 'no'))) {
+ $errors[] = "dependency $i: invalid relation optional attribute, should be one of: yes no";
+ }
+ }
if ($d['rel'] != 'has' && empty($d['version'])) {
$warnings[] = "dependency $i: missing version";
} elseif ($d['rel'] == 'has' && !empty($d['version'])) {
@@ -1350,6 +1366,30 @@ class PEAR_Common extends PEAR
}
// }}}
+ // {{{ betterStates()
+
+ /**
+ * Return an array containing all of the states that are more stable than
+ * or equal to the passed in state
+ *
+ * @param string Release state
+ * @param boolean Determines whether to include $state in the list
+ * @return false|array False if $state is not a valid release state
+ */
+ function betterStates($state, $include = false)
+ {
+ static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
+ $i = array_search($state, $states);
+ if ($i === false) {
+ return false;
+ }
+ if ($include) {
+ $i--;
+ }
+ return array_slice($states, $i + 1);
+ }
+
+ // }}}
// {{{ detectDependencies()
function detectDependencies($any, $status_callback = null)
@@ -1659,7 +1699,7 @@ class PEAR_Common extends PEAR
}
$bytes = 0;
if ($callback) {
- call_user_func($callback, 'start', $length);
+ call_user_func($callback, 'start', array(basename($dest_file), $length));
}
while ($data = @fread($fp, 1024)) {
$bytes += strlen($data);
@@ -1683,6 +1723,173 @@ class PEAR_Common extends PEAR
}
// }}}
+ // {{{ sortPkgDeps()
+
+ /**
+ * Sort a list of arrays of array(downloaded packagefilename) by dependency.
+ *
+ * It also removes duplicate dependencies
+ * @param array
+ * @param boolean Sort packages in reverse order if true
+ * @return array array of array(packagefilename, package.xml contents)
+ */
+ function sortPkgDeps(&$packages, $uninstall = false)
+ {
+ $ret = array();
+ if ($uninstall) {
+ foreach($packages as $packageinfo) {
+ $ret[] = array('info' => $packageinfo);
+ }
+ } else {
+ foreach($packages as $packagefile) {
+ if (!is_array($packagefile)) {
+ $ret[] = array('file' => $packagefile,
+ 'info' => $a = $this->infoFromAny($packagefile),
+ 'pkg' => $a['package']);
+ } else {
+ $ret[] = $packagefile;
+ }
+ }
+ }
+ $checkdupes = array();
+ $newret = array();
+ foreach($ret as $i => $p) {
+ if (!isset($checkdupes[$p['info']['package']])) {
+ $checkdupes[$p['info']['package']][] = $i;
+ $newret[] = $p;
+ }
+ }
+
+ $func = $uninstall ? '_sortPkgDepsRev' : '_sortPkgDeps';
+ usort($newret, array('PEAR_Common', $func));
+ $packages = $newret;
+ }
+
+ // }}}
+ // {{{ _sortPkgDeps()
+
+ /**
+ * Compare two package's package.xml, and sort
+ * so that dependencies are installed first
+ *
+ * This is a crude compare, real dependency checking is done on install.
+ * The only purpose this serves is to make the command-line
+ * order-independent (you can list a dependent package first, and
+ * installation occurs in the order required)
+ * @access private
+ */
+ function _sortPkgDeps($p1, $p2)
+ {
+ $p1name = $p1['info']['package'];
+ $p2name = $p2['info']['package'];
+ $p1deps = PEAR_Common::_getPkgDeps($p1);
+ $p2deps = PEAR_Common::_getPkgDeps($p2);
+ if (!count($p1deps) && !count($p2deps)) {
+ return 0; // order makes no difference
+ }
+ if (!count($p1deps)) {
+ return -1; // package 2 has dependencies, package 1 doesn't
+ }
+ if (!count($p2deps)) {
+ return 1; // package 2 has dependencies, package 1 doesn't
+ }
+ // both have dependencies
+ if (in_array($p1name, $p2deps)) {
+ return -1; // put package 1 first
+ }
+ if (in_array($p2name, $p1deps)) {
+ return 1; // put package 2 first
+ }
+ // doesn't really matter if neither depends on the other
+ return 0;
+ }
+
+ // }}}
+ // {{{ _sortPkgDepsRev()
+
+ /**
+ * Compare two package's package.xml, and sort
+ * so that dependencies are uninstalled last
+ *
+ * This is a crude compare, real dependency checking is done on uninstall.
+ * The only purpose this serves is to make the command-line
+ * order-independent (you can list a dependency first, and
+ * uninstallation occurs in the order required)
+ * @access private
+ */
+ function _sortPkgDepsRev($p1, $p2)
+ {
+ $p1name = $p1['info']['package'];
+ $p2name = $p2['info']['package'];
+ $p1deps = PEAR_Common::_getRevPkgDeps($p1);
+ $p2deps = PEAR_Common::_getRevPkgDeps($p2);
+ if (!count($p1deps) && !count($p2deps)) {
+ return 0; // order makes no difference
+ }
+ if (!count($p1deps)) {
+ return 1; // package 2 has dependencies, package 1 doesn't
+ }
+ if (!count($p2deps)) {
+ return -1; // package 2 has dependencies, package 1 doesn't
+ }
+ // both have dependencies
+ if (in_array($p1name, $p2deps)) {
+ return 1; // put package 1 last
+ }
+ if (in_array($p2name, $p1deps)) {
+ return -1; // put package 2 last
+ }
+ // doesn't really matter if neither depends on the other
+ return 0;
+ }
+
+ // }}}
+ // {{{ _getPkgDeps()
+
+ /**
+ * get an array of package dependency names
+ * @access private
+ */
+ function _getPkgDeps($p)
+ {
+ if (!isset($p['info']['releases'])) {
+ return array();
+ }
+ $rel = array_shift($p['info']['releases']);
+ if (!isset($rel['deps'])) {
+ return array();
+ }
+ $ret = array();
+ foreach($rel['deps'] as $dep) {
+ if ($dep['type'] == 'pkg') {
+ $ret[] = $dep['name'];
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
+ // {{{ _getRevPkgDeps()
+
+ /**
+ * get an array of package dependency names for uninstall
+ * @access private
+ */
+ function _getRevPkgDeps($p)
+ {
+ if (!isset($p['info']['release_deps'])) {
+ return array();
+ }
+ $ret = array();
+ foreach($p['info']['release_deps'] as $dep) {
+ if ($dep['type'] == 'pkg') {
+ $ret[] = $dep['name'];
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
}
?>
diff --git a/pear/PEAR/Dependency.php b/pear/PEAR/Dependency.php
index 37afd71a36..3aca0ebc7e 100644
--- a/pear/PEAR/Dependency.php
+++ b/pear/PEAR/Dependency.php
@@ -26,6 +26,7 @@ define('PEAR_DEPENDENCY_CONFLICT', -2);
define('PEAR_DEPENDENCY_UPGRADE_MINOR', -3);
define('PEAR_DEPENDENCY_UPGRADE_MAJOR', -4);
define('PEAR_DEPENDENCY_BAD_DEPENDENCY', -5);
+define('PEAR_DEPENDENCY_MISSING_OPTIONAL', -6);
/**
* Dependency check for PEAR packages
@@ -54,13 +55,16 @@ class PEAR_Dependency
* This method maps the XML dependency definition to the
* corresponding one from PEAR_Dependency
*
+ * <pre>
* $opts => Array
* (
* [type] => pkg
* [rel] => ge
* [version] => 3.4
* [name] => HTML_Common
+ * [optional] => false
* )
+ * </pre>
*
* @param string Error message
* @param array Options
@@ -71,13 +75,15 @@ class PEAR_Dependency
$rel = isset($opts['rel']) ? $opts['rel'] : 'has';
$req = isset($opts['version']) ? $opts['version'] : null;
$name = isset($opts['name']) ? $opts['name'] : null;
+ $opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ?
+ $opts['optional'] : null;
$errmsg = '';
switch ($opts['type']) {
case 'pkg':
- return $this->checkPackage($errmsg, $name, $req, $rel);
+ return $this->checkPackage($errmsg, $name, $req, $rel, $opt);
break;
case 'ext':
- return $this->checkExtension($errmsg, $name, $req, $rel);
+ return $this->checkExtension($errmsg, $name, $req, $rel, $opt);
break;
case 'php':
return $this->checkPHP($errmsg, $req, $rel);
@@ -105,10 +111,12 @@ class PEAR_Dependency
* @param string $name Name of the package to test
* @param string $version The package version required
* @param string $relation How to compare versions with eachother
+ * @param bool $opt Whether the relationship is optional
*
* @return mixed bool false if no error or the error string
*/
- function checkPackage(&$errmsg, $name, $req = null, $relation = 'has')
+ function checkPackage(&$errmsg, $name, $req = null, $relation = 'has',
+ $opt = false)
{
if (substr($relation, 0, 2) == 'v.') {
$relation = substr($relation, 2);
@@ -116,6 +124,10 @@ class PEAR_Dependency
switch ($relation) {
case 'has':
if (!$this->registry->packageExists($name)) {
+ if ($opt) {
+ $errmsg = "package `$name' is recommended to utilize some features.";
+ return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+ }
$errmsg = "requires package `$name'";
return PEAR_DEPENDENCY_MISSING;
}
@@ -136,10 +148,14 @@ class PEAR_Dependency
if (!$this->registry->packageExists($name)
|| !version_compare("$version", "$req", $relation))
{
+ $code = $this->codeFromRelation($relation, $version, $req);
+ if ($opt) {
+ $errmsg = "package `$name' version $req is recommended to utilize some features.";
+ return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+ }
$errmsg = "requires package `$name' " .
$this->signOperator($relation) . " $req";
- $code = $this->codeFromRelation($relation, $version, $req);
- return PEAR_DEPENDENCY_MISSING;
+ return $code;
}
return false;
}
@@ -151,11 +167,12 @@ class PEAR_Dependency
* Check package dependencies on uninstall
*
* @param string $error The resultant error string
+ * @param string $warning The resultant warning string
* @param string $name Name of the package to test
*
* @return bool true if there were errors
*/
- function checkPackageUninstall(&$error, $package)
+ function checkPackageUninstall(&$error, &$warning, $package)
{
$error = null;
$packages = $this->registry->listPackages();
@@ -169,10 +186,14 @@ class PEAR_Dependency
}
foreach ($deps as $dep) {
if ($dep['type'] == 'pkg' && strcasecmp($dep['name'], $package) == 0) {
+ if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+ $warning .= "\nWarning: Package '$pkg' optionally depends on '$package'";
+ } else {
$error .= "Package '$pkg' depends on '$package'\n";
}
}
}
+ }
return ($error) ? true : false;
}
@@ -182,13 +203,19 @@ class PEAR_Dependency
* @param string $name Name of the extension to test
* @param string $req_ext_ver Required extension version to compare with
* @param string $relation How to compare versions with eachother
+ * @param bool $opt Whether the relationship is optional
*
* @return mixed bool false if no error or the error string
*/
- function checkExtension(&$errmsg, $name, $req = null, $relation = 'has')
+ function checkExtension(&$errmsg, $name, $req = null, $relation = 'has',
+ $opt = false)
{
// XXX (ssb): could we avoid loading the extension here?
if (!PEAR::loadExtension($name)) {
+ if ($opt) {
+ $errmsg = "'$name' PHP extension is recommended to utilize some features";
+ return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+ }
$errmsg = "'$name' PHP extension is not installed";
return PEAR_DEPENDENCY_MISSING;
}
@@ -202,9 +229,13 @@ class PEAR_Dependency
// Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90)
settype($req, "string");
if (!version_compare("$ext_ver", "$req", $operator)) {
- $retval = "'$name' PHP extension version " .
+ $errmsg = "'$name' PHP extension version " .
$this->signOperator($operator) . " $req is required";
$code = $this->codeFromRelation($relation, $ext_ver, $req);
+ if ($opt) {
+ $errmsg = "'$name' PHP extension version $req is recommended to utilize some features";
+ return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+ }
}
}
return $code;
diff --git a/pear/PEAR/Frontend/CLI.php b/pear/PEAR/Frontend/CLI.php
index 81326d27b4..a5cb8f2649 100644
--- a/pear/PEAR/Frontend/CLI.php
+++ b/pear/PEAR/Frontend/CLI.php
@@ -472,10 +472,13 @@ class PEAR_Frontend_CLI extends PEAR
// {{{ log(text)
- function log($text)
+ function log($text, $append_crlf = true)
{
+ if ($append_crlf) {
return $this->_displayLine($text);
}
+ return $this->_display($text);
+ }
// }}}
diff --git a/pear/PEAR/Installer.php b/pear/PEAR/Installer.php
index 416fab9991..4592d88898 100644
--- a/pear/PEAR/Installer.php
+++ b/pear/PEAR/Installer.php
@@ -125,7 +125,7 @@ class PEAR_Installer extends PEAR_Common
// {{{ _deletePackageFiles()
/**
- * Delete a package's installed files, remove empty directories.
+ * Delete a package's installed files, does not remove empty directories.
*
* @param string $package package name
*
@@ -155,7 +155,14 @@ class PEAR_Installer extends PEAR_Common
// }}}
// {{{ _installFile()
- function _installFile($file, $atts, $tmp_path)
+ /**
+ * @param string filename
+ * @param array attributes from <file> tag in package.xml
+ * @param string path to install the file in
+ * @param array options from command-line
+ * @access private
+ */
+ function _installFile($file, $atts, $tmp_path, $options)
{
static $os;
if (isset($atts['platform'])) {
@@ -192,6 +199,7 @@ class PEAR_Installer extends PEAR_Common
default:
return $this->raiseError("Invalid role `$atts[role]' for file $file");
}
+ $save_destdir = $dest_dir;
if (!empty($atts['baseinstalldir'])) {
$dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
}
@@ -222,6 +230,10 @@ class PEAR_Installer extends PEAR_Common
$this->log(3, "+ mkdir $dest_dir");
}
if (empty($atts['replacements'])) {
+ if (!file_exists($orig_file)) {
+ return $this->raiseError("file does not exist",
+ PEAR_INSTALLER_FAILED);
+ }
if (!@copy($orig_file, $dest_file)) {
return $this->raiseError("failed to write $dest_file",
PEAR_INSTALLER_FAILED);
@@ -231,6 +243,10 @@ class PEAR_Installer extends PEAR_Common
$md5sum = md5_file($dest_file);
}
} else {
+ if (!file_exists($orig_file)) {
+ return $this->raiseError("file does not exist",
+ PEAR_INSTALLER_FAILED);
+ }
$fp = fopen($orig_file, "r");
$contents = fread($fp, filesize($orig_file));
fclose($fp);
@@ -249,10 +265,19 @@ class PEAR_Installer extends PEAR_Common
}
} elseif ($a['type'] == 'pear-config') {
$to = $this->config->get($a['to']);
+ if (is_null($to)) {
+ $this->log(0, "invalid pear-config replacement: $a[to]");
+ continue;
+ }
} elseif ($a['type'] == 'package-info') {
+ if (isset($this->pkginfo[$a['to']]) && is_string($this->pkginfo[$a['to']])) {
$to = $this->pkginfo[$a['to']];
+ } else {
+ $this->log(0, "invalid package-info replacement: $a[to]");
+ continue;
+ }
}
- if ($to) {
+ if (!is_null($to)) {
$subst_from[] = $a['from'];
$subst_to[] = $to;
}
@@ -273,12 +298,19 @@ class PEAR_Installer extends PEAR_Common
fclose($wp);
}
if (isset($md5sum)) {
- if ($md5sum == $atts['md5sum']) {
- $this->log(3, "md5sum ok: $final_dest_file");
+ if (strtolower($md5sum) == strtolower($atts['md5sum'])) {
+ $this->log(2, "md5sum ok: $final_dest_file");
+ } else {
+ if (empty($options['force'])) {
+ // delete the file
+ @unlink($dest_file);
+ return $this->raiseError("bad md5sum for file $final_dest_file",
+ PEAR_INSTALLER_FAILED);
} else {
$this->log(0, "warning : bad md5sum for file $final_dest_file");
}
}
+ }
if (!OS_WINDOWS) {
if ($atts['role'] == 'script') {
$mode = 0777 & ~(int)octdec($this->config->get('umask'));
@@ -292,10 +324,9 @@ class PEAR_Installer extends PEAR_Common
}
}
$this->addFileOperation("rename", array($dest_file, $final_dest_file));
-
- // XXX SHOULD BE DONE ONLY AFTER COMMIT
// Store the full path where the file was installed for easy unistall
- $this->pkginfo['filelist'][$file]['installed_as'] = $installed_as;
+ $this->addFileOperation("installed_as", array($file, $installed_as,
+ $save_destdir, dirname(substr($dest_file, strlen($save_destdir)))));
//$this->log(2, "installed: $dest_file");
return PEAR_INSTALLER_OK;
@@ -304,8 +335,34 @@ class PEAR_Installer extends PEAR_Common
// }}}
// {{{ addFileOperation()
+ /**
+ * Add a file operation to the current file transaction.
+ *
+ * @see startFileTransaction()
+ * @var string $type This can be one of:
+ * - rename: rename a file ($data has 2 values)
+ * - chmod: change permissions on a file ($data has 2 values)
+ * - delete: delete a file ($data has 1 value)
+ * - rmdir: delete a directory if empty ($data has 1 value)
+ * - installed_as: mark a file as installed ($data has 4 values).
+ * @var array $data For all file operations, this array must contain the
+ * full path to the file or directory that is being operated on. For
+ * the rename command, the first parameter must be the file to rename,
+ * the second its new name.
+ *
+ * The installed_as operation contains 4 elements in this order:
+ * 1. Filename as listed in the filelist element from package.xml
+ * 2. Full path to the installed file
+ * 3. Full path from the php_dir configuration variable used in this
+ * installation
+ * 4. Relative path from the php_dir that this file is installed in
+ */
function addFileOperation($type, $data)
{
+ if (!is_array($data)) {
+ return $this->raiseError('Internal Error: $data in addFileOperation'
+ . ' must be an array, was ' . gettype($data));
+ }
if ($type == 'chmod') {
$octmode = decoct($data[0]);
$this->log(3, "adding to transaction: $type $octmode $data[1]");
@@ -339,6 +396,9 @@ class PEAR_Installer extends PEAR_Common
list($type, $data) = $tr;
switch ($type) {
case 'rename':
+ if (!file_exists($data[0])) {
+ $errors[] = "cannot rename file $data[0], doesn't exist";
+ }
// check that dest dir. is writable
if (!is_writable(dirname($data[1]))) {
$errors[] = "permission denied ($type): $data[1]";
@@ -347,10 +407,13 @@ class PEAR_Installer extends PEAR_Common
case 'chmod':
// check that file is writable
if (!is_writable($data[1])) {
- $errors[] = "permission denied ($type): $data[1]";
+ $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
}
break;
case 'delete':
+ if (!file_exists($data[0])) {
+ $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
+ }
// check that directory is writable
if (file_exists($data[0]) && !is_writable(dirname($data[0]))) {
$errors[] = "permission denied ($type): $data[0]";
@@ -371,11 +434,12 @@ class PEAR_Installer extends PEAR_Common
list($type, $data) = $tr;
switch ($type) {
case 'rename':
+ @unlink($data[1]);
@rename($data[0], $data[1]);
$this->log(3, "+ mv $data[0] $data[1]");
break;
case 'chmod':
- @chmod($data[0], $data[1]);
+ @chmod($data[1], $data[0]);
$octmode = decoct($data[0]);
$this->log(3, "+ chmod $octmode $data[1]");
break;
@@ -387,9 +451,21 @@ class PEAR_Installer extends PEAR_Common
@rmdir($data[0]);
$this->log(3, "+ rmdir $data[0]");
break;
+ case 'installed_as':
+ $this->pkginfo['filelist'][$data[0]]['installed_as'] = $data[1];
+ if (!isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
+ $this->pkginfo['filelist']['dirtree'][dirname($data[1])] = true;
+ while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
+ && $data[3] != '.') {
+ $this->pkginfo['filelist']['dirtree']
+ [$this->_prependPath($data[3], $data[2])] = true;
+ $data[3] = dirname($data[3]);
+ }
+ }
+ break;
}
}
- $this->log(2, "successfully commited $n file operations");
+ $this->log(2, "successfully committed $n file operations");
$this->file_operations = array();
return true;
}
@@ -416,6 +492,21 @@ class PEAR_Installer extends PEAR_Common
break;
case 'delete':
break;
+ case 'installed_as':
+ unset($this->pkginfo['filelist'][$data[0]]['installed_as']);
+ if (isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
+ unset($this->pkginfo['filelist']['dirtree'][dirname($data[1])]);
+ while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
+ && $data[3] != '.') {
+ unset($this->pkginfo['filelist']['dirtree']
+ [$this->_prependPath($data[3], $data[2])]);
+ $data[3] = dirname($data[3]);
+ }
+ }
+ if (!count($this->pkginfo['filelist']['dirtree'])) {
+ unset($this->pkginfo['filelist']['dirtree']);
+ }
+ break;
}
}
$this->file_operations = array();
@@ -424,8 +515,11 @@ class PEAR_Installer extends PEAR_Common
// }}}
// {{{ getPackageDownloadUrl()
- function getPackageDownloadUrl($package)
+ function getPackageDownloadUrl($package, $version = null)
{
+ if ($version) {
+ $package .= "-$version";
+ }
if ($this === null || $this->config === null) {
$package = "http://pear.php.net/get/$package";
} else {
@@ -464,75 +558,334 @@ class PEAR_Installer extends PEAR_Common
}
// }}}
+ // {{ extractDownloadFileName($pkgfile, &$version)
- // {{{ install()
+ function extractDownloadFileName($pkgfile, &$version)
+ {
+ if (@is_file($pkgfile)) {
+ return $pkgfile;
+ }
+ // regex defined in Common.php
+ if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG, $pkgfile, $m)) {
+ $version = (isset($m[3])) ? $m[3] : null;
+ return $m[1];
+ }
+ $version = null;
+ return $pkgfile;
+ }
+ // }}}
+ // {{{ _downloadFile()
/**
- * Installs the files within the package file specified.
- *
- * @param $pkgfile path to the package file
- *
- * @return array package info if successful, null if not
+ * @param string filename to download
+ * @param PEAR_Config Configuration object
+ * @param array options returned from Console_GetOpt
+ * @param array empty array to populate with error messages, if any
+ * @param string version/state
+ * @param string original value passed to command-line
+ * @param string preferred state (snapshot/devel/alpha/beta/stable)
+ * @access private
*/
-
- function install($pkgfile, $options = array())
+ function _downloadFile($pkgfile, &$config, $options, &$errors, $version,
+ $origpkgfile, $state)
{
- // recognized options:
- // - force : force installation
- // - register-only : update registry but don't install files
- // - upgrade : upgrade existing install
- // - soft : fail silently
- //
- $php_dir = $this->config->get('php_dir');
- if (isset($options['installroot'])) {
- if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
- $options['installroot'] = substr($options['installroot'], 0, -1);
- }
- $php_dir = $this->_prependPath($php_dir, $options['installroot']);
- $this->installroot = $options['installroot'];
- } else {
- $this->installroot = '';
- }
- $this->registry = &new PEAR_Registry($php_dir);
$need_download = false;
- // ==> XXX should be removed later on
- $flag_old_format = false;
if (preg_match('#^(http|ftp)://#', $pkgfile)) {
$need_download = true;
} elseif (!@is_file($pkgfile)) {
if ($this->validPackageName($pkgfile)) {
- if ($this->registry->packageExists($pkgfile) &&
- empty($options['upgrade']) && empty($options['force']))
- {
- return $this->raiseError("$pkgfile already installed");
+ if ($this->registry->packageExists($pkgfile)) {
+ if (empty($options['upgrade']) && empty($options['force'])) {
+ $errors[] = "$pkgfile already installed";
+ return;
+ }
}
- $pkgfile = $this->getPackageDownloadUrl($pkgfile);
+ $pkgfile = $this->getPackageDownloadUrl($pkgfile, $version);
$need_download = true;
} else {
if (strlen($pkgfile)) {
- return $this->raiseError("Could not open the package file: $pkgfile");
+ $errors[] = "Could not open the package file: $pkgfile";
} else {
- return $this->raiseError("No package file given");
+ $errors[] = "No package file given";
}
+ return;
}
}
// Download package -----------------------------------------------
if ($need_download) {
- $downloaddir = $this->config->get('download_dir');
+ $downloaddir = $config->get('download_dir');
if (empty($downloaddir)) {
if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
return $downloaddir;
}
- $this->log(2, '+ tmp dir created at ' . $downloaddir);
+ $this->log(3, '+ tmp dir created at ' . $downloaddir);
}
$callback = $this->ui ? array(&$this, '_downloadCallback') : null;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
$file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+ $this->popErrorHandling();
if (PEAR::isError($file)) {
+ if ($this->validPackageName($origpkgfile)) {
+ include_once 'PEAR/Remote.php';
+ $remote = new PEAR_Remote($config);
+ if (!PEAR::isError($info = $remote->call('package.info',
+ $origpkgfile))) {
+ if (!count($info['releases'])) {
+ return $this->raiseError('Package ' . $origpkgfile .
+ ' has no releases');
+ } else {
+ return $this->raiseError('No releases of preferred state "'
+ . $state . '" exist for package ' . $origpkgfile .
+ '. Use ' . $origpkgfile . '-state to install another' .
+ ' state (like ' . $origpkgfile .'-beta)');
+ }
+ } else {
+ return $pkgfile;
+ }
+ } else {
return $this->raiseError($file);
}
+ }
$pkgfile = $file;
}
+ return $pkgfile;
+ }
+
+ // }}}
+ // {{{ download()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * @param array a mixed list of package names, local files, or package.xml
+ * @param PEAR_Config
+ * @param array options from the command line
+ * @param array this is the array that will be populated with packages to
+ * install. Format of each entry:
+ *
+ * <code>
+ * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+ * 'info' => array() // parsed package.xml
+ * );
+ * </code>
+ * @param array this will be populated with any error messages
+ * @param false private recursion variable
+ * @param false private recursion variable
+ * @param false private recursion variable
+ */
+ function download($packages, $options, &$config, &$installpackages,
+ &$errors, $installed = false, $willinstall = false, $state = false)
+ {
+ // recognized options:
+ // - onlyreqdeps : install all required dependencies as well
+ // - alldeps : install all dependencies, including optional
+ //
+ if (!$willinstall) {
+ $willinstall = array();
+ }
+ if (!$state) {
+ $state = $config->get('preferred_state');
+ if (!$state) {
+ // don't inadvertantly use a non-set preferred_state
+ $state = null;
+ }
+ }
+ $mywillinstall = array();
+ $php_dir = $config->get('php_dir');
+ if (isset($options['installroot'])) {
+ if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $options['installroot'] = substr($options['installroot'], 0, -1);
+ }
+ $php_dir = $this->_prependPath($php_dir, $options['installroot']);
+ $this->installroot = $options['installroot'];
+ } else {
+ $this->installroot = '';
+ }
+ $this->registry = &new PEAR_Registry($php_dir);
+
+ // download files in this list if necessary
+ foreach($packages as $pkgfile) {
+ if (!is_file($pkgfile)) {
+ $origpkgfile = $pkgfile;
+ $pkgfile = $this->extractDownloadFileName($pkgfile, $version);
+ if ($version === null) {
+ // use preferred state if no version number was specified
+ $version = $state;
+ }
+ if ($this->validPackageName($pkgfile) && !isset($options['upgrade'])) {
+ if ($this->registry->packageExists($pkgfile)) {
+ $this->log(0, "Package '$pkgfile' already installed, skipping");
+ // ignore dependencies that are installed unless we are upgrading
+ continue;
+ }
+ }
+ $pkgfile = $this->_downloadFile($pkgfile, $config, $options, $errors,
+ $version, $origpkgfile, $state);
+ if (PEAR::isError($pkgfile)) {
+ return $pkgfile;
+ }
+ }
+ $tempinfo = $this->infoFromAny($pkgfile);
+ if (isset($options['alldeps']) || isset($options['onlyreqdeps'])) {
+ // ignore dependencies if there are any errors
+ if (!PEAR::isError($tempinfo)) {
+ $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
+ }
+ }
+ $installpackages[] = array('pkg' => $tempinfo['package'],
+ 'file' => $pkgfile, 'info' => $tempinfo);
+ }
+
+ // extract dependencies from downloaded files and then download them
+ // if necessary
+ if (isset($options['alldeps']) || isset($options['onlyreqdeps'])) {
+ include_once "PEAR/Remote.php";
+ $remote = new PEAR_Remote($config);
+ if (!$installed) {
+ $installed = $this->registry->listPackages();
+ array_walk($installed, create_function('&$v,$k','$v = strtolower($v);'));
+ $installed = array_flip($installed);
+ }
+ $deppackages = array();
+ // construct the list of dependencies for each file
+ foreach ($mywillinstall as $package => $alldeps) {
+ if (!is_array($alldeps)) {
+ continue;
+ }
+ foreach($alldeps as $info) {
+ if ($info['type'] != 'pkg') {
+ continue;
+ }
+ if (!isset($options['alldeps']) && isset($info['optional']) &&
+ $info['optional'] == 'yes') {
+ // skip optional deps
+ $this->log(0, "skipping Package $package optional dependency $info[name]");
+ continue;
+ }
+ // get releases
+ $releases = $remote->call('package.info', $info['name'], 'releases');
+ if (PEAR::isError($releases)) {
+ return $releases;
+ }
+ if (!count($releases)) {
+ if (!isset($installed[strtolower($info['name'])])) {
+ $errors[] = "Package $package dependency $info[name] ".
+ "has no releases";
+ }
+ continue;
+ }
+ $found = false;
+ $save = $releases;
+ while(count($releases) && !$found) {
+ if (!empty($state) && $state != 'any') {
+ list($release_version,$release) = each($releases);
+ if ($state != $release['state'] &&
+ !in_array($release['state'],
+ $this->betterStates($state))) {
+ // drop this release - it ain't stable enough
+ array_shift($releases);
+ } else {
+ $found = true;
+ }
+ } else {
+ $found = true;
+ }
+ }
+ if (!count($releases) && !$found) {
+ $get = array();
+ foreach($save as $release) {
+ $get = array_merge($get,
+ $this->betterStates($release['state'], true));
+ }
+ $savestate = array_shift($get);
+ $errors[] = "Release for $package dependency $info[name] " .
+ "has state '$savestate', requires $state";
+ continue;
+ }
+ if (in_array(strtolower($info['name']), $willinstall) ||
+ isset($mywillinstall[strtolower($info['name'])])) {
+ // skip upgrade check for packages we will install
+ continue;
+ }
+ if (!isset($installed[strtolower($info['name'])])) {
+ // skip upgrade check for packages we don't have installed
+ $deppackages[] = $info['name'];
+ continue;
+ }
+
+ // see if a dependency must be upgraded
+ $inst_version = $this->registry->packageInfo($info['name'], 'version');
+ if (!isset($info['version'])) {
+ // this is a rel='has' dependency, check against latest
+ if (version_compare($release_version, $inst_version, 'le')) {
+ continue;
+ } else {
+ $deppackages[] = $info['name'];
+ continue;
+ }
+ }
+ if (version_compare($info['version'], $inst_version, 'le')) {
+ // installed version is up-to-date
+ continue;
+ }
+ $deppackages[] = $info['name'];
+ } // foreach($alldeps
+ } // foreach($willinstall
+
+ if (count($deppackages)) {
+ // check dependencies' dependencies
+ // combine the list of packages to install
+ $temppack = array();
+ foreach($installpackages as $p) {
+ $temppack[] = strtolower($p['info']['package']);
+ }
+ foreach($deppackages as $pack) {
+ $temppack[] = strtolower($pack);
+ }
+ $willinstall = array_merge($willinstall, $temppack);
+ $this->download($deppackages, $options, $config, $installpackages,
+ $errors, $installed, $willinstall, $state);
+ }
+ } // if --alldeps or --onlyreqdeps
+ }
+
+ // }}}
+ // {{{ install()
+
+ /**
+ * Installs the files within the package file specified.
+ *
+ * @param string $pkgfile path to the package file
+ * @param array $options
+ * recognized options:
+ * - installroot : optional prefix directory for installation
+ * - force : force installation
+ * - register-only : update registry but don't install files
+ * - upgrade : upgrade existing install
+ * - soft : fail silently
+ * - nodeps : ignore dependency conflicts/missing dependencies
+ * - alldeps : install all dependencies
+ * - onlyreqdeps : install only required dependencies
+ *
+ * @return array package info if successful, null if not
+ */
+
+ function install($pkgfile, $options = array())
+ {
+ $php_dir = $this->config->get('php_dir');
+ if (isset($options['installroot'])) {
+ if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $options['installroot'] = substr($options['installroot'], 0, -1);
+ }
+ $php_dir = $this->_prependPath($php_dir, $options['installroot']);
+ $this->installroot = $options['installroot'];
+ } else {
+ $this->installroot = '';
+ }
+ $this->registry = &new PEAR_Registry($php_dir);
+ // ==> XXX should be removed later on
+ $flag_old_format = false;
if (substr($pkgfile, -4) == '.xml') {
$descfile = $pkgfile;
@@ -549,7 +902,7 @@ class PEAR_Installer extends PEAR_Common
if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
return $tmpdir;
}
- $this->log(2, '+ tmp dir created at ' . $tmpdir);
+ $this->log(3, '+ tmp dir created at ' . $tmpdir);
$tar = new Archive_Tar($pkgfile);
if (!@$tar->extract($tmpdir)) {
@@ -637,6 +990,8 @@ class PEAR_Installer extends PEAR_Common
}
}
+ $this->startFileTransaction();
+
if (empty($options['upgrade'])) {
// checks to do only when installing new packages
if (empty($options['force']) && $this->registry->packageExists($pkgname)) {
@@ -644,9 +999,10 @@ class PEAR_Installer extends PEAR_Common
}
} else {
// checks to do only when upgrading packages
- if (!$this->registry->packageExists($pkgname)) {
+/* if (!$this->registry->packageExists($pkgname)) {
return $this->raiseError("$pkgname not installed");
- }
+ }*/
+ if ($this->registry->packageExists($pkgname)) {
$v1 = $this->registry->packageInfo($pkgname, 'version');
$v2 = $pkginfo['version'];
$cmp = version_compare("$v1", "$v2", 'gt');
@@ -660,6 +1016,7 @@ class PEAR_Installer extends PEAR_Common
}
}
}
+ }
// Copy files to dest dir ---------------------------------------
@@ -690,11 +1047,14 @@ class PEAR_Installer extends PEAR_Common
foreach ($pkginfo['filelist'] as $file => $atts) {
$this->expectError(PEAR_INSTALLER_FAILED);
- $res = $this->_installFile($file, $atts, $tmp_path);
+ $res = $this->_installFile($file, $atts, $tmp_path, $options);
$this->popExpect();
if (PEAR::isError($res)) {
if (empty($options['ignore-errors'])) {
$this->rollbackFileTransaction();
+ if ($res->getMessage() == "file does not exist") {
+ $this->raiseError("file $file in package.xml does not exist");
+ }
return $this->raiseError($res);
} else {
$this->log(0, "Warning: " . $res->getMessage());
@@ -750,8 +1110,13 @@ class PEAR_Installer extends PEAR_Common
}
$ret = $this->registry->addPackage($pkgname, $pkginfo);
} else {
+ // new: upgrade installs a package if it isn't installed
+ if (!$this->registry->packageExists($pkgname)) {
+ $ret = $this->registry->addPackage($pkgname, $pkginfo);
+ } else {
$ret = $this->registry->updatePackage($pkgname, $pkginfo, false);
}
+ }
if (!$ret) {
return null;
}
@@ -761,6 +1126,16 @@ class PEAR_Installer extends PEAR_Common
// }}}
// {{{ uninstall()
+ /**
+ * Uninstall a package
+ *
+ * This method removes all files installed by the application, and then
+ * removes any empty directories.
+ * @param string package name
+ * @param array Command-line options. Possibilities include:
+ *
+ * - installroot: base installation dir, if not the default
+ */
function uninstall($package, $options = array())
{
$php_dir = $this->config->get('php_dir');
@@ -774,13 +1149,21 @@ class PEAR_Installer extends PEAR_Common
$this->installroot = '';
}
$this->registry = &new PEAR_Registry($php_dir);
+ $filelist = $this->registry->packageInfo($package, 'filelist');
+ if ($filelist == null) {
+ return $this->raiseError("$package not installed");
+ }
if (empty($options['nodeps'])) {
$depchecker = &new PEAR_Dependency($this->registry);
- $error = $depchecker->checkPackageUninstall($errors, $package);
+ $error = $depchecker->checkPackageUninstall($errors, $warning, $package);
if ($error) {
return $this->raiseError($errors . 'uninstall failed');
}
+ if ($warning) {
+ $this->log(0, $warning);
+ }
}
+ $this->startFileTransaction();
// Delete the files
if (PEAR::isError($err = $this->_deletePackageFiles($package))) {
$this->rollbackFileTransaction();
@@ -789,6 +1172,19 @@ class PEAR_Installer extends PEAR_Common
if (!$this->commitFileTransaction()) {
$this->rollbackFileTransaction();
return $this->raiseError("uninstall failed");
+ } else {
+ $this->startFileTransaction();
+ if (!isset($filelist['dirtree']) || !count($filelist['dirtree'])) {
+ return $this->registry->deletePackage($package);
+ }
+ // attempt to delete empty directories
+ uksort($filelist['dirtree'], array($this, '_sortDirs'));
+ foreach($filelist['dirtree'] as $dir => $notused) {
+ $this->addFileOperation('rmdir', array($dir));
+ }
+ if (!$this->commitFileTransaction()) {
+ $this->rollbackFileTransaction();
+ }
}
// Register that the package is no longer installed
@@ -796,6 +1192,15 @@ class PEAR_Installer extends PEAR_Common
}
// }}}
+ // {{{ _sortDirs()
+ function _sortDirs($a, $b)
+ {
+ if (strnatcmp($a, $b) == -1) return 1;
+ if (strnatcmp($a, $b) == 1) return -1;
+ return 0;
+ }
+
+ // }}}
// {{{ checkDeps()
/**
@@ -818,10 +1223,11 @@ class PEAR_Installer extends PEAR_Common
$code = $depchecker->callCheckMethod($error, $dep);
if ($code) {
if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+/* die ugly hack die
// Ugly hack to adjust the error messages
$error = str_replace('requires ', '', $error);
$error = ucfirst($error);
- $error = $error . ' is recommended to utilize some features.';
+ $error = $error . ' is recommended to utilize some features.';*/
$optional_deps[] = array($dep, $code, $error);
} else {
$failed_deps[] = array($dep, $code, $error);
@@ -893,6 +1299,19 @@ class PEAR_Installer extends PEAR_Common
case 'done':
$this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
break;
+ case 'bytesread':
+ static $bytes;
+ if (empty($bytes)) {
+ $bytes = 0;
+ }
+ if (!($bytes % 10240)) {
+ $this->log(1, '.', false);
+ }
+ $bytes += $params;
+ break;
+ case 'start':
+ $this->log(1, "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)");
+ break;
}
if (method_exists($this->ui, '_downloadCallback'))
$this->ui->_downloadCallback($msg, $params);
diff --git a/pear/PEAR/Registry.php b/pear/PEAR/Registry.php
index 96ac23087f..c28d2039ae 100644
--- a/pear/PEAR/Registry.php
+++ b/pear/PEAR/Registry.php
@@ -276,7 +276,7 @@ class PEAR_Registry extends PEAR
}
$open_mode = 'w';
// XXX People reported problems with LOCK_SH and 'w'
- if ($mode === LOCK_SH) {
+ if ($mode === LOCK_SH || $mode === LOCK_UN) {
if (@!is_file($this->lockfile)) {
touch($this->lockfile);
}
diff --git a/pear/System.php b/pear/System.php
index 8f5e5fae51..5915b6b5d2 100644
--- a/pear/System.php
+++ b/pear/System.php
@@ -41,6 +41,11 @@ $GLOBALS['_System_temp_files'] = array();
* print "could not delete file1 or dir1";
* }
*
+* In case you need to to pass file names with spaces,
+* pass the params as an array:
+*
+* System::rm(array('-r', $file1, $dir1));
+*
* @package System
* @author Tomas V.V.Cox <cox@idecnet.com>
* @version $Revision$
@@ -123,17 +128,17 @@ class System
}
closedir($dir);
sort($list);
+ if ($aktinst < $maxinst || $maxinst == 0) {
foreach($list as $val) {
$path = $sPath . DIRECTORY_SEPARATOR . $val;
if (is_dir($path)) {
- if ($aktinst < $maxinst || $maxinst == 0) {
$tmp = System::_dirToStruct($path, $maxinst, $aktinst+1);
$struct = array_merge_recursive($tmp, $struct);
- }
} else {
$struct['files'][] = $path;
}
}
+ }
return $struct;
}
@@ -445,5 +450,82 @@ class System
}
return $fallback;
}
+
+ /**
+ * The "find" command
+ *
+ * Usage:
+ *
+ * System::find($dir);
+ * System::find("$dir -type d");
+ * System::find("$dir -type f");
+ * System::find("$dir -name *.php");
+ * System::find("$dir -name *.php -name *.htm*");
+ * System::find("$dir -maxdepth 1");
+ *
+ * Params implmented:
+ * $dir -> Start the search at this directory
+ * -type d -> return only directories
+ * -type f -> return only files
+ * -maxdepth <n> -> max depth of recursion
+ * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
+ *
+ * @param mixed Either array or string with the command line
+ * @return array Array of found files
+ *
+ */
+ function find($args)
+ {
+ if (!is_array($args)) {
+ $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
+ }
+ $dir = array_shift($args);
+ $patterns = array();
+ $depth = 0;
+ $do_files = $do_dirs = true;
+ for ($i = 0; $i < count($args); $i++) {
+ switch ($args[$i]) {
+ case '-type':
+ if (in_array($args[$i+1], array('d', 'f'))) {
+ if ($args[$i+1] == 'd') {
+ $do_files = false;
+ } else {
+ $do_dirs = false;
+ }
+ }
+ $i++;
+ break;
+ case '-name':
+ $patterns[] = "(" . preg_replace(array('/\./', '/\*/'),
+ array('\.', '.*'),
+ $args[$i+1])
+ . ")";
+ $i++;
+ break;
+ case '-maxdepth':
+ $depth = $args[$i+1];
+ break;
+ }
+ }
+ $path = System::_dirToStruct($dir, $depth);
+ if ($do_files && $do_dirs) {
+ $files = array_merge($path['files'], $path['dirs']);
+ } elseif ($do_dirs) {
+ $files = $path['dirs'];
+ } else {
+ $files = $path['files'];
+ }
+ if (count($patterns)) {
+ $patterns = implode('|', $patterns);
+ $ret = array();
+ for ($i = 0; $i < count($files); $i++) {
+ if (preg_match("#^$patterns\$#", $files[$i])) {
+ $ret[] = $files[$i];
+ }
+ }
+ return $ret;
+ }
+ return $files;
+ }
}
?>
diff --git a/pear/package-PEAR.xml b/pear/package-PEAR.xml
index f6b2563bc2..7bf4fb4a9c 100644
--- a/pear/package-PEAR.xml
+++ b/pear/package-PEAR.xml
@@ -19,7 +19,7 @@
</maintainer>
<maintainer>
<user>cox</user>
- <role>developer</role>
+ <role>lead</role>
<name>Tomas V.V.Cox</name>
<email>cox@idecnet.com</email>
</maintainer>
@@ -35,15 +35,55 @@
<name>Pierre-Alain Joye</name>
<email>pajoye@pearfr.org</email>
</maintainer>
+ <maintainer>
+ <user>cellog</user>
+ <role>developer</role>
+ <name>Greg Beaver</name>
+ <email>cellog@php.net</email>
+ </maintainer>
</maintainers>
<release>
- <version>1.2.1</version>
- <state>stable</state>
- <date>2003-08-15</date>
+ <version>1.3b1</version>
+ <date>2003-09-29</date>
+ <state>beta</state>
<notes>
-- Set back the default library path (BC issues)
- </notes>
+PEAR Base Class:
+
+* Fixed static calls to PEAR error-handling methods in classes
+* Added ability to use a static method callback for error-handling,
+ and removed use of inadvisable @ in setErrorHandling
+
+PEAR Installer:
+
+* Fixed #25117 - MD5 checksum should be case-insensitive
+* Added dependency on XML_RPC, and optional dependency on xmlrpc extension
+* Added --alldeps and --onlyreqdeps options to pear install/pear upgrade
+* Sorting of installation/uninstallation so package order on the command-line is
+ insignificant (fixes upgrade-all if every package is installed)
+* pear upgrade will now install if the package is not installed (necessary for
+ pear upgrade --alldeps, as installation is often necessary for new
+ dependencies)
+* fixed pear.bat if PHP is installed in a path like C:\Program Files\php
+* Added ability to specify "pear install package-version" or
+ "pear install package-state". For example: "pear install DB-1.2",
+ or "pear install DB-stable"
+* Fix #25008 - unhelpful error message
+* Fixed optional dependencies in Dependency.php
+* Fix #25322 - bad md5sum should be fatal error
+* Package uninstall now also removes empty directories
+* Fixed locking problems for reading commands (pear list, pear info)
+
+OS_Guess Class:
+
+* Fixed #25131 - OS_Guess warnings on empty lines from
+ popen(&quot;/usr/bin/cpp $tmpfile&quot;, &quot;r&quot;);
+
+System Class:
+
+* Fixed recursion deep param in _dirToStruct()
+* Added the System::find() command (read API doc for more info)
+ </notes>
<provides type="class" name="OS_Guess" />
<provides type="class" name="System" />
<provides type="function" name="md5_file" />
@@ -105,270 +145,8 @@
<dep type="php" rel="ge" version="4.1"/>
<dep type="pkg" rel="ge" version="0.4">Archive_Tar</dep>
<dep type="pkg" rel="ge" version="0.11">Console_Getopt</dep>
+ <dep type="pkg" rel="ge" version="1.0.4">XML_RPC</dep>
+ <dep type="ext" rel="has" optional="yes">xmlrpc</dep>
</deps>
</release>
- <changelog>
- <release>
- <version>1.2</version>
- <state>stable</state>
- <date>2003-08-14</date>
- <notes>
-Changes from 1.1:
-
-* Changed license from PHP 2.02 to 3.0
-* Added support for optional dependencies
-* Made upgrade and uninstall package case insensitive
-* pear makerpm, now works and generates a better system independant spec file
-* pear install|build pecl-package, now exposes the compilation progress
-* Installer now checks dependencies on package uninstall
-* Added proxy support for remote commands using the xmlrcp C ext (Adam Ashley)
-* Added the command "download-all" (Alex Merz)
-* Made package dependency checking back to work
-* Added support for spaces in path names (Greg)
-* Various bugfixes
-* Added new pear "bundle" command, which downloads and uncompress a PECL package.
-The main purpouse of this command is for easily adding extensions to the PHP sources
-before compiling it.
- </notes>
- </release>
- <release>
- <version>1.1</version>
- <state>stable</state>
- <date>2003-01-10</date>
- <notes>
-PEAR BASE CLASS:
-
-* PEAR_Error now supports exceptions when using Zend Engine 2. Set the
- error mode to PEAR_ERROR_EXCEPTION to make PEAR_Error throw itself
- as an exception (invoke PEAR errors with raiseError() or throwError()
- just like before).
-
-PEAR INSTALLER:
-
-* Packaging and validation now parses PHP source code (unless
- ext/tokenizer is disabled) and does some coding standard conformance
- checks. Specifically, the names of classes and functions are
- checked to ensure that they are prefixed with the package name. If
- your package has symbols that should be without this prefix, you can
- override this warning by explicitly adding a "provides" entry in
- your package.xml file. See the package.xml file for this release
- for an example (OS_Guess, System and md5_file).
-
- All classes and non-private (not underscore-prefixed) methods and
- functions are now registered during "pear package".
- </notes>
- </release>
- <release>
- <version>1.0.1</version>
- <state>stable</state>
- <date>2003-01-10</date>
- <notes>
- * PEAR_Error class has call backtrace available by
- calling getBacktrace(). Available if used with
- PHP 4.3 or newer.
-
- * PEAR_Config class uses getenv() rather than $_ENV
- to read environment variables.
-
- * System::which() Windows fix, now looks for
- exe/bat/cmd/com suffixes rather than just exe
-
- * Added "pear cvsdiff" command
-
- * Windows output buffering bugfix for "pear" command
-
- * Multiple drives installation now works on windows
-
- * pear.bat uses ENV variables, allowing the installation
- of many PEAR (windows)
- </notes>
- <deps>
- <dep type="php" rel="ge" version="4.1"/>
- <dep type="pkg" rel="ge" version="0.4">Archive_Tar</dep>
- <dep type="pkg" rel="ge" version="0.11">Console_Getopt</dep>
- </deps>
- </release>
- <release>
- <version>1.0</version>
- <state>stable</state>
- <date>2002-12-27</date>
- <notes>
- * set default cache_ttl to 1 hour
- * added "clear-cache" command
- </notes>
- <deps>
- <dep type="php" rel="ge" version="4.1"/>
- <dep type="pkg" rel="ge" version="0.4">Archive_Tar</dep>
- <dep type="pkg" rel="ge" version="0.11">Console_Getopt</dep>
- </deps>
- </release>
- <release>
- <version>1.0b3</version>
- <state>stable</state>
- <date>2002-12-13</date>
- <notes>
- * fixed "info" shortcut (conflicted with "install")
- * added "php_bin" config parameter
- * all "non-personal" config parameters now use
- environment variables for defaults (very useful
- to override the default php_dir on Windows!)
- </notes>
- <deps>
- <dep type="php" rel="ge" version="4.1"/>
- <dep type="pkg" rel="ge" version="0.4">Archive_Tar</dep>
- <dep type="pkg" rel="ge" version="0.11">Console_Getopt</dep>
- </deps>
- </release>
- <release>
- <version>1.0b2</version>
- <state>stable</state>
- <date>2002-11-26</date>
- <notes>
-Changes, Installer:
-* --force option no longer ignores errors, use
- --ignore-errors instead
-* installer transactions: failed installs abort
- cleanly, without leaving half-installed packages
- around
-</notes>
- </release>
- <release>
- <version>1.0b1</version>
- <state>stable</state>
- <date>2002-10-12</date>
- <notes>
-New Features, Installer:
-* new command: "pear makerpm"
-* new command: "pear search"
-* new command: "pear upgrade-all"
-* new command: "pear config-help"
-* new command: "pear sign"
-* Windows support for "pear build" (requires
- msdev)
-* new dependency type: "zend"
-* XML-RPC results may now be cached (see
- cache_dir and cache_ttl config)
-* HTTP proxy authorization support
-* install/upgrade install-root support
-
-Bugfixes, Installer:
-* fix for XML-RPC bug that made some remote
- commands fail
-* fix problems under Windows with
- DIRECTORY_SEPARATOR
-* lots of other minor fixes
-* --force option did not work for "pear install
- Package"
-* http downloader used "4.2.1" rather than
- "PHP/4.2.1" as user agent
-* bending over a little more to figure out how
- PHP is installed
-* "platform" file attribute was not included
- during "pear package"
-
-New Features, PEAR Library:
-* added PEAR::loadExtension($ext)
-* added PEAR::delExpect()
-* System::mkTemp() now cleans up at shutdown
-* defined PEAR_ZE2 constant (boolean)
-* added PEAR::throwError() with a simpler API
- than raiseError()
-
-Bugfixes, PEAR Library:
-* ZE2 compatibility fixes
-* use getenv() as fallback for $_ENV
-</notes>
- <deps>
- <dep type="php" rel="ge" version="4.1"/>
- <dep type="pkg" rel="ge" version="0.4">Archive_Tar</dep>
- <dep type="pkg" rel="ge" version="0.11">Console_Getopt</dep>
- </deps>
- </release>
- <release>
- <version>0.90</version>
- <state>beta</state>
- <date>2002-05-28</date>
- <notes>
-* fix: "help" command was broken
-* new command: "info"
-* new command: "config-help"
-* un-indent multi-line data from xml description files
-* new command: "build"
-* fix: config-set did not work with "set" parameters
-* disable magic_quotes_runtime
-* "install" now builds and installs C extensions
-* added PEAR::delExpect()
-* System class no longer inherits PEAR
-* grouped PEAR_Config parameters
-* add --nobuild option to install/upgrade commands
-* new and more generic Frontend API
-</notes>
- <deps>
- <dep type="php" rel="ge" version="4.1"/>
- <dep type="pkg" rel="has" version="0.4">Archive_Tar</dep>
- <dep type="pkg" rel="ge" version="0.11">Console_Getopt</dep>
- </deps>
- </release>
- <release>
- <version>0.10</version>
- <state>beta</state>
- <date>2002-05-26</date>
- <notes>
-Lots of stuff this time. 0.9 was not actually self-hosting, even
-though it claimed to be. This version finally is self-hosting
-(really!), meaning you can upgrade the installer with the command
-"pear upgrade PEAR".
-
-* new config paramers: http_proxy and umask
-* HTTP proxy support when downloading packages
-* generalized command handling code
-* and fixed the bug that would not let commands have the
- same options as "pear" itself
-* added long options to every command
-* added command shortcuts ("pear help shortcuts")
-* added stub for Gtk installer
-* some phpdoc fixes
-* added class dependency detector (using ext/tokenizer)
-* dependency handling fixes
-* added OS_Guess class for detecting OS
-* install files with the "platform" attribute set
- only on matching operating systems
-* PEAR_Remote now falls back to the XML_RPC package
- if xmlrpc-epi is not available
-* renamed command: package-list -> list
-* new command: package-dependencies
-* lots of minor fixes
-</notes>
- <deps>
- <dep type="php" rel="ge" version="4.1"/>
- <dep type="pkg" rel="has" version="0.5">Archive_Tar</dep>
- <dep type="pkg" rel="ge" version="0.11">Console_Getopt</dep>
- </deps>
- </release>
- <release>
- <version>0.9</version>
- <state>beta</state>
- <date>2002-04-07</date>
- <notes>
-First package release. Commands implemented:
- remote-package-info
- list-upgrades
- list-remote-packages
- download
- config-show
- config-get
- config-set
- list-installed
- shell-test
- install
- uninstall
- upgrade
- package
- package-list
- package-info
- login
- logout
-</notes>
- </release>
- </changelog>
</package>
diff --git a/pear/package.dtd b/pear/package.dtd
index f67bdab606..05238b8fd2 100644
--- a/pear/package.dtd
+++ b/pear/package.dtd
@@ -1,5 +1,5 @@
<!--
- $Id: package.dtd,v 1.27.4.7 2003-07-08 20:39:45 pajoye Exp $
+ $Id: package.dtd,v 1.27.4.8 2003-09-29 14:06:38 pajoye Exp $
This is the PEAR package description, version 1.0.
It should be used with the informal public identifier:
diff --git a/pear/scripts/pear.bat b/pear/scripts/pear.bat
index 0085b53384..d5b795c7aa 100755
--- a/pear/scripts/pear.bat
+++ b/pear/scripts/pear.bat
@@ -16,22 +16,22 @@ REM ----------------------------------------------------------------------
REM Authors: Alexander Merz (alexmerz@php.net)
REM ----------------------------------------------------------------------
REM
-REM $Id: pear.bat,v 1.15 2003/06/10 20:03:44 imajes Exp $
+REM $Id: pear.bat,v 1.17 2003/08/29 21:21:27 cellog Exp $
REM change this lines to match the paths of your system
REM -------------------
@ECHO OFF
:: Check PEAR global ENV, set them if they do not exist
-IF "%PHP_PEAR_INSTALL_DIR%"=="" SET PHP_PEAR_INSTALL_DIR=@include_path@
-IF "%PHP_PEAR_BIN_DIR%"=="" SET PHP_PEAR_BIN_DIR=@bin_dir@
-IF "%PHP_PEAR_PHP_BIN%"=="" SET PHP_PEAR_PHP_BIN=@php_bin@
+IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
+IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
+IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
:: Check Folders and files
-IF NOT EXIST %PHP_PEAR_INSTALL_DIR% GOTO PEAR_INSTALL_ERROR
-IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php GOTO PEAR_INSTALL_ERROR2
-IF NOT EXIST %PHP_PEAR_BIN_DIR% GOTO PEAR_BIN_ERROR
-IF NOT EXIST %PHP_PEAR_PHP_BIN% GOTO PEAR_PHPBIN_ERROR
+IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
+IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
+IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
+IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
:: launch pearcmd
GOTO RUN
:PEAR_INSTALL_ERROR
@@ -64,6 +64,6 @@ ECHO The current value is:
ECHO %PHP_PEAR_PHP_BIN%
GOTO END
:RUN
-%PHP_PEAR_PHP_BIN% -C -d output_buffering=1 -d include_path=%PHP_PEAR_INSTALL_DIR% -f %PHP_PEAR_INSTALL_DIR%\pearcmd.php -- %1 %2 %3 %4 %5 %6 %7 %8 %9
+"%PHP_PEAR_PHP_BIN%" -C -d output_buffering=1 -d include_path="%PHP_PEAR_INSTALL_DIR%" -f "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
:END
@ECHO ON