diff options
author | Pierre Joye <pajoye@php.net> | 2003-09-29 14:06:44 +0000 |
---|---|---|
committer | Pierre Joye <pajoye@php.net> | 2003-09-29 14:06:44 +0000 |
commit | c2a66c59c620e3933248038e61f15248199ee836 (patch) | |
tree | 1e69e834f36648cad0d447e785899290444ca68a | |
parent | cb9b99262e4ab7a67924c71fec17d5a1125fce67 (diff) | |
download | php-git-c2a66c59c620e3933248038e61f15248199ee836.tar.gz |
MFH 4.3.4RC1
-rw-r--r-- | pear/OS/Guess.php | 2 | ||||
-rw-r--r-- | pear/PEAR.php | 17 | ||||
-rw-r--r-- | pear/PEAR/Command/Install.php | 59 | ||||
-rw-r--r-- | pear/PEAR/Common.php | 219 | ||||
-rw-r--r-- | pear/PEAR/Dependency.php | 47 | ||||
-rw-r--r-- | pear/PEAR/Frontend/CLI.php | 5 | ||||
-rw-r--r-- | pear/PEAR/Installer.php | 527 | ||||
-rw-r--r-- | pear/PEAR/Registry.php | 2 | ||||
-rw-r--r-- | pear/System.php | 86 | ||||
-rw-r--r-- | pear/package-PEAR.xml | 318 | ||||
-rw-r--r-- | pear/package.dtd | 2 | ||||
-rwxr-xr-x | pear/scripts/pear.bat | 18 |
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("/usr/bin/cpp $tmpfile", "r"); + +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
|