--- /dev/null
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Sæther Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Installer.php";
+
+/**
+ * PEAR commands for installation or deinstallation/upgrading of
+ * packages.
+ *
+ */
+class PEAR_Command_Install extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'install' => array(
+ 'summary' => 'Install Package',
+ 'function' => 'doInstall',
+ 'shortcut' => 'i',
+ 'options' => array(
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'will overwrite newer installed packages',
+ ),
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, install anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as installed',
+ ),
+ 'soft' => array(
+ 'shortopt' => 's',
+ 'doc' => 'soft install, fail silently, or upgrade if already installed',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ '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
+install in four ways:
+
+"Package-1.0.tgz" : installs from a local file
+
+"http://example.com/Package-1.0.tgz" : installs from
+anywhere on the net.
+
+"package.xml" : installs the package described in
+package.xml. Useful for testing, or for wrapping a PEAR package in
+another package manager such as RPM.
+
+"Package" : queries your configured server
+({config master_server}) and downloads the newest package with
+the preferred quality/state ({config preferred_state}).
+
+More than one package may be specified at once. It is ok to mix these
+four ways of specifying packages.
+'),
+ 'upgrade' => array(
+ 'summary' => 'Upgrade Package',
+ 'function' => 'doInstall',
+ 'shortcut' => 'up',
+ 'options' => array(
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'overwrite newer installed packages',
+ ),
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, upgrade anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as upgraded',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ '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
+"install" command for ways to specify a package.
+
+When upgrading, your package will be updated if the provided new
+package has a higher version number (use the -f option if you need to
+upgrade anyway).
+
+More than one package may be specified at once.
+'),
+ 'upgrade-all' => array(
+ 'summary' => 'Upgrade All Packages',
+ 'function' => 'doInstall',
+ 'shortcut' => 'ua',
+ 'options' => array(
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, upgrade anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as upgraded',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ ),
+ 'doc' => '
+Upgrades all packages that have a newer release available. Upgrades are
+done only if there is a release available of the state specified in
+"preferred_state" (currently {config preferred_state}), or a state considered
+more stable.
+'),
+ 'uninstall' => array(
+ 'summary' => 'Un-install Package',
+ 'function' => 'doUninstall',
+ 'shortcut' => 'un',
+ 'options' => array(
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, uninstall anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not remove files, only register the packages as not installed',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ ),
+ 'doc' => '<package> ...
+Uninstalls one or more PEAR packages. More than one package may be
+specified at once.
+'),
+ 'bundle' => array(
+ 'summary' => 'Unpacks a Pecl Package',
+ 'function' => 'doBundle',
+ 'shortcut' => 'bun',
+ 'options' => array(
+ 'destination' => array(
+ 'shortopt' => 'd',
+ 'arg' => 'DIR',
+ 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
+ ),
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'Force the unpacking even if there were errors in the package',
+ ),
+ ),
+ 'doc' => '<package>
+Unpacks a Pecl Package into the selected location. It will download the
+package if needed.
+'),
+ 'revert' => array(
+ 'summary' => 'Revert the Most Recent Upgrade of a Package',
+ 'function' => 'doRevert',
+ 'shortcut' => 'r',
+ 'options' => array(
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'Force the revert even if there were errors',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'deps' => array(
+ 'shortopt' => 'd',
+ 'doc' => 'revert any dependencies that are incompatible with earlier versions',
+ ),
+ ),
+ 'doc' => '<package> ...
+This command may only be used after a pear upgrade <package> has
+been executed. pear revert will revert a package to the previously
+installed version number. This command will only work once, and will
+fail if any packages depend on the newer version.'),
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Install constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Install(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doInstall()
+
+ function doInstall($command, $options, $params)
+ {
+ require_once 'PEAR/Downloader.php';
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Installer($this->ui);
+ }
+ if ($command == 'upgrade') {
+ $options['upgrade'] = true;
+ }
+ if ($command == 'upgrade-all') {
+ include_once "PEAR/Remote.php";
+ $options['upgrade'] = true;
+ $remote = &new PEAR_Remote($this->config);
+ $state = $this->config->get('preferred_state');
+ if (empty($state) || $state == 'any') {
+ $latest = $remote->call("package.listLatestReleases");
+ } else {
+ $latest = $remote->call("package.listLatestReleases", $state);
+ }
+ if (PEAR::isError($latest)) {
+ return $latest;
+ }
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $installed = array_flip($reg->listPackages());
+ $params = array();
+ foreach ($latest as $package => $info) {
+ $package = strtolower($package);
+ if (!isset($installed[$package])) {
+ // skip packages we don't have installed
+ continue;
+ }
+ $inst_version = $reg->packageInfo($package, 'version');
+ if (version_compare("$info[version]", "$inst_version", "le")) {
+ // installed version is up-to-date
+ continue;
+ }
+ $params[] = $package;
+ $this->ui->outputData(array('data' => "Will upgrade $package"), $command);
+ }
+ }
+ $this->downloader = &new PEAR_Downloader($this->ui, $options, $this->config);
+ $errors = array();
+ $downloaded = array();
+ $this->downloader->download($params);
+ if ($command != 'upgrade-all') {
+ for ($i = 0; $i < count($params); $i++) {
+ $params[$i] = $this->downloader->extractDownloadFileName($params[$i], $_tmp);
+ }
+ }
+ $errors = $this->downloader->getErrorMsgs();
+ if (count($errors)) {
+ $err['data'] = array($errors);
+ $err['headline'] = 'Install Errors';
+ $this->ui->outputData($err);
+ return $this->raiseError("$command failed");
+ }
+ $downloaded = $this->downloader->getDownloadedPackages();
+ $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]";
+ $out = array('data' => "$command ok: $label");
+ if (isset($info['release_warnings'])) {
+ $out['release_warnings'] = $info['release_warnings'];
+ }
+ $this->ui->outputData($out, $command);
+ }
+ } else {
+ return $this->raiseError("$command failed");
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doUninstall()
+
+ function doUninstall($command, $options, $params)
+ {
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Installer($this->ui);
+ }
+ 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) {
+ $this->ui->outputData("uninstall ok: $pkg", $command);
+ }
+ } else {
+ return $this->raiseError("uninstall failed: $pkg");
+ }
+ }
+ return true;
+ }
+
+ // }}}
+
+
+ // }}}
+ // {{{ doBundle()
+ /*
+ (cox) It just downloads and untars the package, does not do
+ any check that the PEAR_Installer::_installFile() does.
+ */
+
+ function doBundle($command, $options, $params)
+ {
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Downloader($this->ui);
+ }
+ $installer = &$this->installer;
+ if (sizeof($params) < 1) {
+ return $this->raiseError("Please supply the package you want to bundle");
+ }
+ $pkgfile = $params[0];
+ $need_download = false;
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ } elseif (!@is_file($pkgfile)) {
+ if ($installer->validPackageName($pkgfile)) {
+ $pkgfile = $installer->getPackageDownloadUrl($pkgfile);
+ $need_download = true;
+ } else {
+ if (strlen($pkgfile)) {
+ return $this->raiseError("Could not open the package file: $pkgfile");
+ } else {
+ return $this->raiseError("No package file given");
+ }
+ }
+ }
+
+ // Download package -----------------------------------------------
+ if ($need_download) {
+ $downloaddir = $installer->config->get('download_dir');
+ if (empty($downloaddir)) {
+ if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+ return $downloaddir;
+ }
+ $installer->log(2, '+ tmp dir created at ' . $downloaddir);
+ }
+ $callback = $this->ui ? array(&$installer, '_downloadCallback') : null;
+ $file = $installer->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+ if (PEAR::isError($file)) {
+ return $this->raiseError($file);
+ }
+ $pkgfile = $file;
+ }
+
+ // Parse xml file -----------------------------------------------
+ $pkginfo = $installer->infoFromTgzFile($pkgfile);
+ if (PEAR::isError($pkginfo)) {
+ return $this->raiseError($pkginfo);
+ }
+ $installer->validatePackageInfo($pkginfo, $errors, $warnings);
+ // XXX We allow warnings, do we have to do it?
+ if (count($errors)) {
+ if (empty($options['force'])) {
+ return $this->raiseError("The following errors where found:\n".
+ implode("\n", $errors));
+ } else {
+ $this->log(0, "warning : the following errors were found:\n".
+ implode("\n", $errors));
+ }
+ }
+ $pkgname = $pkginfo['package'];
+
+ // Unpacking -------------------------------------------------
+
+ if (isset($options['destination'])) {
+ if (!is_dir($options['destination'])) {
+ System::mkdir('-p ' . $options['destination']);
+ }
+ $dest = realpath($options['destination']);
+ } else {
+ $pwd = getcwd();
+ if (is_dir($pwd . DIRECTORY_SEPARATOR . 'ext')) {
+ $dest = $pwd . DIRECTORY_SEPARATOR . 'ext';
+ } else {
+ $dest = $pwd;
+ }
+ }
+ $dest .= DIRECTORY_SEPARATOR . $pkgname;
+ $orig = $pkgname . '-' . $pkginfo['version'];
+
+ $tar = new Archive_Tar($pkgfile);
+ if (!@$tar->extractModify($dest, $orig)) {
+ return $this->raiseError("unable to unpack $pkgfile");
+ }
+ $this->ui->outputData("Package ready at '$dest'");
+ // }}}
+ }
+
+ // }}}
+ // {{{ doRevert()
+ function doRevert($command, $options, $params)
+ {
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Installer($this->ui);
+ }
+ if (sizeof($params) < 1) {
+ return $this->raiseError("Please supply the package(s) you want to revert");
+ }
+ 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[]['info']['package'] = $pkg;
+ } else {
+ $newparams = array_merge($newparams,
+ $this->installer->processRevertDeps($info, $options, $config, $reg));
+ $newparams[] = $info;
+ }
+ }
+ PEAR_Common::sortPkgDeps($newparams, true);
+ $params = array();
+ $params = array_merge($newparams, $badparams);
+ foreach ($params as $pkg) {
+ if (!PEAR::isError($this->installer->revert($pkg['info']['package'],
+ $options, $this->config))) {
+ if ($this->config->get('verbose') > 0) {
+ $this->ui->outputData('revert ok: ' . $pkg['info']['package'], $command);
+ }
+ } else {
+ $this->ui->outputData('revert failed: ');
+ return $this->raiseError($pkg);
+ }
+ }
+ return true;
+ }
+
+ // }}}
+
+}
+?>
--- /dev/null
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// | Martin Jansen <mj@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Dependency.php';
+require_once 'PEAR/Remote.php';
+require_once 'System.php';
+
+/**
+ * Administration class used to download PEAR packages and maintain the
+ * installed package database.
+ *
+ * @since PEAR 1.4
+ * @author Greg Beaver <cellog@php.net>
+ */
+class PEAR_Downloader extends PEAR_Common
+{
+ /**
+ * @var PEAR_Config
+ * @access private
+ */
+ var $_config;
+
+ /**
+ * @var PEAR_Registry
+ * @access private
+ */
+ var $_registry;
+
+ /**
+ * @var PEAR_Remote
+ * @access private
+ */
+ var $_remote;
+
+ /**
+ * Preferred Installation State (snapshot, devel, alpha, beta, stable)
+ * @var string|null
+ * @access private
+ */
+ var $_preferredState;
+
+ /**
+ * Options from command-line passed to Install.
+ *
+ * Recognized options:<br />
+ * - onlyreqdeps : install all required dependencies as well
+ * - alldeps : install all dependencies, including optional
+ * - installroot : base relative path to install files in
+ * - force : force a download even if warnings would prevent it
+ * @see PEAR_Command_Install
+ * @access private
+ * @var array
+ */
+ var $_options;
+
+ /**
+ * Downloaded Packages after a call to download().
+ *
+ * Format of each entry:
+ *
+ * <code>
+ * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+ * 'info' => array() // parsed package.xml
+ * );
+ * </code>
+ * @access private
+ * @var array
+ */
+ var $_downloadedPackages = array();
+
+ /**
+ * Packages slated for download.
+ *
+ * This is used to prevent downloading a package more than once should it be a dependency
+ * for two packages to be installed.
+ * Format of each entry:
+ *
+ * <pre>
+ * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
+ * );
+ * </pre>
+ * @access private
+ * @var array
+ */
+ var $_toDownload = array();
+
+ /**
+ * Array of every package installed, with names lower-cased.
+ *
+ * Format:
+ * <code>
+ * array('package1' => 0, 'package2' => 1, );
+ * </code>
+ * @var array
+ */
+ var $_installed = array();
+
+ /**
+ * @var array
+ * @access private
+ */
+ var $_errorStack = array();
+
+ // {{{ PEAR_Downloader()
+
+ function PEAR_Downloader(&$ui, $options, &$config)
+ {
+ $this->_options = $options;
+ $this->_config = &$config;
+ $this->_preferredState = $this->_config->get('preferred_state');
+ if (!$this->_preferredState) {
+ // don't inadvertantly use a non-set preferred_state
+ $this->_preferredState = null;
+ }
+
+ $php_dir = $this->_config->get('php_dir');
+ if (isset($this->_options['installroot'])) {
+ if (substr($this->_options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $this->_options['installroot'] = substr($this->_options['installroot'], 0, -1);
+ }
+ $php_dir = $this->_prependPath($php_dir, $this->_options['installroot']);
+ }
+ $this->_registry = &new PEAR_Registry($php_dir);
+ $this->_remote = &new PEAR_Remote($config);
+
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ $this->_installed = $this->_registry->listPackages();
+ array_walk($this->_installed, create_function('&$v,$k','$v = strtolower($v);'));
+ $this->_installed = array_flip($this->_installed);
+ }
+ parent::PEAR_Common($ui);
+ }
+
+ // }}}
+ // {{{ _downloadFile()
+ /**
+ * @param string filename to download
+ * @param string version/state
+ * @param string original value passed to command-line
+ * @param string|null preferred state (snapshot/devel/alpha/beta/stable)
+ * Defaults to configuration preferred state
+ * @return null|PEAR_Error|string
+ * @access private
+ */
+ function _downloadFile($pkgfile, $version, $origpkgfile, $state = null)
+ {
+ if (is_null($state)) {
+ $state = $this->_preferredState;
+ }
+ // {{{ check the package filename, and whether it's already installed
+ $need_download = false;
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ } elseif (!@is_file($pkgfile)) {
+ if ($this->validPackageName($pkgfile)) {
+ if ($this->_registry->packageExists($pkgfile)) {
+ if (empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+ $errors[] = "$pkgfile already installed";
+ return;
+ }
+ }
+ $pkgfile = $this->getPackageDownloadUrl($pkgfile, $version);
+ $need_download = true;
+ } else {
+ if (strlen($pkgfile)) {
+ $errors[] = "Could not open the package file: $pkgfile";
+ } else {
+ $errors[] = "No package file given";
+ }
+ return;
+ }
+ }
+ // }}}
+
+ // {{{ Download package -----------------------------------------------
+ if ($need_download) {
+ $downloaddir = $this->_config->get('download_dir');
+ if (empty($downloaddir)) {
+ if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+ return $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)) {
+ if (!PEAR::isError($info = $this->_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)',
+ PEAR_INSTALLER_ERROR_NO_PREF_STATE);
+ }
+ } else {
+ return $pkgfile;
+ }
+ } else {
+ return $this->raiseError($file);
+ }
+ }
+ $pkgfile = $file;
+ }
+ // }}}
+ return $pkgfile;
+ }
+ // }}}
+ // {{{ getPackageDownloadUrl()
+
+ function getPackageDownloadUrl($package, $version = null)
+ {
+ if ($version) {
+ $package .= "-$version";
+ }
+ if ($this === null || $this->_config === null) {
+ $package = "http://pear.php.net/get/$package";
+ } else {
+ $package = "http://" . $this->_config->get('master_server') .
+ "/get/$package";
+ }
+ if (!extension_loaded("zlib")) {
+ $package .= '?uncompress=yes';
+ }
+ return $package;
+ }
+
+ // }}}
+ // {{{ extractDownloadFileName($pkgfile, &$version)
+
+ 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;
+ }
+
+ // }}}
+
+ // }}}
+ // {{{ getDownloadedPackages()
+
+ /**
+ * Retrieve a list of downloaded packages after a call to {@link download()}.
+ *
+ * Also resets the list of downloaded packages.
+ * @return array
+ */
+ function getDownloadedPackages()
+ {
+ $ret = $this->_downloadedPackages;
+ $this->_downloadedPackages = array();
+ $this->_toDownload = array();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ download()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * @param array a mixed list of package names, local files, or package.xml
+ */
+ function download($packages)
+ {
+ $mywillinstall = array();
+ $state = $this->_preferredState;
+
+ // {{{ download files in this list if necessary
+ foreach($packages as $pkgfile) {
+ if (!is_file($pkgfile)) {
+ $pkgfile = $this->_downloadNonFile($pkgfile);
+ if (PEAR::isError($pkgfile)) {
+ return $pkgfile;
+ }
+ if ($pkgfile === false) {
+ continue;
+ }
+ } // end is_file()
+ $tempinfo = $this->infoFromAny($pkgfile);
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ // ignore dependencies if there are any errors
+ if (!PEAR::isError($tempinfo)) {
+ $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
+ }
+ }
+ $this->_downloadedPackages[] = array('pkg' => $tempinfo['package'],
+ 'file' => $pkgfile, 'info' => $tempinfo);
+ } // end foreach($packages)
+ // }}}
+
+ // {{{ extract dependencies from downloaded files and then download
+ // them if necessary
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ $deppackages = array();
+ foreach ($mywillinstall as $package => $alldeps) {
+ if (!is_array($alldeps)) {
+ // there are no dependencies
+ continue;
+ }
+ foreach($alldeps as $info) {
+ if ($info['type'] != 'pkg') {
+ continue;
+ }
+ $ret = $this->_processDependency($info, $mywillinstall);
+ if ($ret === false) {
+ continue;
+ }
+ if (PEAR::isError($ret)) {
+ return $ret;
+ }
+ $deppackages[] = $ret;
+ } // foreach($alldeps
+ }
+
+ if (count($deppackages)) {
+ // check dependencies' dependencies
+ // combine the list of packages to install
+ $temppack = array();
+ foreach($this->_downloadedPackages as $p) {
+ $temppack[] = strtolower($p['info']['package']);
+ }
+ foreach($deppackages as $pack) {
+ $temppack[] = strtolower($pack);
+ }
+ $this->_toDownload = array_merge($this->_toDownload, $temppack);
+ $this->download($deppackages);
+ }
+ } // }}} if --alldeps or --onlyreqdeps
+ }
+
+ // }}}
+ // {{{ _downloadNonFile($pkgfile)
+
+ /**
+ * @return false|PEAR_Error|string false if loop should be broken out of,
+ * string if the file was downloaded,
+ * PEAR_Error on exception
+ * @access private
+ */
+ function _downloadNonFile($pkgfile)
+ {
+ $origpkgfile = $pkgfile;
+ $state = null;
+ $pkgfile = $this->extractDownloadFileName($pkgfile, $version);
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $pkgfile = $this->_downloadFile($pkgfile, $version, $origpkgfile);
+ if (PEAR::isError($pkgfile)) {
+ return $pkgfile;
+ }
+ $tempinfo = $this->infoFromAny($pkgfile);
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ // ignore dependencies if there are any errors
+ if (!PEAR::isError($tempinfo)) {
+ $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
+ }
+ }
+ $this->_downloadedPackages[] = array('pkg' => $tempinfo['package'],
+ 'file' => $pkgfile, 'info' => $tempinfo);
+ return false;
+ }
+ if (!$this->validPackageName($pkgfile)) {
+ return $this->raiseError("Package name '$pkgfile' not valid");
+ }
+ // ignore packages that are installed unless we are upgrading
+ $curinfo = $this->_registry->packageInfo($pkgfile);
+ if ($this->_registry->packageExists($pkgfile)
+ && empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+ $this->log(0, "Package '{$curinfo['package']}' already installed, skipping");
+ return false;
+ }
+ $curver = $curinfo['version'];
+ $releases = $this->_remote->call('package.info', $pkgfile, 'releases');
+ if (!count($releases)) {
+ return $this->raiseError("No releases found for package '$pkgfile'");
+ }
+ // Want a specific version/state
+ if ($version !== null) {
+ // Passed Foo-1.2
+ if ($this->validPackageVersion($version)) {
+ if (!isset($releases[$version])) {
+ return $this->raiseError("No release with version '$version' found for '$pkgfile'");
+ }
+ // Passed Foo-alpha
+ } elseif (in_array($version, $this->getReleaseStates())) {
+ $state = $version;
+ $version = 0;
+ foreach ($releases as $ver => $inf) {
+ if ($inf['state'] == $state && version_compare("$version", "$ver") < 0) {
+ $version = $ver;
+ }
+ }
+ if ($version == 0) {
+ return $this->raiseError("No release with state '$state' found for '$pkgfile'");
+ }
+ // invalid postfix passed
+ } else {
+ return $this->raiseError("Invalid postfix '-$version', be sure to pass a valid PEAR ".
+ "version number or release state");
+ }
+ // Guess what to download
+ } else {
+ $states = $this->betterStates($this->_preferredState, true);
+ $possible = false;
+ $version = 0;
+ foreach ($releases as $ver => $inf) {
+ if (in_array($inf['state'], $states) && version_compare("$version", "$ver") < 0) {
+ $version = $ver;
+ }
+ }
+ if ($version == 0 && !isset($this->_options['force'])) {
+ return $this->raiseError('No release with state equal to: \'' . implode(', ', $states) .
+ "' found for '$pkgfile'");
+ } elseif ($version == 0) {
+ $this->log(0, "Warning: $pkgfile is state '$inf[state]' which is less stable " .
+ "than state '$state'");
+ }
+ }
+ // Check if we haven't already the version
+ if (empty($this->_options['force'])) {
+ if ($curinfo['version'] == $version) {
+ $this->log(0, "Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping");
+ return false;
+ } elseif (version_compare("$version", "{$curinfo['version']}") < 0) {
+ $this->log(0, "Already got '{$curinfo['package']}-{$curinfo['version']}' greater than requested '$version', skipping");
+ return false;
+ }
+ }
+ return $this->_downloadFile($pkgfile, $version, $origpkgfile, $state);
+ }
+
+ // }}}
+ // {{{ _processDependency($info)
+
+ /**
+ * Process a dependency, download if necessary
+ * @param array dependency information from PEAR_Remote call
+ * @param array packages that will be installed in this iteration
+ * @return false|string|PEAR_Error
+ * @access private
+ */
+ function _processDependency($info, $mywillinstall)
+ {
+ $state = $this->_preferredState;
+ if (!isset($this->_options['alldeps']) && isset($info['optional']) &&
+ $info['optional'] == 'yes') {
+ // skip optional deps
+ $this->log(0, "skipping Package $package optional dependency $info[name]");
+ return false;
+ }
+ // {{{ get releases
+ $releases = $this->_remote->call('package.info', $info['name'], 'releases');
+ if (PEAR::isError($releases)) {
+ return $releases;
+ }
+ if (!count($releases)) {
+ if (!isset($this->_installed[strtolower($info['name'])])) {
+ $this->pushError("Package $package dependency $info[name] ".
+ "has no releases");
+ }
+ return false;
+ }
+ $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);
+ $this->pushError( "Release for $package dependency $info[name] " .
+ "has state '$savestate', requires $state");
+ continue;
+ }
+ if (in_array(strtolower($info['name']), $this->_toDownload) ||
+ isset($mywillinstall[strtolower($info['name'])])) {
+ // skip upgrade check for packages we will install
+ continue;
+ }
+ if (!isset($this->_installed[strtolower($info['name'])])) {
+ // check to see if we can install the specific version required
+ if ($info['rel'] == 'eq') {
+ return $info['name'] . '-' . $info['version'];
+ }
+ // skip upgrade check for packages we don't have installed
+ return $info['name'];
+ }
+ // }}}
+
+ // {{{ 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')) {
+ return false;
+ } else {
+ return $info['name'];
+ }
+ }
+ if (version_compare($info['version'], $inst_version, 'le')) {
+ // installed version is up-to-date
+ return false;
+ }
+ return $info['name'];
+ }
+
+ // }}}
+ // {{{ pushError($errmsg, $code)
+
+ /**
+ * @param string
+ * @param integer
+ */
+ function pushError($errmsg, $code = -1)
+ {
+ array_push($this->_errorStack, array($errmsg, $code));
+ }
+
+ // }}}
+ // {{{ getErrorMsgs()
+
+ function getErrorMsgs()
+ {
+ $msgs = array();
+ $errs = $this->_errorStack;
+ foreach ($errs as $err) {
+ $msgs[] = $err[0];
+ }
+ $this->_errorStack = array();
+ return $msgs;
+ }
+
+ // }}}
+}
+// }}}
+
+?>
--- /dev/null
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// | Martin Jansen <mj@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Dependency.php';
+require_once 'System.php';
+
+define('PEAR_INSTALLER_OK', 1);
+define('PEAR_INSTALLER_FAILED', 0);
+define('PEAR_INSTALLER_SKIPPED', -1);
+define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
+
+/**
+ * Administration class used to install PEAR packages and maintain the
+ * installed package database.
+ *
+ * TODO:
+ * - Check dependencies break on package uninstall (when no force given)
+ * - add a guessInstallDest() method with the code from _installFile() and
+ * use that method in Registry::_rebuildFileMap() & Command_Registry::doList(),
+ * others..
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <ssb@php.net>
+ * @author Martin Jansen <mj@php.net>
+ */
+class PEAR_Installer extends PEAR_Common
+{
+ // {{{ properties
+
+ /** name of the package directory, for example Foo-1.0
+ * @var string
+ */
+ var $pkgdir;
+
+ /** directory where PHP code files go
+ * @var string
+ */
+ var $phpdir;
+
+ /** directory where PHP extension files go
+ * @var string
+ */
+ var $extdir;
+
+ /** directory where documentation goes
+ * @var string
+ */
+ var $docdir;
+
+ /** installation root directory (ala PHP's INSTALL_ROOT or
+ * automake's DESTDIR
+ * @var string
+ */
+ var $installroot = '';
+
+ /** debug level
+ * @var int
+ */
+ var $debug = 1;
+
+ /** temporary directory
+ * @var string
+ */
+ var $tmpdir;
+
+ /** PEAR_Registry object used by the installer
+ * @var object
+ */
+ var $registry;
+
+ /** List of file transactions queued for an install/upgrade/uninstall.
+ *
+ * Format:
+ * array(
+ * 0 => array("rename => array("from-file", "to-file")),
+ * 1 => array("delete" => array("file-to-delete")),
+ * ...
+ * )
+ *
+ * @var array
+ */
+ var $file_operations = array();
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Installer constructor.
+ *
+ * @param object $ui user interface object (instance of PEAR_Frontend_*)
+ *
+ * @access public
+ */
+ function PEAR_Installer(&$ui)
+ {
+ parent::PEAR_Common();
+ $this->setFrontendObject($ui);
+ $this->debug = $this->config->get('verbose');
+ //$this->registry = &new PEAR_Registry($this->config->get('php_dir'));
+ }
+
+ // }}}
+
+ // {{{ _deletePackageFiles()
+
+ /**
+ * Delete a package's installed files, does not remove empty directories.
+ *
+ * @param string $package package name
+ *
+ * @return bool TRUE on success, or a PEAR error on failure
+ *
+ * @access private
+ */
+ function _deletePackageFiles($package)
+ {
+ if (!strlen($package)) {
+ return $this->raiseError("No package to uninstall given");
+ }
+ $filelist = $this->registry->packageInfo($package, 'filelist');
+ if ($filelist == null) {
+ return $this->raiseError("$package not installed");
+ }
+ foreach ($filelist as $file => $props) {
+ if (empty($props['installed_as'])) {
+ continue;
+ }
+ $path = $this->_prependPath($props['installed_as'], $this->installroot);
+ $this->addFileOperation('delete', array($path));
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _installFile()
+
+ /**
+ * @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)
+ {
+ // {{{ return if this file is meant for another platform
+ static $os;
+ if (isset($atts['platform'])) {
+ if (empty($os)) {
+ include_once "OS/Guess.php";
+ $os = new OS_Guess();
+ }
+ if (!$os->matchSignature($atts['platform'])) {
+ $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
+ return PEAR_INSTALLER_SKIPPED;
+ }
+ }
+ // }}}
+
+ // {{{ assemble the destination paths
+ switch ($atts['role']) {
+ case 'doc':
+ case 'data':
+ case 'test':
+ $dest_dir = $this->config->get($atts['role'] . '_dir') .
+ DIRECTORY_SEPARATOR . $this->pkginfo['package'];
+ unset($atts['baseinstalldir']);
+ break;
+ case 'ext':
+ case 'php':
+ $dest_dir = $this->config->get($atts['role'] . '_dir');
+ break;
+ case 'script':
+ $dest_dir = $this->config->get('bin_dir');
+ break;
+ case 'src':
+ case 'extsrc':
+ $this->source_files++;
+ return;
+ 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'];
+ }
+ if (dirname($file) != '.' && empty($atts['install-as'])) {
+ $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
+ }
+ if (empty($atts['install-as'])) {
+ $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
+ } else {
+ $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
+ }
+ $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
+
+ // Clean up the DIRECTORY_SEPARATOR mess
+ $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
+ list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
+ DIRECTORY_SEPARATOR,
+ array($dest_file, $orig_file));
+ $installed_as = $dest_file;
+ $final_dest_file = $this->_prependPath($dest_file, $this->installroot);
+ $dest_dir = dirname($final_dest_file);
+ $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
+ // }}}
+
+ if (!@is_dir($dest_dir)) {
+ if (!$this->mkDirHier($dest_dir)) {
+ return $this->raiseError("failed to mkdir $dest_dir",
+ PEAR_INSTALLER_FAILED);
+ }
+ $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);
+ }
+ $this->log(3, "+ cp $orig_file $dest_file");
+ if (isset($atts['md5sum'])) {
+ $md5sum = md5_file($dest_file);
+ }
+ } else {
+ // {{{ file with replacements
+ 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);
+ if (isset($atts['md5sum'])) {
+ $md5sum = md5($contents);
+ }
+ $subst_from = $subst_to = array();
+ foreach ($atts['replacements'] as $a) {
+ $to = '';
+ if ($a['type'] == 'php-const') {
+ if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) {
+ eval("\$to = $a[to];");
+ } else {
+ $this->log(0, "invalid php-const replacement: $a[to]");
+ continue;
+ }
+ } elseif ($a['type'] == 'pear-config') {
+ $to = $this->config->get($a['to']);
+ if (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 (!is_null($to)) {
+ $subst_from[] = $a['from'];
+ $subst_to[] = $to;
+ }
+ }
+ $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
+ if (sizeof($subst_from)) {
+ $contents = str_replace($subst_from, $subst_to, $contents);
+ }
+ $wp = @fopen($dest_file, "wb");
+ if (!is_resource($wp)) {
+ return $this->raiseError("failed to create $dest_file: $php_errormsg",
+ PEAR_INSTALLER_FAILED);
+ }
+ if (!fwrite($wp, $contents)) {
+ return $this->raiseError("failed writing to $dest_file: $php_errormsg",
+ PEAR_INSTALLER_FAILED);
+ }
+ fclose($wp);
+ // }}}
+ }
+ // {{{ check the md5
+ if (isset($md5sum)) {
+ 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");
+ }
+ }
+ }
+ // }}}
+ // {{{ set file permissions
+ if (!OS_WINDOWS) {
+ if ($atts['role'] == 'script') {
+ $mode = 0777 & ~(int)octdec($this->config->get('umask'));
+ $this->log(3, "+ chmod +x $dest_file");
+ } else {
+ $mode = 0666 & ~(int)octdec($this->config->get('umask'));
+ }
+ $this->addFileOperation("chmod", array($mode, $dest_file));
+ if (!@chmod($dest_file, $mode)) {
+ $this->log(0, "failed to change mode of $dest_file");
+ }
+ }
+ // }}}
+ $this->addFileOperation("rename", array($dest_file, $final_dest_file));
+ // Store the full path where the file was installed for easy unistall
+ $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;
+ }
+
+ // }}}
+ // {{{ 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]");
+ } else {
+ $this->log(3, "adding to transaction: $type " . implode(" ", $data));
+ }
+ $this->file_operations[] = array($type, $data);
+ }
+
+ // }}}
+ // {{{ startFileTransaction()
+
+ function startFileTransaction($rollback_in_case = false)
+ {
+ if (count($this->file_operations) && $rollback_in_case) {
+ $this->rollbackFileTransaction();
+ }
+ $this->file_operations = array();
+ }
+
+ // }}}
+ // {{{ commitFileTransaction()
+
+ function commitFileTransaction()
+ {
+ $n = count($this->file_operations);
+ $this->log(2, "about to commit $n file operations");
+ // {{{ first, check permissions and such manually
+ $errors = array();
+ foreach ($this->file_operations as $tr) {
+ 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]";
+ }
+ break;
+ case 'chmod':
+ // check that file is writable
+ if (!is_writable($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]";
+ }
+ break;
+ }
+
+ }
+ // }}}
+ $m = sizeof($errors);
+ if ($m > 0) {
+ foreach ($errors as $error) {
+ $this->log(1, $error);
+ }
+ return false;
+ }
+ // {{{ really commit the transaction
+ foreach ($this->file_operations as $tr) {
+ 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[1], $data[0]);
+ $octmode = decoct($data[0]);
+ $this->log(3, "+ chmod $octmode $data[1]");
+ break;
+ case 'delete':
+ @unlink($data[0]);
+ $this->log(3, "+ rm $data[0]");
+ break;
+ case 'rmdir':
+ @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 committed $n file operations");
+ $this->file_operations = array();
+ return true;
+ }
+
+ // }}}
+ // {{{ rollbackFileTransaction()
+
+ function rollbackFileTransaction()
+ {
+ $n = count($this->file_operations);
+ $this->log(2, "rolling back $n file operations");
+ foreach ($this->file_operations as $tr) {
+ list($type, $data) = $tr;
+ switch ($type) {
+ case 'rename':
+ @unlink($data[0]);
+ $this->log(3, "+ rm $data[0]");
+ break;
+ case 'mkdir':
+ @rmdir($data[0]);
+ $this->log(3, "+ rmdir $data[0]");
+ break;
+ case 'chmod':
+ break;
+ case 'delete':
+ break;
+ case 'installed_as':
+ if (isset($this->pkginfo['filelist'])) {
+ 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 (isset($this->pkginfo['filelist']['dirtree'])
+ && !count($this->pkginfo['filelist']['dirtree'])) {
+ unset($this->pkginfo['filelist']['dirtree']);
+ }
+ break;
+ }
+ }
+ $this->file_operations = array();
+ }
+
+ // }}}
+ // {{{ mkDirHier($dir)
+
+ function mkDirHier($dir)
+ {
+ $this->addFileOperation('mkdir', array($dir));
+ return parent::mkDirHier($dir);
+ }
+
+ // }}}
+ // {{{ _prependPath($path, $prepend)
+
+ function _prependPath($path, $prepend)
+ {
+ if (strlen($prepend) > 0) {
+ if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
+ $path = $prepend . substr($path, 2);
+ } else {
+ $path = $prepend . $path;
+ }
+ }
+ return $path;
+ }
+
+ // }}}
+ // {{{ 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|PEAR_Error package info if successful
+ */
+
+ 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;
+ } else {
+ // {{{ Decompress pack in tmp dir -------------------------------------
+
+ // To allow relative package file names
+ $pkgfile = realpath($pkgfile);
+
+ if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
+ return $tmpdir;
+ }
+ $this->log(3, '+ tmp dir created at ' . $tmpdir);
+
+ $tar = new Archive_Tar($pkgfile);
+ if (!@$tar->extract($tmpdir)) {
+ return $this->raiseError("unable to unpack $pkgfile");
+ }
+
+ // {{{ Look for existing package file
+ $descfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
+
+ if (!is_file($descfile)) {
+ // ----- Look for old package archive format
+ // In this format the package.xml file was inside the
+ // Package-n.n directory
+ $dp = opendir($tmpdir);
+ do {
+ $pkgdir = readdir($dp);
+ } while ($pkgdir{0} == '.');
+
+ $descfile = $tmpdir . DIRECTORY_SEPARATOR . $pkgdir . DIRECTORY_SEPARATOR . 'package.xml';
+ $flag_old_format = true;
+ $this->log(0, "warning : you are using an archive with an old format");
+ }
+ // }}}
+ // <== XXX This part should be removed later on
+ // }}}
+ }
+
+ if (!is_file($descfile)) {
+ return $this->raiseError("no package.xml file after extracting the archive");
+ }
+
+ // Parse xml file -----------------------------------------------
+ $pkginfo = $this->infoFromDescriptionFile($descfile);
+ if (PEAR::isError($pkginfo)) {
+ return $pkginfo;
+ }
+ $this->validatePackageInfo($pkginfo, $errors, $warnings);
+ // XXX We allow warnings, do we have to do it?
+ if (count($errors)) {
+ if (empty($options['force'])) {
+ return $this->raiseError("The following errors where found (use force option to install anyway):\n".
+ implode("\n", $errors));
+ } else {
+ $this->log(0, "warning : the following errors were found:\n".
+ implode("\n", $errors));
+ }
+ }
+
+ $pkgname = $pkginfo['package'];
+
+ // {{{ Check dependencies -------------------------------------------
+ if (isset($pkginfo['release_deps']) && empty($options['nodeps'])) {
+ $dep_errors = '';
+ $error = $this->checkDeps($pkginfo, $dep_errors);
+ if ($error == true) {
+ if (empty($options['soft'])) {
+ $this->log(0, substr($dep_errors, 1));
+ }
+ return $this->raiseError("$pkgname: Dependencies failed");
+ } else if (!empty($dep_errors)) {
+ // Print optional dependencies
+ if (empty($options['soft'])) {
+ $this->log(0, $dep_errors);
+ }
+ }
+ }
+ // }}}
+
+ // {{{ checks to do when not in "force" mode
+ if (empty($options['force'])) {
+ $test = $this->registry->checkFileMap($pkginfo);
+ if (sizeof($test)) {
+ $tmp = $test;
+ foreach ($tmp as $file => $pkg) {
+ if ($pkg == $pkgname) {
+ unset($test[$file]);
+ }
+ }
+ if (sizeof($test)) {
+ $msg = "$pkgname: conflicting files found:\n";
+ $longest = max(array_map("strlen", array_keys($test)));
+ $fmt = "%${longest}s (%s)\n";
+ foreach ($test as $file => $pkg) {
+ $msg .= sprintf($fmt, $file, $pkg);
+ }
+ return $this->raiseError($msg);
+ }
+ }
+ }
+ // }}}
+
+ $this->startFileTransaction();
+
+ if (empty($options['upgrade'])) {
+ // checks to do only when installing new packages
+ if (empty($options['force']) && $this->registry->packageExists($pkgname)) {
+ return $this->raiseError("$pkgname already installed");
+ }
+ } else {
+ if ($this->registry->packageExists($pkgname)) {
+ $v1 = $this->registry->packageInfo($pkgname, 'version');
+ $v2 = $pkginfo['version'];
+ $cmp = version_compare("$v1", "$v2", 'gt');
+ if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
+ return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
+ }
+ if (empty($options['register-only'])) {
+ // when upgrading, remove old release's files first:
+ if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) {
+ return $this->raiseError($err);
+ }
+ }
+ }
+ }
+
+ // {{{ Copy files to dest dir ---------------------------------------
+
+ // info from the package it self we want to access from _installFile
+ $this->pkginfo = &$pkginfo;
+ // used to determine whether we should build any C code
+ $this->source_files = 0;
+
+ if (empty($options['register-only'])) {
+ if (!is_dir($php_dir)) {
+ return $this->raiseError("no script destination directory\n",
+ null, PEAR_ERROR_DIE);
+ }
+
+ $tmp_path = dirname($descfile);
+ if (substr($pkgfile, -4) != '.xml') {
+ $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkginfo['version'];
+ }
+
+ // ==> XXX This part should be removed later on
+ if ($flag_old_format) {
+ $tmp_path = dirname($descfile);
+ }
+ // <== XXX This part should be removed later on
+
+ // {{{ install files
+ foreach ($pkginfo['filelist'] as $file => $atts) {
+ $this->expectError(PEAR_INSTALLER_FAILED);
+ $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());
+ }
+ }
+ if ($res != PEAR_INSTALLER_OK) {
+ // Do not register files that were not installed
+ unset($pkginfo['filelist'][$file]);
+ }
+ }
+ // }}}
+
+ // {{{ compile and install source files
+ if ($this->source_files > 0 && empty($options['nobuild'])) {
+ $this->log(1, "$this->source_files source files, building");
+ $bob = &new PEAR_Builder($this->ui);
+ $bob->debug = $this->debug;
+ $built = $bob->build($descfile, array(&$this, '_buildCallback'));
+ if (PEAR::isError($built)) {
+ $this->rollbackFileTransaction();
+ return $built;
+ }
+ $this->log(1, "\nBuild process completed successfully");
+ foreach ($built as $ext) {
+ $bn = basename($ext['file']);
+ list($_ext_name, ) = explode('.', $bn);
+ if (extension_loaded($_ext_name)) {
+ $this->raiseError("Extension '$_ext_name' already loaded. Please unload it ".
+ "in your php.ini file prior to install or upgrade it.");
+ }
+ $dest = $this->config->get('ext_dir') . DIRECTORY_SEPARATOR . $bn;
+ $this->log(1, "Installing '$bn' at ext_dir ($dest)");
+ $this->log(3, "+ cp $ext[file] ext_dir ($dest)");
+ $copyto = $this->_prependPath($dest, $this->installroot);
+ if (!@copy($ext['file'], $copyto)) {
+ $this->rollbackFileTransaction();
+ return $this->raiseError("failed to copy $bn to $copyto");
+ }
+ $pkginfo['filelist'][$bn] = array(
+ 'role' => 'ext',
+ 'installed_as' => $dest,
+ 'php_api' => $ext['php_api'],
+ 'zend_mod_api' => $ext['zend_mod_api'],
+ 'zend_ext_api' => $ext['zend_ext_api'],
+ );
+ }
+ }
+ // }}}
+ }
+
+ if (!$this->commitFileTransaction()) {
+ $this->rollbackFileTransaction();
+ return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
+ }
+ // }}}
+
+ $ret = false;
+ // {{{ Register that the package is installed -----------------------
+ if (empty($options['upgrade'])) {
+ // if 'force' is used, replace the info in registry
+ if (!empty($options['force']) && $this->registry->packageExists($pkgname)) {
+ $this->registry->deletePackage($pkgname);
+ }
+ $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 $this->raiseError("Adding package $pkgname to registry failed");
+ }
+ // }}}
+ return $pkginfo;
+ }
+
+ // }}}
+ // {{{ 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');
+ if (isset($options['installroot'])) {
+ if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $options['installroot'] = substr($options['installroot'], 0, -1);
+ }
+ $this->installroot = $options['installroot'];
+ $php_dir = $this->_prependPath($php_dir, $this->installroot);
+ } else {
+ $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, $warning, $package);
+ if ($error) {
+ return $this->raiseError($errors . 'uninstall failed');
+ }
+ if ($warning) {
+ $this->log(0, $warning);
+ }
+ }
+ // {{{ Delete the files
+ $this->startFileTransaction();
+ if (PEAR::isError($err = $this->_deletePackageFiles($package))) {
+ $this->rollbackFileTransaction();
+ return $this->raiseError($err);
+ }
+ 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
+ return $this->registry->deletePackage($package);
+ }
+
+ // }}}
+ // {{{ _sortDirs()
+ function _sortDirs($a, $b)
+ {
+ if (strnatcmp($a, $b) == -1) return 1;
+ if (strnatcmp($a, $b) == 1) return -1;
+ return 0;
+ }
+
+ // }}}
+ // {{{ checkDeps()
+
+ /**
+ * Check if the package meets all dependencies
+ *
+ * @param array Package information (passed by reference)
+ * @param string Error message (passed by reference)
+ * @return boolean False when no error occured, otherwise true
+ */
+ function checkDeps(&$pkginfo, &$errors)
+ {
+ if (empty($this->registry)) {
+ $this->registry = &new PEAR_Registry($this->config->get('php_dir'));
+ }
+ $depchecker = &new PEAR_Dependency($this->registry);
+ $error = $errors = '';
+ $failed_deps = $optional_deps = array();
+ if (is_array($pkginfo['release_deps'])) {
+ foreach($pkginfo['release_deps'] as $dep) {
+ $code = $depchecker->callCheckMethod($error, $dep);
+ if ($code) {
+ if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+ $optional_deps[] = array($dep, $code, $error);
+ } else {
+ $failed_deps[] = array($dep, $code, $error);
+ }
+ }
+ }
+ // {{{ failed dependencies
+ $n = count($failed_deps);
+ if ($n > 0) {
+ for ($i = 0; $i < $n; $i++) {
+ if (isset($failed_deps[$i]['type'])) {
+ $type = $failed_deps[$i]['type'];
+ } else {
+ $type = 'pkg';
+ }
+ switch ($failed_deps[$i][1]) {
+ case PEAR_DEPENDENCY_MISSING:
+ if ($type == 'pkg') {
+ // install
+ }
+ $errors .= "\n" . $failed_deps[$i][2];
+ break;
+ case PEAR_DEPENDENCY_UPGRADE_MINOR:
+ if ($type == 'pkg') {
+ // upgrade
+ }
+ $errors .= "\n" . $failed_deps[$i][2];
+ break;
+ default:
+ $errors .= "\n" . $failed_deps[$i][2];
+ break;
+ }
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ optional dependencies
+ $count_optional = count($optional_deps);
+ if ($count_optional > 0) {
+ $errors = "Optional dependencies:";
+
+ for ($i = 0; $i < $count_optional; $i++) {
+ if (isset($optional_deps[$i]['type'])) {
+ $type = $optional_deps[$i]['type'];
+ } else {
+ $type = 'pkg';
+ }
+ switch ($optional_deps[$i][1]) {
+ case PEAR_DEPENDENCY_MISSING:
+ case PEAR_DEPENDENCY_UPGRADE_MINOR:
+ default:
+ $errors .= "\n" . $optional_deps[$i][2];
+ break;
+ }
+ }
+ return false;
+ }
+ // }}}
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ _buildCallback()
+
+ function _buildCallback($what, $data)
+ {
+ if (($what == 'cmdoutput' && $this->debug > 1) ||
+ ($what == 'output' && $this->debug > 0)) {
+ $this->ui->outputData(rtrim($data), 'build');
+ }
+ }
+
+ // }}}
+}
+
+// {{{ md5_file() utility function
+if (!function_exists("md5_file")) {
+ function md5_file($filename) {
+ $fp = fopen($filename, "r");
+ if (!$fp) return null;
+ $contents = fread($fp, filesize($filename));
+ fclose($fp);
+ return md5($contents);
+ }
+}
+// }}}
+
+?>
--- /dev/null
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE package SYSTEM "package.dtd">
+<package version="1.0">
+ <name>PEAR</name>
+ <summary>PEAR Base System</summary>
+ <description>The PEAR package contains:
+ * the PEAR base class
+ * the PEAR_Error error handling mechanism
+ * the PEAR installer, for creating, distributing
+ and installing packages
+ * the OS_Guess class for retrieving info about the OS
+ where PHP is running on
+ * the System class for quick handling common operations
+ with files and directories
+</description>
+ <license>PHP License</license>
+ <maintainers>
+ <maintainer>
+ <user>ssb</user>
+ <role>lead</role>
+ <name>Stig Sæther Bakken</name>
+ <email>stig@php.net</email>
+ </maintainer>
+ <maintainer>
+ <user>cox</user>
+ <role>lead</role>
+ <name>Tomas V.V.Cox</name>
+ <email>cox@idecnet.com</email>
+ </maintainer>
+ <maintainer>
+ <user>mj</user>
+ <role>developer</role>
+ <name>Martin Jansen</name>
+ <email>mj@php.net</email>
+ </maintainer>
+ <maintainer>
+ <user>pajoye</user>
+ <role>developer</role>
+ <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.4a1</version>
+ <date>2003-11-17</date>
+ <state>alpha</state>
+ <notes>
+PEAR Installer:
+
+* Bug #171 --alldeps with a rel="eq" should install the required version, if possible
+* Bug #249 installing from an url doesnt work
+* Bug #248 --force command does not work as expected
+* Bug #293 [Patch] PEAR_Error not calling static method callbacks for error-handler
+* Bug #324 pear -G gives Fatal Error (PHP-GTK not installed, but error is at engine level)
+
+ </notes>
+ <provides type="class" name="OS_Guess" />
+ <provides type="class" name="System" />
+ <provides type="function" name="md5_file" />
+ <filelist>
+ <file role="data" name="package.dtd"/>
+ <file role="data" name="template.spec"/>
+ <file role="php" name="PEAR.php"/>
+ <file role="php" name="System.php"/>
+ <dir name="PEAR">
+ <file role="php" name="Autoloader.php"/>
+ <file role="php" name="Command.php"/>
+ <dir name="Command">
+ <file role="php" name="Auth.php"/>
+ <file role="php" name="Build.php"/>
+ <file role="php" name="Common.php"/>
+ <file role="php" name="Config.php"/>
+ <file role="php" name="Install-using-downloader.php" install-as="PEAR/Command/Install.php"/>
+ <file role="php" name="Package.php"/>
+ <file role="php" name="Registry.php"/>
+ <file role="php" name="Remote.php"/>
+ <file role="php" name="Mirror.php"/>
+ </dir>
+ <file role="php" name="Common.php"/>
+ <file role="php" name="Config.php"/>
+ <file role="php" name="Dependency.php"/>
+ <dir name="Frontend">
+ <file role="php" name="CLI.php"/>
+ </dir>
+ <file role="php" name="Builder.php"/>
+ <file role="php" name="Installer-minus-download.php" install-as="PEAR/Installer.php" />
+ <file role="php" name="Downloader.php"/>
+ <file role="php" name="Packager.php"/>
+ <file role="php" name="Registry.php"/>
+ <file role="php" name="Remote.php"/>
+ </dir>
+ <dir name="OS">
+ <file role="php" name="Guess.php"/>
+ </dir>
+ <dir name="scripts" baseinstalldir="/">
+ <file role="script" install-as="pear" name="pear.sh">
+ <replace from="@php_bin@" to="php_bin" type="pear-config"/>
+ <replace from="@php_dir@" to="php_dir" type="pear-config"/>
+ <replace from="@pear_version@" to="version" type="package-info"/>
+ <replace from="@include_path@" to="php_dir" type="pear-config"/>
+ </file>
+ <file role="script" platform="windows" install-as="pear.bat" name="pear.bat">
+ <replace from="@bin_dir@" to="bin_dir" type="pear-config"/>
+ <replace from="@php_bin@" to="php_bin" type="pear-config"/>
+ <replace from="@include_path@" to="php_dir" type="pear-config"/>
+ </file>
+ <file role="php" install-as="pearcmd.php" name="pearcmd.php">
+ <replace from="@php_bin@" to="php_bin" type="pear-config"/>
+ <replace from="@php_dir@" to="php_dir" type="pear-config"/>
+ <replace from="@pear_version@" to="version" type="package-info"/>
+ <replace from="@include_path@" to="php_dir" type="pear-config"/>
+ </file>
+ </dir>
+ </filelist>
+ <deps>
+ <dep type="php" rel="ge" version="4.1"/>
+ <dep type="pkg" rel="ge" version="1.1">Archive_Tar</dep>
+ <dep type="pkg" rel="ge" version="1.0">Console_Getopt</dep>
+ <dep type="pkg" rel="ge" version="1.0.4">XML_RPC</dep>
+ <dep type="ext" rel="has" optional="yes">xmlrpc</dep>
+ <dep type="ext" rel="has">xml</dep>
+ </deps>
+ </release>
+</package>